1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.owasp.dependencycheck.taskdefs;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.List;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27 import org.apache.tools.ant.BuildException;
28 import org.apache.tools.ant.Task;
29 import org.apache.tools.ant.types.EnumeratedAttribute;
30 import org.apache.tools.ant.types.Reference;
31 import org.apache.tools.ant.types.Resource;
32 import org.apache.tools.ant.types.ResourceCollection;
33 import org.apache.tools.ant.types.resources.FileProvider;
34 import org.apache.tools.ant.types.resources.Resources;
35 import org.owasp.dependencycheck.Engine;
36 import org.owasp.dependencycheck.dependency.Dependency;
37 import org.owasp.dependencycheck.dependency.Identifier;
38 import org.owasp.dependencycheck.dependency.Vulnerability;
39 import org.owasp.dependencycheck.reporting.ReportGenerator;
40 import org.owasp.dependencycheck.reporting.ReportGenerator.Format;
41 import org.owasp.dependencycheck.utils.LogUtils;
42 import org.owasp.dependencycheck.utils.Settings;
43
44
45
46
47
48
49 public class DependencyCheckTask extends Task {
50
51
52
53
54 private static final String PROPERTIES_FILE = "task.properties";
55
56
57
58 private static final String LOG_PROPERTIES_FILE = "log.properties";
59
60
61
62 private static final String NEW_LINE = System.getProperty("line.separator", "\n").intern();
63
64
65
66
67 public DependencyCheckTask() {
68 super();
69 }
70
71
72
73
74
75 private Resources path = null;
76
77
78
79 private Reference refid = null;
80
81
82
83
84
85
86
87 public void add(ResourceCollection rc) {
88 if (isReference()) {
89 throw new BuildException("Nested elements are not allowed when using the refid attribute.");
90 }
91 getPath().add(rc);
92 }
93
94
95
96
97
98
99
100 private synchronized Resources getPath() {
101 if (path == null) {
102 path = new Resources(getProject());
103 path.setCache(true);
104 }
105 return path;
106 }
107
108
109
110
111
112
113 public boolean isReference() {
114 return refid != null;
115 }
116
117
118
119
120
121
122
123 public void setRefid(Reference r) {
124 if (path != null) {
125 throw new BuildException("Nested elements are not allowed when using the refid attribute.");
126 }
127 refid = r;
128 }
129
130
131
132
133
134
135
136 private void dealWithReferences() throws BuildException {
137 if (isReference()) {
138 final Object o = refid.getReferencedObject(getProject());
139 if (!(o instanceof ResourceCollection)) {
140 throw new BuildException("refid '" + refid.getRefId()
141 + "' does not refer to a resource collection.");
142 }
143 getPath().add((ResourceCollection) o);
144 }
145 }
146
147
148
149
150 private String applicationName = "Dependency-Check";
151
152
153
154
155
156
157 public String getApplicationName() {
158 return applicationName;
159 }
160
161
162
163
164
165
166 public void setApplicationName(String applicationName) {
167 this.applicationName = applicationName;
168 }
169
170
171
172 private String dataDirectory = null;
173
174
175
176
177
178
179 public String getDataDirectory() {
180 return dataDirectory;
181 }
182
183
184
185
186
187
188 public void setDataDirectory(String dataDirectory) {
189 this.dataDirectory = dataDirectory;
190 }
191
192
193
194
195 private String reportOutputDirectory = ".";
196
197
198
199
200
201
202 public String getReportOutputDirectory() {
203 return reportOutputDirectory;
204 }
205
206
207
208
209
210
211 public void setReportOutputDirectory(String reportOutputDirectory) {
212 this.reportOutputDirectory = reportOutputDirectory;
213 }
214
215
216
217
218
219
220
221 private float failBuildOnCVSS = 11;
222
223
224
225
226
227
228 public float getFailBuildOnCVSS() {
229 return failBuildOnCVSS;
230 }
231
232
233
234
235
236
237 public void setFailBuildOnCVSS(float failBuildOnCVSS) {
238 this.failBuildOnCVSS = failBuildOnCVSS;
239 }
240
241
242
243
244 private boolean autoUpdate = true;
245
246
247
248
249
250
251 public boolean isAutoUpdate() {
252 return autoUpdate;
253 }
254
255
256
257
258
259
260 public void setAutoUpdate(boolean autoUpdate) {
261 this.autoUpdate = autoUpdate;
262 }
263
264
265
266
267
268 private String reportFormat = "HTML";
269
270
271
272
273
274
275 public String getReportFormat() {
276 return reportFormat;
277 }
278
279
280
281
282
283
284 public void setReportFormat(ReportFormats reportFormat) {
285 this.reportFormat = reportFormat.getValue();
286 }
287
288
289
290 private String proxyUrl;
291
292
293
294
295
296
297 public String getProxyUrl() {
298 return proxyUrl;
299 }
300
301
302
303
304
305
306 public void setProxyUrl(String proxyUrl) {
307 this.proxyUrl = proxyUrl;
308 }
309
310
311
312 private String proxyPort;
313
314
315
316
317
318
319 public String getProxyPort() {
320 return proxyPort;
321 }
322
323
324
325
326
327
328 public void setProxyPort(String proxyPort) {
329 this.proxyPort = proxyPort;
330 }
331
332
333
334 private String proxyUsername;
335
336
337
338
339
340
341 public String getProxyUsername() {
342 return proxyUsername;
343 }
344
345
346
347
348
349
350 public void setProxyUsername(String proxyUsername) {
351 this.proxyUsername = proxyUsername;
352 }
353
354
355
356 private String proxyPassword;
357
358
359
360
361
362
363 public String getProxyPassword() {
364 return proxyPassword;
365 }
366
367
368
369
370
371
372 public void setProxyPassword(String proxyPassword) {
373 this.proxyPassword = proxyPassword;
374 }
375
376
377
378 private String connectionTimeout;
379
380
381
382
383
384
385 public String getConnectionTimeout() {
386 return connectionTimeout;
387 }
388
389
390
391
392
393
394 public void setConnectionTimeout(String connectionTimeout) {
395 this.connectionTimeout = connectionTimeout;
396 }
397
398
399
400 private String logFile = null;
401
402
403
404
405
406
407 public String getLogFile() {
408 return logFile;
409 }
410
411
412
413
414
415
416 public void setLogFile(String logFile) {
417 this.logFile = logFile;
418 }
419
420
421
422 private String suppressionFile;
423
424
425
426
427
428
429 public String getSuppressionFile() {
430 return suppressionFile;
431 }
432
433
434
435
436
437
438 public void setSuppressionFile(String suppressionFile) {
439 this.suppressionFile = suppressionFile;
440 }
441
442
443
444 private boolean showSummary = true;
445
446
447
448
449
450
451 public boolean isShowSummary() {
452 return showSummary;
453 }
454
455
456
457
458
459
460 public void setShowSummary(boolean showSummary) {
461 this.showSummary = showSummary;
462 }
463
464 @Override
465 public void execute() throws BuildException {
466 final InputStream in = DependencyCheckTask.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
467 LogUtils.prepareLogger(in, logFile);
468
469 dealWithReferences();
470 validateConfiguration();
471 populateSettings();
472
473 final Engine engine = new Engine();
474 for (Resource resource : path) {
475 final FileProvider provider = resource.as(FileProvider.class);
476 if (provider != null) {
477 final File file = provider.getFile();
478 if (file != null && file.exists()) {
479 engine.scan(file);
480 }
481 }
482 }
483 try {
484 engine.analyzeDependencies();
485 final ReportGenerator reporter = new ReportGenerator(applicationName, engine.getDependencies(), engine.getAnalyzers());
486 reporter.generateReports(reportOutputDirectory, reportFormat);
487
488 if (this.failBuildOnCVSS <= 10) {
489 checkForFailure(engine.getDependencies());
490 }
491 if (this.showSummary) {
492 showSummary(engine.getDependencies());
493 }
494 } catch (IOException ex) {
495 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, "Unable to generate dependency-check report", ex);
496 throw new BuildException("Unable to generate dependency-check report", ex);
497 } catch (Exception ex) {
498 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, "An exception occurred; unable to continue task", ex);
499 throw new BuildException("An exception occurred; unable to continue task", ex);
500 }
501 }
502
503
504
505
506
507
508
509 private void validateConfiguration() throws BuildException {
510 if (path == null) {
511 throw new BuildException("No project dependencies have been defined to analyze.");
512 }
513 if (failBuildOnCVSS < 0 || failBuildOnCVSS > 11) {
514 throw new BuildException("Invalid configuration, failBuildOnCVSS must be between 0 and 11.");
515 }
516 }
517
518
519
520
521
522
523 private void populateSettings() {
524 InputStream taskProperties = null;
525 try {
526 taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
527 Settings.mergeProperties(taskProperties);
528 } catch (IOException ex) {
529 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.WARNING, "Unable to load the dependency-check ant task.properties file.");
530 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, null, ex);
531 } finally {
532 if (taskProperties != null) {
533 try {
534 taskProperties.close();
535 } catch (IOException ex) {
536 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINEST, null, ex);
537 }
538 }
539 }
540 if (dataDirectory != null) {
541 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
542 } else {
543 final File jarPath = new File(DependencyCheckTask.class.getProtectionDomain().getCodeSource().getLocation().getPath());
544 final File base = jarPath.getParentFile();
545 final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
546 final File dataDir = new File(base, sub);
547 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
548 }
549
550 Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
551
552 if (proxyUrl != null && !proxyUrl.isEmpty()) {
553 Settings.setString(Settings.KEYS.PROXY_URL, proxyUrl);
554 }
555 if (proxyPort != null && !proxyPort.isEmpty()) {
556 Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
557 }
558 if (proxyUsername != null && !proxyUsername.isEmpty()) {
559 Settings.setString(Settings.KEYS.PROXY_USERNAME, proxyUsername);
560 }
561 if (proxyPassword != null && !proxyPassword.isEmpty()) {
562 Settings.setString(Settings.KEYS.PROXY_PASSWORD, proxyPassword);
563 }
564 if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
565 Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
566 }
567 if (suppressionFile != null && !suppressionFile.isEmpty()) {
568 Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile);
569 }
570 }
571
572
573
574
575
576
577
578
579
580 private void checkForFailure(List<Dependency> dependencies) throws BuildException {
581 final StringBuilder ids = new StringBuilder();
582 for (Dependency d : dependencies) {
583 for (Vulnerability v : d.getVulnerabilities()) {
584 if (v.getCvssScore() >= failBuildOnCVSS) {
585 if (ids.length() == 0) {
586 ids.append(v.getName());
587 } else {
588 ids.append(", ").append(v.getName());
589 }
590 }
591 }
592 }
593 if (ids.length() > 0) {
594 final String msg = String.format("%n%nDependency-Check Failure:%n"
595 + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
596 + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
597 throw new BuildException(msg);
598 }
599 }
600
601
602
603
604
605
606
607 private void showSummary(List<Dependency> dependencies) {
608 final StringBuilder summary = new StringBuilder();
609 for (Dependency d : dependencies) {
610 boolean firstEntry = true;
611 final StringBuilder ids = new StringBuilder();
612 for (Vulnerability v : d.getVulnerabilities()) {
613 if (firstEntry) {
614 firstEntry = false;
615 } else {
616 ids.append(", ");
617 }
618 ids.append(v.getName());
619 }
620 if (ids.length() > 0) {
621 summary.append(d.getFileName()).append(" (");
622 firstEntry = true;
623 for (Identifier id : d.getIdentifiers()) {
624 if (firstEntry) {
625 firstEntry = false;
626 } else {
627 summary.append(", ");
628 }
629 summary.append(id.getValue());
630 }
631 summary.append(") : ").append(ids).append(NEW_LINE);
632 }
633 }
634 if (summary.length() > 0) {
635 final String msg = String.format("%n%n"
636 + "One or more dependencies were identified with known vulnerabilities:%n%n%s"
637 + "%n%nSee the dependency-check report for more details.%n%n", summary.toString());
638 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.WARNING, msg);
639 }
640 }
641
642
643
644
645
646 public static class ReportFormats extends EnumeratedAttribute {
647
648
649
650
651
652
653 @Override
654 public String[] getValues() {
655 int i = 0;
656 final Format[] formats = Format.values();
657 final String[] values = new String[formats.length];
658 for (Format format : formats) {
659 values[i++] = format.name();
660 }
661 return values;
662 }
663 }
664 }