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