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.util.List;
23 import org.apache.tools.ant.BuildException;
24 import org.apache.tools.ant.Project;
25 import org.apache.tools.ant.types.EnumeratedAttribute;
26 import org.apache.tools.ant.types.Reference;
27 import org.apache.tools.ant.types.Resource;
28 import org.apache.tools.ant.types.ResourceCollection;
29 import org.apache.tools.ant.types.resources.FileProvider;
30 import org.apache.tools.ant.types.resources.Resources;
31 import org.owasp.dependencycheck.Engine;
32 import org.owasp.dependencycheck.data.nvdcve.CveDB;
33 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
34 import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
35 import org.owasp.dependencycheck.dependency.Dependency;
36 import org.owasp.dependencycheck.dependency.Identifier;
37 import org.owasp.dependencycheck.dependency.Vulnerability;
38 import org.owasp.dependencycheck.reporting.ReportGenerator;
39 import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
40 import org.owasp.dependencycheck.utils.Settings;
41 import org.slf4j.impl.StaticLoggerBinder;
42
43
44
45
46
47
48 public class Check extends Update {
49
50
51
52
53 private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern();
54
55
56
57
58 public Check() {
59 super();
60
61
62 StaticLoggerBinder.getSingleton().setTask(this);
63 }
64
65
66
67
68
69 private Resources path = null;
70
71
72
73 private Reference refid = null;
74
75
76
77
78
79
80
81 public void add(ResourceCollection rc) {
82 if (isReference()) {
83 throw new BuildException("Nested elements are not allowed when using the refid attribute.");
84 }
85 getPath().add(rc);
86 }
87
88
89
90
91
92
93
94 private synchronized Resources getPath() {
95 if (path == null) {
96 path = new Resources(getProject());
97 path.setCache(true);
98 }
99 return path;
100 }
101
102
103
104
105
106
107 public boolean isReference() {
108 return refid != null;
109 }
110
111
112
113
114
115
116
117 public void setRefid(Reference r) {
118 if (path != null) {
119 throw new BuildException("Nested elements are not allowed when using the refid attribute.");
120 }
121 refid = r;
122 }
123
124
125
126
127
128
129
130 private void dealWithReferences() throws BuildException {
131 if (isReference()) {
132 final Object o = refid.getReferencedObject(getProject());
133 if (!(o instanceof ResourceCollection)) {
134 throw new BuildException("refid '" + refid.getRefId()
135 + "' does not refer to a resource collection.");
136 }
137 getPath().add((ResourceCollection) o);
138 }
139 }
140
141
142
143
144
145
146 @Deprecated
147 private String applicationName = null;
148
149
150
151
152
153
154
155
156 @Deprecated
157 public String getApplicationName() {
158 return applicationName;
159 }
160
161
162
163
164
165
166
167 @Deprecated
168 public void setApplicationName(String applicationName) {
169 this.applicationName = applicationName;
170 }
171
172
173
174 private String projectName = "dependency-check";
175
176
177
178
179
180
181 public String getProjectName() {
182 if (applicationName != null) {
183 log("Configuration 'applicationName' has been deprecated, please use 'projectName' instead", Project.MSG_WARN);
184 if ("dependency-check".equals(projectName)) {
185 projectName = applicationName;
186 }
187 }
188 return projectName;
189 }
190
191
192
193
194
195
196 public void setProjectName(String projectName) {
197 this.projectName = projectName;
198 }
199
200
201
202
203
204 private String reportOutputDirectory = ".";
205
206
207
208
209
210
211 public String getReportOutputDirectory() {
212 return reportOutputDirectory;
213 }
214
215
216
217
218
219
220 public void setReportOutputDirectory(String reportOutputDirectory) {
221 this.reportOutputDirectory = reportOutputDirectory;
222 }
223
224
225
226
227
228
229
230 private float failBuildOnCVSS = 11;
231
232
233
234
235
236
237 public float getFailBuildOnCVSS() {
238 return failBuildOnCVSS;
239 }
240
241
242
243
244
245
246 public void setFailBuildOnCVSS(float failBuildOnCVSS) {
247 this.failBuildOnCVSS = failBuildOnCVSS;
248 }
249
250
251
252
253 private Boolean autoUpdate;
254
255
256
257
258
259
260 public Boolean isAutoUpdate() {
261 return autoUpdate;
262 }
263
264
265
266
267
268
269 public void setAutoUpdate(Boolean autoUpdate) {
270 this.autoUpdate = autoUpdate;
271 }
272
273
274
275
276
277 @Deprecated
278 private boolean updateOnly = false;
279
280
281
282
283
284
285
286 @Deprecated
287 public boolean isUpdateOnly() {
288 return updateOnly;
289 }
290
291
292
293
294
295
296
297 @Deprecated
298 public void setUpdateOnly(boolean updateOnly) {
299 this.updateOnly = updateOnly;
300 }
301
302
303
304
305
306 private String reportFormat = "HTML";
307
308
309
310
311
312
313 public String getReportFormat() {
314 return reportFormat;
315 }
316
317
318
319
320
321
322 public void setReportFormat(ReportFormats reportFormat) {
323 this.reportFormat = reportFormat.getValue();
324 }
325
326
327
328 private String suppressionFile;
329
330
331
332
333
334
335 public String getSuppressionFile() {
336 return suppressionFile;
337 }
338
339
340
341
342
343
344 public void setSuppressionFile(String suppressionFile) {
345 this.suppressionFile = suppressionFile;
346 }
347
348
349
350 private boolean showSummary = true;
351
352
353
354
355
356
357 public boolean isShowSummary() {
358 return showSummary;
359 }
360
361
362
363
364
365
366 public void setShowSummary(boolean showSummary) {
367 this.showSummary = showSummary;
368 }
369
370
371
372
373 private Boolean enableExperimental;
374
375
376
377
378
379
380 public Boolean isEnableExperimental() {
381 return enableExperimental;
382 }
383
384
385
386
387
388
389 public void setEnableExperimental(Boolean enableExperimental) {
390 this.enableExperimental = enableExperimental;
391 }
392
393
394
395
396 private Boolean jarAnalyzerEnabled;
397
398
399
400
401
402
403 public Boolean isJarAnalyzerEnabled() {
404 return jarAnalyzerEnabled;
405 }
406
407
408
409
410
411
412 public void setJarAnalyzerEnabled(Boolean jarAnalyzerEnabled) {
413 this.jarAnalyzerEnabled = jarAnalyzerEnabled;
414 }
415
416
417
418 private Boolean archiveAnalyzerEnabled;
419
420
421
422
423
424
425 public Boolean isArchiveAnalyzerEnabled() {
426 return archiveAnalyzerEnabled;
427 }
428
429
430
431 private Boolean assemblyAnalyzerEnabled;
432
433
434
435
436
437
438 public void setArchiveAnalyzerEnabled(Boolean archiveAnalyzerEnabled) {
439 this.archiveAnalyzerEnabled = archiveAnalyzerEnabled;
440 }
441
442
443
444
445
446
447 public Boolean isAssemblyAnalyzerEnabled() {
448 return assemblyAnalyzerEnabled;
449 }
450
451
452
453
454
455
456 public void setAssemblyAnalyzerEnabled(Boolean assemblyAnalyzerEnabled) {
457 this.assemblyAnalyzerEnabled = assemblyAnalyzerEnabled;
458 }
459
460
461
462 private Boolean nuspecAnalyzerEnabled;
463
464
465
466
467
468
469 public Boolean isNuspecAnalyzerEnabled() {
470 return nuspecAnalyzerEnabled;
471 }
472
473
474
475
476
477
478 public void setNuspecAnalyzerEnabled(Boolean nuspecAnalyzerEnabled) {
479 this.nuspecAnalyzerEnabled = nuspecAnalyzerEnabled;
480 }
481
482
483
484 private Boolean composerAnalyzerEnabled;
485
486
487
488
489
490
491 public Boolean isComposerAnalyzerEnabled() {
492 return composerAnalyzerEnabled;
493 }
494
495
496
497
498
499
500 public void setComposerAnalyzerEnabled(Boolean composerAnalyzerEnabled) {
501 this.composerAnalyzerEnabled = composerAnalyzerEnabled;
502 }
503
504
505
506 private Boolean autoconfAnalyzerEnabled;
507
508
509
510
511
512
513 public Boolean isAutoconfAnalyzerEnabled() {
514 return autoconfAnalyzerEnabled;
515 }
516
517
518
519
520
521
522 public void setAutoconfAnalyzerEnabled(Boolean autoconfAnalyzerEnabled) {
523 this.autoconfAnalyzerEnabled = autoconfAnalyzerEnabled;
524 }
525
526
527
528 private Boolean cmakeAnalyzerEnabled;
529
530
531
532
533
534
535 public Boolean isCMakeAnalyzerEnabled() {
536 return cmakeAnalyzerEnabled;
537 }
538
539
540
541
542
543
544 public void setCMakeAnalyzerEnabled(Boolean cmakeAnalyzerEnabled) {
545 this.cmakeAnalyzerEnabled = cmakeAnalyzerEnabled;
546 }
547
548
549
550 private Boolean opensslAnalyzerEnabled;
551
552
553
554
555
556
557 public Boolean isOpensslAnalyzerEnabled() {
558 return opensslAnalyzerEnabled;
559 }
560
561
562
563
564
565
566 public void setOpensslAnalyzerEnabled(Boolean opensslAnalyzerEnabled) {
567 this.opensslAnalyzerEnabled = opensslAnalyzerEnabled;
568 }
569
570
571
572 private Boolean nodeAnalyzerEnabled;
573
574
575
576
577
578
579 public Boolean isNodeAnalyzerEnabled() {
580 return nodeAnalyzerEnabled;
581 }
582
583
584
585
586
587
588 public void setNodeAnalyzerEnabled(Boolean nodeAnalyzerEnabled) {
589 this.nodeAnalyzerEnabled = nodeAnalyzerEnabled;
590 }
591
592
593
594 private Boolean rubygemsAnalyzerEnabled;
595
596
597
598
599
600
601 public Boolean isRubygemsAnalyzerEnabled() {
602 return rubygemsAnalyzerEnabled;
603 }
604
605
606
607
608
609
610 public void setRubygemsAnalyzerEnabled(Boolean rubygemsAnalyzerEnabled) {
611 this.rubygemsAnalyzerEnabled = rubygemsAnalyzerEnabled;
612 }
613
614
615
616 private Boolean pyPackageAnalyzerEnabled;
617
618
619
620
621
622
623 public Boolean isPyPackageAnalyzerEnabled() {
624 return pyPackageAnalyzerEnabled;
625 }
626
627
628
629
630
631
632 public void setPyPackageAnalyzerEnabled(Boolean pyPackageAnalyzerEnabled) {
633 this.pyPackageAnalyzerEnabled = pyPackageAnalyzerEnabled;
634 }
635
636
637
638
639 private Boolean pyDistributionAnalyzerEnabled;
640
641
642
643
644
645
646 public Boolean isPyDistributionAnalyzerEnabled() {
647 return pyDistributionAnalyzerEnabled;
648 }
649
650
651
652
653
654
655
656 public void setPyDistributionAnalyzerEnabled(Boolean pyDistributionAnalyzerEnabled) {
657 this.pyDistributionAnalyzerEnabled = pyDistributionAnalyzerEnabled;
658 }
659
660
661
662
663 private Boolean centralAnalyzerEnabled;
664
665
666
667
668
669
670 public Boolean isCentralAnalyzerEnabled() {
671 return centralAnalyzerEnabled;
672 }
673
674
675
676
677
678
679 public void setCentralAnalyzerEnabled(Boolean centralAnalyzerEnabled) {
680 this.centralAnalyzerEnabled = centralAnalyzerEnabled;
681 }
682
683
684
685
686 private Boolean nexusAnalyzerEnabled;
687
688
689
690
691
692
693 public Boolean isNexusAnalyzerEnabled() {
694 return nexusAnalyzerEnabled;
695 }
696
697
698
699
700
701
702 public void setNexusAnalyzerEnabled(Boolean nexusAnalyzerEnabled) {
703 this.nexusAnalyzerEnabled = nexusAnalyzerEnabled;
704 }
705
706
707
708
709
710 private String nexusUrl;
711
712
713
714
715
716
717 public String getNexusUrl() {
718 return nexusUrl;
719 }
720
721
722
723
724
725
726 public void setNexusUrl(String nexusUrl) {
727 this.nexusUrl = nexusUrl;
728 }
729
730
731
732 private Boolean nexusUsesProxy;
733
734
735
736
737
738
739 public Boolean isNexusUsesProxy() {
740 return nexusUsesProxy;
741 }
742
743
744
745
746
747
748 public void setNexusUsesProxy(Boolean nexusUsesProxy) {
749 this.nexusUsesProxy = nexusUsesProxy;
750 }
751
752
753
754
755
756 private String zipExtensions;
757
758
759
760
761
762
763 public String getZipExtensions() {
764 return zipExtensions;
765 }
766
767
768
769
770
771
772 public void setZipExtensions(String zipExtensions) {
773 this.zipExtensions = zipExtensions;
774 }
775
776
777
778
779 private String pathToMono;
780
781
782
783
784
785
786 public String getPathToMono() {
787 return pathToMono;
788 }
789
790
791
792
793
794
795 public void setPathToMono(String pathToMono) {
796 this.pathToMono = pathToMono;
797 }
798
799 @Override
800 public void execute() throws BuildException {
801 dealWithReferences();
802 validateConfiguration();
803 populateSettings();
804 Engine engine = null;
805 try {
806 engine = new Engine(Check.class.getClassLoader());
807 if (isUpdateOnly()) {
808 log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN);
809 engine.doUpdates();
810 } else {
811 try {
812 for (Resource resource : path) {
813 final FileProvider provider = resource.as(FileProvider.class);
814 if (provider != null) {
815 final File file = provider.getFile();
816 if (file != null && file.exists()) {
817 engine.scan(file);
818 }
819 }
820 }
821
822 engine.analyzeDependencies();
823 DatabaseProperties prop = null;
824 CveDB cve = null;
825 try {
826 cve = new CveDB();
827 cve.open();
828 prop = cve.getDatabaseProperties();
829 } catch (DatabaseException ex) {
830 log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
831 } finally {
832 if (cve != null) {
833 cve.close();
834 }
835 }
836 final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop);
837 reporter.generateReports(reportOutputDirectory, reportFormat);
838
839 if (this.failBuildOnCVSS <= 10) {
840 checkForFailure(engine.getDependencies());
841 }
842 if (this.showSummary) {
843 showSummary(engine.getDependencies());
844 }
845 } catch (IOException ex) {
846 log("Unable to generate dependency-check report", ex, Project.MSG_DEBUG);
847 throw new BuildException("Unable to generate dependency-check report", ex);
848 } catch (Exception ex) {
849 log("An exception occurred; unable to continue task", ex, Project.MSG_DEBUG);
850 throw new BuildException("An exception occurred; unable to continue task", ex);
851 }
852 }
853 } catch (DatabaseException ex) {
854 log("Unable to connect to the dependency-check database; analysis has stopped", ex, Project.MSG_ERR);
855 } finally {
856 Settings.cleanup(true);
857 if (engine != null) {
858 engine.cleanup();
859 }
860 }
861 }
862
863
864
865
866
867
868
869 private void validateConfiguration() throws BuildException {
870 if (path == null) {
871 throw new BuildException("No project dependencies have been defined to analyze.");
872 }
873 if (failBuildOnCVSS < 0 || failBuildOnCVSS > 11) {
874 throw new BuildException("Invalid configuration, failBuildOnCVSS must be between 0 and 11.");
875 }
876 }
877
878
879
880
881
882
883
884
885 @Override
886 protected void populateSettings() throws BuildException {
887 super.populateSettings();
888 Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
889 Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
890 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental);
891 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled);
892 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled);
893 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled);
894 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, rubygemsAnalyzerEnabled);
895 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, opensslAnalyzerEnabled);
896 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CMAKE_ENABLED, cmakeAnalyzerEnabled);
897 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, autoconfAnalyzerEnabled);
898 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled);
899 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled);
900 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled);
901 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled);
902 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled);
903 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled);
904 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled);
905 Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
906 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy);
907 Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions);
908 Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
909 }
910
911
912
913
914
915
916
917
918
919 private void checkForFailure(List<Dependency> dependencies) throws BuildException {
920 final StringBuilder ids = new StringBuilder();
921 for (Dependency d : dependencies) {
922 for (Vulnerability v : d.getVulnerabilities()) {
923 if (v.getCvssScore() >= failBuildOnCVSS) {
924 if (ids.length() == 0) {
925 ids.append(v.getName());
926 } else {
927 ids.append(", ").append(v.getName());
928 }
929 }
930 }
931 }
932 if (ids.length() > 0) {
933 final String msg = String.format("%n%nDependency-Check Failure:%n"
934 + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
935 + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
936 throw new BuildException(msg);
937 }
938 }
939
940
941
942
943
944
945
946 private void showSummary(List<Dependency> dependencies) {
947 final StringBuilder summary = new StringBuilder();
948 for (Dependency d : dependencies) {
949 boolean firstEntry = true;
950 final StringBuilder ids = new StringBuilder();
951 for (Vulnerability v : d.getVulnerabilities()) {
952 if (firstEntry) {
953 firstEntry = false;
954 } else {
955 ids.append(", ");
956 }
957 ids.append(v.getName());
958 }
959 if (ids.length() > 0) {
960 summary.append(d.getFileName()).append(" (");
961 firstEntry = true;
962 for (Identifier id : d.getIdentifiers()) {
963 if (firstEntry) {
964 firstEntry = false;
965 } else {
966 summary.append(", ");
967 }
968 summary.append(id.getValue());
969 }
970 summary.append(") : ").append(ids).append(NEW_LINE);
971 }
972 }
973 if (summary.length() > 0) {
974 final String msg = String.format("%n%n"
975 + "One or more dependencies were identified with known vulnerabilities:%n%n%s"
976 + "%n%nSee the dependency-check report for more details.%n%n", summary.toString());
977 log(msg, Project.MSG_WARN);
978 }
979 }
980
981
982
983
984
985 public static class ReportFormats extends EnumeratedAttribute {
986
987
988
989
990
991
992 @Override
993 public String[] getValues() {
994 int i = 0;
995 final Format[] formats = Format.values();
996 final String[] values = new String[formats.length];
997 for (Format format : formats) {
998 values[i++] = format.name();
999 }
1000 return values;
1001 }
1002 }
1003 }