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 String hintsFile;
353
354
355
356
357
358
359 public String getHintsFile() {
360 return hintsFile;
361 }
362
363
364
365
366
367
368 public void setHintsFile(String hintsFile) {
369 this.hintsFile = hintsFile;
370 }
371
372
373
374 private boolean showSummary = true;
375
376
377
378
379
380
381 public boolean isShowSummary() {
382 return showSummary;
383 }
384
385
386
387
388
389
390 public void setShowSummary(boolean showSummary) {
391 this.showSummary = showSummary;
392 }
393
394
395
396
397 private Boolean enableExperimental;
398
399
400
401
402
403
404 public Boolean isEnableExperimental() {
405 return enableExperimental;
406 }
407
408
409
410
411
412
413 public void setEnableExperimental(Boolean enableExperimental) {
414 this.enableExperimental = enableExperimental;
415 }
416
417
418
419
420 private Boolean jarAnalyzerEnabled;
421
422
423
424
425
426
427 public Boolean isJarAnalyzerEnabled() {
428 return jarAnalyzerEnabled;
429 }
430
431
432
433
434
435
436 public void setJarAnalyzerEnabled(Boolean jarAnalyzerEnabled) {
437 this.jarAnalyzerEnabled = jarAnalyzerEnabled;
438 }
439
440
441
442 private Boolean archiveAnalyzerEnabled;
443
444
445
446
447
448
449 public Boolean isArchiveAnalyzerEnabled() {
450 return archiveAnalyzerEnabled;
451 }
452
453
454
455 private Boolean assemblyAnalyzerEnabled;
456
457
458
459
460
461
462 public void setArchiveAnalyzerEnabled(Boolean archiveAnalyzerEnabled) {
463 this.archiveAnalyzerEnabled = archiveAnalyzerEnabled;
464 }
465
466
467
468
469
470
471 public Boolean isAssemblyAnalyzerEnabled() {
472 return assemblyAnalyzerEnabled;
473 }
474
475
476
477
478
479
480 public void setAssemblyAnalyzerEnabled(Boolean assemblyAnalyzerEnabled) {
481 this.assemblyAnalyzerEnabled = assemblyAnalyzerEnabled;
482 }
483
484
485
486 private Boolean nuspecAnalyzerEnabled;
487
488
489
490
491
492
493 public Boolean isNuspecAnalyzerEnabled() {
494 return nuspecAnalyzerEnabled;
495 }
496
497
498
499
500
501
502 public void setNuspecAnalyzerEnabled(Boolean nuspecAnalyzerEnabled) {
503 this.nuspecAnalyzerEnabled = nuspecAnalyzerEnabled;
504 }
505
506
507
508 private Boolean composerAnalyzerEnabled;
509
510
511
512
513
514
515 public Boolean isComposerAnalyzerEnabled() {
516 return composerAnalyzerEnabled;
517 }
518
519
520
521
522
523
524 public void setComposerAnalyzerEnabled(Boolean composerAnalyzerEnabled) {
525 this.composerAnalyzerEnabled = composerAnalyzerEnabled;
526 }
527
528
529
530 private Boolean autoconfAnalyzerEnabled;
531
532
533
534
535
536
537 public Boolean isAutoconfAnalyzerEnabled() {
538 return autoconfAnalyzerEnabled;
539 }
540
541
542
543
544
545
546 public void setAutoconfAnalyzerEnabled(Boolean autoconfAnalyzerEnabled) {
547 this.autoconfAnalyzerEnabled = autoconfAnalyzerEnabled;
548 }
549
550
551
552 private Boolean cmakeAnalyzerEnabled;
553
554
555
556
557
558
559 public Boolean isCMakeAnalyzerEnabled() {
560 return cmakeAnalyzerEnabled;
561 }
562
563
564
565
566
567
568 public void setCMakeAnalyzerEnabled(Boolean cmakeAnalyzerEnabled) {
569 this.cmakeAnalyzerEnabled = cmakeAnalyzerEnabled;
570 }
571
572
573
574 private Boolean opensslAnalyzerEnabled;
575
576
577
578
579
580
581 public Boolean isOpensslAnalyzerEnabled() {
582 return opensslAnalyzerEnabled;
583 }
584
585
586
587
588
589
590 public void setOpensslAnalyzerEnabled(Boolean opensslAnalyzerEnabled) {
591 this.opensslAnalyzerEnabled = opensslAnalyzerEnabled;
592 }
593
594
595
596 private Boolean nodeAnalyzerEnabled;
597
598
599
600
601
602
603 public Boolean isNodeAnalyzerEnabled() {
604 return nodeAnalyzerEnabled;
605 }
606
607
608
609
610
611
612 public void setNodeAnalyzerEnabled(Boolean nodeAnalyzerEnabled) {
613 this.nodeAnalyzerEnabled = nodeAnalyzerEnabled;
614 }
615
616
617
618 private Boolean rubygemsAnalyzerEnabled;
619
620
621
622
623
624
625 public Boolean isRubygemsAnalyzerEnabled() {
626 return rubygemsAnalyzerEnabled;
627 }
628
629
630
631
632
633
634 public void setRubygemsAnalyzerEnabled(Boolean rubygemsAnalyzerEnabled) {
635 this.rubygemsAnalyzerEnabled = rubygemsAnalyzerEnabled;
636 }
637
638
639
640 private Boolean pyPackageAnalyzerEnabled;
641
642
643
644
645
646
647 public Boolean isPyPackageAnalyzerEnabled() {
648 return pyPackageAnalyzerEnabled;
649 }
650
651
652
653
654
655
656 public void setPyPackageAnalyzerEnabled(Boolean pyPackageAnalyzerEnabled) {
657 this.pyPackageAnalyzerEnabled = pyPackageAnalyzerEnabled;
658 }
659
660
661
662
663 private Boolean pyDistributionAnalyzerEnabled;
664
665
666
667
668
669
670 public Boolean isPyDistributionAnalyzerEnabled() {
671 return pyDistributionAnalyzerEnabled;
672 }
673
674
675
676
677
678
679
680 public void setPyDistributionAnalyzerEnabled(Boolean pyDistributionAnalyzerEnabled) {
681 this.pyDistributionAnalyzerEnabled = pyDistributionAnalyzerEnabled;
682 }
683
684
685
686
687 private Boolean centralAnalyzerEnabled;
688
689
690
691
692
693
694 public Boolean isCentralAnalyzerEnabled() {
695 return centralAnalyzerEnabled;
696 }
697
698
699
700
701
702
703 public void setCentralAnalyzerEnabled(Boolean centralAnalyzerEnabled) {
704 this.centralAnalyzerEnabled = centralAnalyzerEnabled;
705 }
706
707
708
709
710 private Boolean nexusAnalyzerEnabled;
711
712
713
714
715
716
717 public Boolean isNexusAnalyzerEnabled() {
718 return nexusAnalyzerEnabled;
719 }
720
721
722
723
724
725
726 public void setNexusAnalyzerEnabled(Boolean nexusAnalyzerEnabled) {
727 this.nexusAnalyzerEnabled = nexusAnalyzerEnabled;
728 }
729
730
731
732
733
734 private String nexusUrl;
735
736
737
738
739
740
741 public String getNexusUrl() {
742 return nexusUrl;
743 }
744
745
746
747
748
749
750 public void setNexusUrl(String nexusUrl) {
751 this.nexusUrl = nexusUrl;
752 }
753
754
755
756 private Boolean nexusUsesProxy;
757
758
759
760
761
762
763 public Boolean isNexusUsesProxy() {
764 return nexusUsesProxy;
765 }
766
767
768
769
770
771
772 public void setNexusUsesProxy(Boolean nexusUsesProxy) {
773 this.nexusUsesProxy = nexusUsesProxy;
774 }
775
776
777
778
779
780 private String zipExtensions;
781
782
783
784
785
786
787 public String getZipExtensions() {
788 return zipExtensions;
789 }
790
791
792
793
794
795
796 public void setZipExtensions(String zipExtensions) {
797 this.zipExtensions = zipExtensions;
798 }
799
800
801
802
803 private String pathToMono;
804
805
806
807
808
809
810 public String getPathToMono() {
811 return pathToMono;
812 }
813
814
815
816
817
818
819 public void setPathToMono(String pathToMono) {
820 this.pathToMono = pathToMono;
821 }
822
823 @Override
824 public void execute() throws BuildException {
825 dealWithReferences();
826 validateConfiguration();
827 populateSettings();
828 Engine engine = null;
829 try {
830 engine = new Engine(Check.class.getClassLoader());
831 if (isUpdateOnly()) {
832 log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN);
833 try {
834 engine.doUpdates();
835 } catch (UpdateException ex) {
836 if (this.isFailOnError()) {
837 throw new BuildException(ex);
838 }
839 log(ex.getMessage(), Project.MSG_ERR);
840 }
841 } else {
842 for (Resource resource : path) {
843 final FileProvider provider = resource.as(FileProvider.class);
844 if (provider != null) {
845 final File file = provider.getFile();
846 if (file != null && file.exists()) {
847 engine.scan(file);
848 }
849 }
850 }
851
852 try {
853 engine.analyzeDependencies();
854 } catch (ExceptionCollection ex) {
855 if (this.isFailOnError()) {
856 throw new BuildException(ex);
857 }
858 }
859 DatabaseProperties prop = null;
860 CveDB cve = null;
861 try {
862 cve = new CveDB();
863 cve.open();
864 prop = cve.getDatabaseProperties();
865 } catch (DatabaseException ex) {
866 log("Unable to retrieve DB Properties", ex, Project.MSG_DEBUG);
867 } finally {
868 if (cve != null) {
869 cve.close();
870 }
871 }
872 final ReportGenerator reporter = new ReportGenerator(getProjectName(), engine.getDependencies(), engine.getAnalyzers(), prop);
873 reporter.generateReports(reportOutputDirectory, reportFormat);
874
875 if (this.failBuildOnCVSS <= 10) {
876 checkForFailure(engine.getDependencies());
877 }
878 if (this.showSummary) {
879 showSummary(engine.getDependencies());
880 }
881 }
882 } catch (DatabaseException ex) {
883 final String msg = "Unable to connect to the dependency-check database; analysis has stopped";
884 if (this.isFailOnError()) {
885 throw new BuildException(msg, ex);
886 }
887 log(msg, ex, Project.MSG_ERR);
888 } catch (ReportException ex) {
889 final String msg = "Unable to generate the dependency-check report";
890 if (this.isFailOnError()) {
891 throw new BuildException(msg, ex);
892 }
893 log(msg, ex, Project.MSG_ERR);
894 } finally {
895 Settings.cleanup(true);
896 if (engine != null) {
897 engine.cleanup();
898 }
899 }
900 }
901
902
903
904
905
906
907
908 private void validateConfiguration() throws BuildException {
909 if (path == null) {
910 throw new BuildException("No project dependencies have been defined to analyze.");
911 }
912 if (failBuildOnCVSS < 0 || failBuildOnCVSS > 11) {
913 throw new BuildException("Invalid configuration, failBuildOnCVSS must be between 0 and 11.");
914 }
915 }
916
917
918
919
920
921
922
923
924 @Override
925 protected void populateSettings() throws BuildException {
926 super.populateSettings();
927 Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
928 Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
929 Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile);
930 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental);
931 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled);
932 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled);
933 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled);
934 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, rubygemsAnalyzerEnabled);
935 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, opensslAnalyzerEnabled);
936 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CMAKE_ENABLED, cmakeAnalyzerEnabled);
937 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, autoconfAnalyzerEnabled);
938 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled);
939 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled);
940 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled);
941 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled);
942 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled);
943 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled);
944 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled);
945 Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl);
946 Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy);
947 Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions);
948 Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono);
949 }
950
951
952
953
954
955
956
957
958
959 private void checkForFailure(List<Dependency> dependencies) throws BuildException {
960 final StringBuilder ids = new StringBuilder();
961 for (Dependency d : dependencies) {
962 for (Vulnerability v : d.getVulnerabilities()) {
963 if (v.getCvssScore() >= failBuildOnCVSS) {
964 if (ids.length() == 0) {
965 ids.append(v.getName());
966 } else {
967 ids.append(", ").append(v.getName());
968 }
969 }
970 }
971 }
972 if (ids.length() > 0) {
973 final String msg = String.format("%n%nDependency-Check Failure:%n"
974 + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
975 + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
976 throw new BuildException(msg);
977 }
978 }
979
980
981
982
983
984
985
986 private void showSummary(List<Dependency> dependencies) {
987 final StringBuilder summary = new StringBuilder();
988 for (Dependency d : dependencies) {
989 boolean firstEntry = true;
990 final StringBuilder ids = new StringBuilder();
991 for (Vulnerability v : d.getVulnerabilities()) {
992 if (firstEntry) {
993 firstEntry = false;
994 } else {
995 ids.append(", ");
996 }
997 ids.append(v.getName());
998 }
999 if (ids.length() > 0) {
1000 summary.append(d.getFileName()).append(" (");
1001 firstEntry = true;
1002 for (Identifier id : d.getIdentifiers()) {
1003 if (firstEntry) {
1004 firstEntry = false;
1005 } else {
1006 summary.append(", ");
1007 }
1008 summary.append(id.getValue());
1009 }
1010 summary.append(") : ").append(ids).append(NEW_LINE);
1011 }
1012 }
1013 if (summary.length() > 0) {
1014 final String msg = String.format("%n%n"
1015 + "One or more dependencies were identified with known vulnerabilities:%n%n%s"
1016 + "%n%nSee the dependency-check report for more details.%n%n", summary.toString());
1017 log(msg, Project.MSG_WARN);
1018 }
1019 }
1020
1021
1022
1023
1024
1025 public static class ReportFormats extends EnumeratedAttribute {
1026
1027
1028
1029
1030
1031
1032 @Override
1033 public String[] getValues() {
1034 int i = 0;
1035 final Format[] formats = Format.values();
1036 final String[] values = new String[formats.length];
1037 for (Format format : formats) {
1038 values[i++] = format.name();
1039 }
1040 return values;
1041 }
1042 }
1043 }