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