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.LogManager;
27 import java.util.logging.Logger;
28 import org.apache.tools.ant.BuildException;
29 import org.apache.tools.ant.Task;
30 import org.apache.tools.ant.types.EnumeratedAttribute;
31 import org.apache.tools.ant.types.Reference;
32 import org.apache.tools.ant.types.Resource;
33 import org.apache.tools.ant.types.ResourceCollection;
34 import org.apache.tools.ant.types.resources.FileProvider;
35 import org.apache.tools.ant.types.resources.Resources;
36 import org.owasp.dependencycheck.Engine;
37 import org.owasp.dependencycheck.dependency.Dependency;
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.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 connectionTimeout;
330
331
332
333
334
335
336 public String getConnectionTimeout() {
337 return connectionTimeout;
338 }
339
340
341
342
343
344
345 public void setConnectionTimeout(String connectionTimeout) {
346 this.connectionTimeout = connectionTimeout;
347 }
348
349
350
351
352 private static void prepareLogger() {
353 InputStream in = null;
354 try {
355 in = DependencyCheckTask.class.getClassLoader().getResourceAsStream(LOG_PROPERTIES_FILE);
356 LogManager.getLogManager().reset();
357 LogManager.getLogManager().readConfiguration(in);
358
359
360
361
362
363 } catch (IOException ex) {
364 System.err.println(ex.toString());
365 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
366 } catch (SecurityException ex) {
367 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
368 } finally {
369 if (in != null) {
370 try {
371 in.close();
372 } catch (Exception ex) {
373
374 in = null;
375 }
376 }
377 }
378 }
379
380 @Override
381 public void execute() throws BuildException {
382 prepareLogger();
383
384 dealWithReferences();
385 validateConfiguration();
386 populateSettings();
387
388 final Engine engine = new Engine();
389 for (Resource resource : path) {
390 final FileProvider provider = resource.as(FileProvider.class);
391 if (provider != null) {
392 final File file = provider.getFile();
393 if (file != null && file.exists()) {
394 engine.scan(file);
395 }
396 }
397 }
398 try {
399 engine.analyzeDependencies();
400 final ReportGenerator reporter = new ReportGenerator(applicationName, engine.getDependencies(), engine.getAnalyzers());
401 reporter.generateReports(reportOutputDirectory, reportFormat);
402
403 if (this.failBuildOnCVSS <= 10) {
404 checkForFailure(engine.getDependencies());
405 }
406 } catch (IOException ex) {
407 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, null, ex);
408 throw new BuildException("Unable to generate dependency-check report", ex);
409 } catch (Exception ex) {
410 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.SEVERE, null, ex);
411 throw new BuildException("An exception occured; unable to continue task", ex);
412 }
413 }
414
415
416
417
418
419
420
421 private void validateConfiguration() throws BuildException {
422 if (path == null) {
423 throw new BuildException("No project dependencies have been defined to analyze.");
424 }
425 if (failBuildOnCVSS < 0 || failBuildOnCVSS > 11) {
426 throw new BuildException("Invalid configuration, failBuildOnCVSS must be between 0 and 11.");
427 }
428 }
429
430
431
432
433
434
435 private void populateSettings() {
436 InputStream taskProperties = null;
437 try {
438 taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
439 Settings.mergeProperties(taskProperties);
440 } catch (IOException ex) {
441 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.WARNING, "Unable to load the dependency-check ant task.properties file.");
442 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINE, null, ex);
443 } finally {
444 if (taskProperties != null) {
445 try {
446 taskProperties.close();
447 } catch (IOException ex) {
448 Logger.getLogger(DependencyCheckTask.class.getName()).log(Level.FINEST, null, ex);
449 }
450 }
451 }
452 if (dataDirectory != null) {
453 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
454 } else {
455 final File jarPath = new File(DependencyCheckTask.class.getProtectionDomain().getCodeSource().getLocation().getPath());
456 final File base = jarPath.getParentFile();
457 final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY);
458 final File dataDir = new File(base, sub);
459 Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
460 }
461
462 Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate);
463
464 if (proxyUrl != null && !proxyUrl.isEmpty()) {
465 Settings.setString(Settings.KEYS.PROXY_URL, proxyUrl);
466 }
467 if (proxyPort != null && !proxyPort.isEmpty()) {
468 Settings.setString(Settings.KEYS.PROXY_PORT, proxyPort);
469 }
470 if (connectionTimeout != null && !connectionTimeout.isEmpty()) {
471 Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout);
472 }
473 }
474
475
476
477
478
479
480
481
482
483 private void checkForFailure(List<Dependency> dependencies) throws BuildException {
484 final StringBuilder ids = new StringBuilder();
485 for (Dependency d : dependencies) {
486 for (Vulnerability v : d.getVulnerabilities()) {
487 if (v.getCvssScore() >= failBuildOnCVSS) {
488 if (ids.length() == 0) {
489 ids.append(v.getName());
490 } else {
491 ids.append(", ").append(v.getName());
492 }
493 }
494 }
495 }
496 if (ids.length() > 0) {
497 final String msg = String.format("%n%nDependency-Check Failure:%n"
498 + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater then '%.1f': %s%n"
499 + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString());
500 throw new BuildException(msg);
501 }
502 }
503
504
505
506
507
508 public static class ReportFormats extends EnumeratedAttribute {
509
510
511
512
513
514
515 public String[] getValues() {
516 int i = 0;
517 final Format[] formats = Format.values();
518 final String[] values = new String[formats.length];
519 for (Format format : formats) {
520 values[i++] = format.name();
521 }
522 return values;
523 }
524 }
525 }