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