1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck.taskdefs;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.List;
24 import java.util.logging.Level;
25 import java.util.logging.Logger;
26 import org.apache.tools.ant.BuildException;
27 import org.apache.tools.ant.Task;
28 import org.apache.tools.ant.types.EnumeratedAttribute;
29 import org.apache.tools.ant.types.Reference;
30 import org.apache.tools.ant.types.Resource;
31 import org.apache.tools.ant.types.ResourceCollection;
32 import org.apache.tools.ant.types.resources.FileProvider;
33 import org.apache.tools.ant.types.resources.Resources;
34 import org.owasp.dependencycheck.Engine;
35 import org.owasp.dependencycheck.data.nvdcve.CveDB;
36 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
37 import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
38 import org.owasp.dependencycheck.dependency.Dependency;
39 import org.owasp.dependencycheck.dependency.Identifier;
40 import org.owasp.dependencycheck.dependency.Vulnerability;
41 import org.owasp.dependencycheck.reporting.ReportGenerator;
42 import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
43 import org.owasp.dependencycheck.utils.LogUtils;
44 import org.owasp.dependencycheck.utils.Settings;
45
46
47
48
49
50
51 public class DependencyCheckTask extends Task {
52
53
54
55
56 private static final String PROPERTIES_FILE = "task.properties";
57
58
59
60 private static final String LOG_PROPERTIES_FILE = "log.properties";
61
62
63
64 private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern();
65
66
67
68
69 public DependencyCheckTask() {
70 super();
71 }
72
73
74
75
76
77 private Resources path = null;
78
79
80
81 private Reference refid = null;
82
83
84
85
86
87
88
89 public void add(ResourceCollection rc) {
90 if (isReference()) {
91 throw new BuildException("Nested elements are not allowed when using the refid attribute.");
92 }
93 getPath().add(rc);
94 }
95
96
97
98
99
100
101
102 private synchronized Resources getPath() {
103 if (path == null) {
104 path = new Resources(getProject());
105 path.setCache(true);
106 }
107 return path;
108 }
109
110
111
112
113
114
115 public boolean isReference() {
116 return refid != null;
117 }
118
119
120
121
122
123
124 public void setRefid(Reference r) {
125 if (path != null) {
126 throw new BuildException("Nested elements are not allowed when using the refid attribute.");
127 }
128 refid = r;
129 }
130
131
132
133
134
135
136 private void dealWithReferences() throws BuildException {
137 if (isReference()) {
138 final Object o = refid.getReferencedObject(getProject());
139 if (!(o instanceof ResourceCollection)) {
140 throw new BuildException("refid '" + refid.getRefId()
141 + "' does not refer to a resource collection.");
142 }
143 getPath().add((ResourceCollection) o);
144 }
145 }
146
147
148
149
150 private String applicationName = "Dependency-Check";
151
152
153
154
155
156
157 public String getApplicationName() {
158 return applicationName;
159 }
160
161
162
163
164
165
166 public void setApplicationName(String applicationName) {
167 this.applicationName = applicationName;
168 }
169
170
171
172 private String dataDirectory = null;
173
174
175
176
177
178
179 public String getDataDirectory() {
180 return dataDirectory;
181 }
182
183
184
185
186
187
188 public void setDataDirectory(String dataDirectory) {
189 this.dataDirectory = dataDirectory;
190 }
191
192
193
194 private String reportOutputDirectory = ".";
195
196
197
198
199
200
201 public String getReportOutputDirectory() {
202 return reportOutputDirectory;
203 }
204
205
206
207
208
209
210 public void setReportOutputDirectory(String reportOutputDirectory) {
211 this.reportOutputDirectory = reportOutputDirectory;
212 }
213
214
215
216
217
218 private float failBuildOnCVSS = 11;
219
220
221
222
223
224
225 public float getFailBuildOnCVSS() {
226 return failBuildOnCVSS;
227 }
228
229
230
231
232
233
234 public void setFailBuildOnCVSS(float failBuildOnCVSS) {
235 this.failBuildOnCVSS = failBuildOnCVSS;
236 }
237
238
239
240
241 private boolean autoUpdate = true;
242
243
244
245
246
247
248 public boolean isAutoUpdate() {
249 return autoUpdate;
250 }
251
252
253
254
255
256
257 public void setAutoUpdate(boolean autoUpdate) {
258 this.autoUpdate = autoUpdate;
259 }
260
261
262
263
264 private String reportFormat = "HTML";
265
266
267
268
269
270
271 public String getReportFormat() {
272 return reportFormat;
273 }
274
275
276
277
278
279
280 public void setReportFormat(ReportFormats reportFormat) {
281 this.reportFormat = reportFormat.getValue();
282 }
283
284
285
286 private String proxyUrl;
287
288
289
290
291
292
293 public String getProxyUrl() {
294 return proxyUrl;
295 }
296
297
298
299
300
301
302 public void setProxyUrl(String proxyUrl) {
303 this.proxyUrl = proxyUrl;
304 }
305
306
307
308 private String proxyPort;
309
310
311
312
313
314
315 public String getProxyPort() {
316 return proxyPort;
317 }
318
319
320
321
322
323
324 public void setProxyPort(String proxyPort) {
325 this.proxyPort = proxyPort;
326 }
327
328
329
330 private String proxyUsername;
331
332
333
334
335
336
337 public String getProxyUsername() {
338 return proxyUsername;
339 }
340
341
342
343
344
345
346 public void setProxyUsername(String proxyUsername) {
347 this.proxyUsername = proxyUsername;
348 }
349
350
351
352 private String proxyPassword;
353
354
355
356
357
358
359 public String getProxyPassword() {
360 return proxyPassword;
361 }
362
363
364
365
366
367
368 public void setProxyPassword(String proxyPassword) {
369 this.proxyPassword = proxyPassword;
370 }
371
372
373
374 private String connectionTimeout;
375
376
377
378
379
380
381 public String getConnectionTimeout() {
382 return connectionTimeout;
383 }
384
385
386
387
388
389
390 public void setConnectionTimeout(String connectionTimeout) {
391 this.connectionTimeout = connectionTimeout;
392 }
393
394
395
396 private String logFile = null;
397
398
399
400
401
402
403 public String getLogFile() {
404 return logFile;
405 }
406
407
408
409
410
411
412 public void setLogFile(String logFile) {
413 this.logFile = logFile;
414 }
415
416
417
418 private String suppressionFile;
419
420
421
422
423
424
425 public String getSuppressionFile() {
426 return suppressionFile;
427 }
428
429
430
431
432
433
434 public void setSuppressionFile(String suppressionFile) {
435 this.suppressionFile = suppressionFile;
436 }
437
438
439
440 private boolean showSummary = true;
441
442
443
444
445
446
447 public boolean isShowSummary() {
448 return showSummary;
449 }
450
451
452
453
454
455
456 public void setShowSummary(boolean showSummary) {
457 this.showSummary = showSummary;
458 }
459
460
461
462
463
464
465 public void setJarAnalyzerEnabled(boolean jarAnalyzerEnabled) {
466 this.jarAnalyzerEnabled = jarAnalyzerEnabled;
467 }
468
469
470
471 private boolean archiveAnalyzerEnabled = true;
472
473
474
475
476
477
478 public boolean isArchiveAnalyzerEnabled() {
479 return archiveAnalyzerEnabled;
480 }
481
482
483
484 private boolean assemblyAnalyzerEnabled = true;
485
486
487
488
489
490
491 public void setArchiveAnalyzerEnabled(boolean archiveAnalyzerEnabled) {
492 this.archiveAnalyzerEnabled = archiveAnalyzerEnabled;
493 }
494
495
496
497
498
499
500 public boolean isAssemblyAnalyzerEnabled() {
501 return assemblyAnalyzerEnabled;
502 }
503
504
505
506
507
508
509 public void setAssemblyAnalyzerEnabled(boolean assemblyAnalyzerEnabled) {
510 this.assemblyAnalyzerEnabled = assemblyAnalyzerEnabled;
511 }
512
513
514
515 private boolean nuspecAnalyzerEnabled = true;
516
517
518
519
520
521
522 public boolean isNuspecAnalyzerEnabled() {
523 return nuspecAnalyzerEnabled;
524 }
525
526
527
528
529
530
531 public void setNuspecAnalyzerEnabled(boolean nuspecAnalyzerEnabled) {
532 this.nuspecAnalyzerEnabled = nuspecAnalyzerEnabled;
533 }
534
535
536
537
538 private boolean nexusAnalyzerEnabled = true;
539
540
541
542
543
544
545 public boolean isNexusAnalyzerEnabled() {
546 return nexusAnalyzerEnabled;
547 }
548
549
550
551
552
553
554 public void setNexusAnalyzerEnabled(boolean nexusAnalyzerEnabled) {
555 this.nexusAnalyzerEnabled = nexusAnalyzerEnabled;
556 }
557
558
559
560
561 private String nexusUrl;
562
563
564
565
566
567
568 public String getNexusUrl() {
569 return nexusUrl;
570 }
571
572
573
574
575
576
577 public void setNexusUrl(String nexusUrl) {
578 this.nexusUrl = nexusUrl;
579 }
580
581
582
583 private boolean nexusUsesProxy = true;
584
585
586
587
588
589
590 public boolean isNexusUsesProxy() {
591 return nexusUsesProxy;
592 }
593
594
595
596
597
598
599 public void setNexusUsesProxy(boolean nexusUsesProxy) {
600 this.nexusUsesProxy = nexusUsesProxy;
601 }
602
603
604
605
606 private String databaseDriverName;
607
608
609
610
611
612
613 public String getDatabaseDriverName() {
614 return databaseDriverName;
615 }
616
617
618
619
620
621
622 public void setDatabaseDriverName(String databaseDriverName) {
623 this.databaseDriverName = databaseDriverName;
624 }
625
626
627
628
629 private String databaseDriverPath;
630
631
632
633
634
635
636 public String getDatabaseDriverPath() {
637 return databaseDriverPath;
638 }
639
640
641
642
643
644
645 public void setDatabaseDriverPath(String databaseDriverPath) {
646 this.databaseDriverPath = databaseDriverPath;
647 }
648
649
650
651 private String connectionString;
652
653
654
655
656
657
658 public String getConnectionString() {
659 return connectionString;
660 }
661
662
663
664
665
666
667 public void setConnectionString(String connectionString) {
668 this.connectionString = connectionString;
669 }
670
671
672
673 private String databaseUser;
674
675
676
677
678
679
680 public String getDatabaseUser() {
681 return databaseUser;
682 }
683
684
685
686
687
688
689 public void setDatabaseUser(String databaseUser) {
690 this.databaseUser = databaseUser;
691 }
692
693
694
695
696 private String databasePassword;
697
698
699
700
701
702
703 public String getDatabasePassword() {
704 return databasePassword;
705 }
706
707
708
709
710
711
712 public void setDatabasePassword(String databasePassword) {
713 this.databasePassword = databasePassword;
714 }
715
716
717
718
719
720 private String zipExtensions;
721
722
723
724
725
726
727 public String getZipExtensions() {
728 return zipExtensions;
729 }
730
731
732
733
734
735
736 public void setZipExtensions(String zipExtensions) {
737 this.zipExtensions = zipExtensions;
738 }
739
740
741
742
743 private String cveUrl12Modified;
744
745
746
747
748
749
750 public String getCveUrl12Modified() {
751 return cveUrl12Modified;
752 }
753
754
755
756
757
758
759 public void setCveUrl12Modified(String cveUrl12Modified) {
760 this.cveUrl12Modified = cveUrl12Modified;
761 }
762
763
764
765
766 private String cveUrl20Modified;
767
768
769
770
771
772
773 public String getCveUrl20Modified() {
774 return cveUrl20Modified;
775 }
776
777
778
779
780
781
782 public void setCveUrl20Modified(String cveUrl20Modified) {
783 this.cveUrl20Modified = cveUrl20Modified;
784 }
785
786
787
788
789 private String cveUrl12Base;
790
791
792
793
794
795
796 public String getCveUrl12Base() {
797 return cveUrl12Base;
798 }
799
800
801
802
803
804
805 public void setCveUrl12Base(String cveUrl12Base) {
806 this.cveUrl12Base = cveUrl12Base;
807 }
808
809
810
811
812 private String cveUrl20Base;
813
814
815
816
817
818
819 public String getCveUrl20Base() {
820 return cveUrl20Base;
821 }
822
823
824
825
826
827
828 public void setCveUrl20Base(String cveUrl20Base) {
829 this.cveUrl20Base = cveUrl20Base;
830 }
831
832
833
834 private String pathToMono;
835
836
837
838
839
840
841 public String getPathToMono() {
842 return pathToMono;
843 }
844
845
846
847
848
849
850 public void setPathToMono(String pathToMono) {
851 this.pathToMono = pathToMono;
852 }
853
854 @Override
855 public void execute() throws BuildException {
856 final InputStream in = DependencyCheckTask.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
857 LogUtils.prepareLogger(in, logFile);
858
859 dealWithReferences();
860 validateConfiguration();
861 populateSettings();
862
863 Engine engine = null;
864 try {
865 engine = new Engine();
866
867 for (Resource resource : path) {
868 final FileProvider provider = resource.as(FileProvider.class);
869 if (provider != null) {
870 final File file = provider.getFile();
871 if (file != null && file.exists()) {
872 engine.scan(file);
873 }
874 }
875 }
876 try {
877 engine.analyzeDependencies();
878 DatabaseProperties prop = null;
879 CveDB cve = null;
880 try {
881 cve = new CveDB();
882 cve.open();
883 prop = cve.getDatabaseProperties();
884 } catch (DatabaseException ex) {
885 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, "Unable to retrieve DB Properties", ex);
886 } finally {
887 if (cve != null) {
888 cve.close();
889 }
890 }
891 final ReportGenerator reporter = new ReportGenerator(applicationName, engine.getDependencies(), engine.getAnalyzers(), prop);
892 reporter.generateReports(reportOutputDirectory, reportFormat);
893
894 if (this.failBuildOnCVSS <= 10) {
895 checkForFailure(engine.getDependencies());
896 }
897 if (this.showSummary) {
898 showSummary(engine.getDependencies());
899 }
900 } catch (IOException ex) {
901 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE,
902 "Unable to generate dependency-check report", ex);
903 throw new BuildException("Unable to generate dependency-check report", ex);
904 } catch (Exception ex) {
905 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE,
906 "An exception occurred; unable to continue task", ex);
907 throw new BuildException("An exception occurred; unable to continue task", ex);
908 }
909 } catch (DatabaseException ex) {
910 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE,
911 "Unable to connect to the dependency-check database; analysis has stopped");
912 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, "", ex);
913 } finally {
914 if (engine != null) {
915 engine.cleanup();
916 }
917 }
918 }
919
920
921
922
923
924
925 private void validateConfiguration() throws BuildException {
926 if (path == null) {
927 throw new BuildException("No project dependencies have been defined to analyze.");
928 }
929 if (failBuildOnCVSS < 0 || failBuildOnCVSS > 11) {
930 throw new BuildException("Invalid configuration, failBuildOnCVSS must be between 0 and 11.");
931 }
932 }
933
934
935
936
937
938 private void populateSettings() {
939 InputStream taskProperties = null;
940 try {
941 taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
942 Settings.mergeProperties(taskProperties);
943 } catch (IOException ex) {
944 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.WARNING, "Unable to load the dependency-check ant task.properties file.");
945 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, null, ex);
946 } finally {
947 if (taskProperties != null) {
948 try {
949 taskProperties.close();
950 } catch (IOException ex) {
951 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINEST, null, ex);
952 }
953 }
954 }
955 if (dataDirectory != null) {
956 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
957 } else {
958 final File jarPath = new File(DependencyCheckTask.class.getProtectionDomain().getCodeSource().getLocation().getPath());
959 final File base = jarPath.getParentFile();
960 final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
961 final File dataDir = new File(base, sub);
962 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
963 }
964
965 Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
966
967 if (proxyUrl != null && !proxyUrl.isEmpty()) {
968 Settings.setString(Settings.KEYS.PROXY_URL, proxyUrl);
969 }
970 if (proxyPort != null && !proxyPort.isEmpty()) {
971 Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
972 }
973 if (proxyUsername != null && !proxyUsername.isEmpty()) {
974 Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUsername);
975 }
976 if (proxyPassword != null && !proxyPassword.isEmpty()) {
977 Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPassword);
978 }
979 if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
980 Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
981 }
982 if (suppressionFile != null && !suppressionFile.isEmpty()) {
983 Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
984 }
985
986
987
988 Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled);
989
990 Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled);
991
992 Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled);
993 if (nexusUrl != null && !nexusUrl.isEmpty()) {
994 Settings.setString(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
995 }
996 Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_PROXY, nexusUsesProxy);
997
998 Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled);
999 if (zipExtensions != null && !zipExtensions.isEmpty()) {
1000 Settings.setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions);
1001 }
1002
1003 Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled);
1004 if (pathToMono != null && !pathToMono.isEmpty()) {
1005 Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
1006 }
1007
1008 if (databaseDriverName != null && !databaseDriverName.isEmpty()) {
1009 Settings.setString(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName);
1010 }
1011 if (databaseDriverPath != null && !databaseDriverPath.isEmpty()) {
1012 Settings.setString(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath);
1013 }
1014 if (connectionString != null && !connectionString.isEmpty()) {
1015 Settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString);
1016 }
1017 if (databaseUser != null && !databaseUser.isEmpty()) {
1018 Settings.setString(Settings.KEYS.DB_USER, databaseUser);
1019 }
1020 if (databasePassword != null && !databasePassword.isEmpty()) {
1021 Settings.setString(Settings.KEYS.DB_PASSWORD, databasePassword);
1022 }
1023 if (cveUrl12Modified != null && !cveUrl12Modified.isEmpty()) {
1024 Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified);
1025 }
1026 if (cveUrl20Modified != null && !cveUrl20Modified.isEmpty()) {
1027 Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified);
1028 }
1029 if (cveUrl12Base != null && !cveUrl12Base.isEmpty()) {
1030 Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base);
1031 }
1032 if (cveUrl20Base != null && !cveUrl20Base.isEmpty()) {
1033 Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base);
1034 }
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044 private void checkForFailure(List<Dependency> dependencies) throws BuildException {
1045 final StringBuilder ids = new StringBuilder();
1046 for (Dependency d : dependencies) {
1047 for (Vulnerability v : d.getVulnerabilities()) {
1048 if (v.getCvssScore() >= failBuildOnCVSS) {
1049 if (ids.length() == 0) {
1050 ids.append(v.getName());
1051 } else {
1052 ids.append(", ").append(v.getName());
1053 }
1054 }
1055 }
1056 }
1057 if (ids.length() > 0) {
1058 final String msg = String.format("%n%nDependency-Check Failure:%n"
1059 + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
1060 + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
1061 throw new BuildException(msg);
1062 }
1063 }
1064
1065
1066
1067
1068
1069
1070 private void showSummary(List<Dependency> dependencies) {
1071 final StringBuilder summary = new StringBuilder();
1072 for (Dependency d : dependencies) {
1073 boolean firstEntry = true;
1074 final StringBuilder ids = new StringBuilder();
1075 for (Vulnerability v : d.getVulnerabilities()) {
1076 if (firstEntry) {
1077 firstEntry = false;
1078 } else {
1079 ids.append(", ");
1080 }
1081 ids.append(v.getName());
1082 }
1083 if (ids.length() > 0) {
1084 summary.append(d.getFileName()).append(" (");
1085 firstEntry = true;
1086 for (Identifier id : d.getIdentifiers()) {
1087 if (firstEntry) {
1088 firstEntry = false;
1089 } else {
1090 summary.append(", ");
1091 }
1092 summary.append(id.getValue());
1093 }
1094 summary.append(") : ").append(ids).append(NEW_LINE);
1095 }
1096 }
1097 if (summary.length() > 0) {
1098 final String msg = String.format("%n%n"
1099 + "One or more dependencies were identified with known vulnerabilities:%n%n%s"
1100 + "%n%nSee the dependency-check report for more details.%n%n", summary.toString());
1101 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.WARNING, msg);
1102 }
1103 }
1104
1105
1106
1107
1108 public static class ReportFormats extends EnumeratedAttribute {
1109
1110
1111
1112
1113
1114
1115 @Override
1116 public String[] getValues() {
1117 int i = 0;
1118 final Format[] formats = Format.values();
1119 final String[] values = new String[formats.length];
1120 for (Format format : formats) {
1121 values[i++] = format.name();
1122 }
1123 return values;
1124 }
1125 }
1126
1127
1128
1129
1130 private boolean jarAnalyzerEnabled = true;
1131
1132
1133
1134
1135
1136
1137 public boolean isJarAnalyzerEnabled() {
1138 return jarAnalyzerEnabled;
1139 }
1140 }