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