1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.owasp.dependencycheck;
19
20 import org.owasp.dependencycheck.analyzer.AnalysisPhase;
21 import org.owasp.dependencycheck.analyzer.Analyzer;
22 import org.owasp.dependencycheck.analyzer.AnalyzerService;
23 import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer;
24 import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
25 import org.owasp.dependencycheck.data.nvdcve.ConnectionFactory;
26 import org.owasp.dependencycheck.data.nvdcve.CveDB;
27 import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
28 import org.owasp.dependencycheck.data.update.CachedWebDataSource;
29 import org.owasp.dependencycheck.data.update.UpdateService;
30 import org.owasp.dependencycheck.data.update.exception.UpdateException;
31 import org.owasp.dependencycheck.dependency.Dependency;
32 import org.owasp.dependencycheck.exception.NoDataException;
33 import org.owasp.dependencycheck.exception.ExceptionCollection;
34 import org.owasp.dependencycheck.exception.InitializationException;
35 import org.owasp.dependencycheck.utils.InvalidSettingException;
36 import org.owasp.dependencycheck.utils.Settings;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import java.io.File;
41 import java.io.FileFilter;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.EnumMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50
51
52
53
54
55
56
57
58
59 public class Engine implements FileFilter {
60
61
62
63
64 private List<Dependency> dependencies = new ArrayList<Dependency>();
65
66
67
68 private final Map<AnalysisPhase, List<Analyzer>> analyzers = new EnumMap<AnalysisPhase, List<Analyzer>>(AnalysisPhase.class);
69
70
71
72
73 private final Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<FileTypeAnalyzer>();
74
75
76
77
78
79 private ClassLoader serviceClassLoader = Thread.currentThread().getContextClassLoader();
80
81
82
83 private static final Logger LOGGER = LoggerFactory.getLogger(Engine.class);
84
85
86
87
88
89
90
91 public Engine() throws DatabaseException {
92 initializeEngine();
93 }
94
95
96
97
98
99
100
101
102 public Engine(ClassLoader serviceClassLoader) throws DatabaseException {
103 this.serviceClassLoader = serviceClassLoader;
104 initializeEngine();
105 }
106
107
108
109
110
111
112
113
114 protected final void initializeEngine() throws DatabaseException {
115 ConnectionFactory.initialize();
116 loadAnalyzers();
117 }
118
119
120
121
122 public void cleanup() {
123 ConnectionFactory.cleanup();
124 }
125
126
127
128
129
130 private void loadAnalyzers() {
131 if (!analyzers.isEmpty()) {
132 return;
133 }
134 for (AnalysisPhase phase : AnalysisPhase.values()) {
135 analyzers.put(phase, new ArrayList<Analyzer>());
136 }
137
138 final AnalyzerService service = new AnalyzerService(serviceClassLoader);
139 final List<Analyzer> iterator = service.getAnalyzers();
140 for (Analyzer a : iterator) {
141 analyzers.get(a.getAnalysisPhase()).add(a);
142 if (a instanceof FileTypeAnalyzer) {
143 this.fileTypeAnalyzers.add((FileTypeAnalyzer) a);
144 }
145 }
146 }
147
148
149
150
151
152
153
154 public List<Analyzer> getAnalyzers(AnalysisPhase phase) {
155 return analyzers.get(phase);
156 }
157
158
159
160
161
162
163 public List<Dependency> getDependencies() {
164 return dependencies;
165 }
166
167
168
169
170
171
172 public void setDependencies(List<Dependency> dependencies) {
173 this.dependencies = dependencies;
174 }
175
176
177
178
179
180
181
182
183
184
185 public List<Dependency> scan(String[] paths) {
186 final List<Dependency> deps = new ArrayList<Dependency>();
187 for (String path : paths) {
188 final List<Dependency> d = scan(path);
189 if (d != null) {
190 deps.addAll(d);
191 }
192 }
193 return deps;
194 }
195
196
197
198
199
200
201
202
203
204 public List<Dependency> scan(String path) {
205 final File file = new File(path);
206 return scan(file);
207 }
208
209
210
211
212
213
214
215
216
217
218 public List<Dependency> scan(File[] files) {
219 final List<Dependency> deps = new ArrayList<Dependency>();
220 for (File file : files) {
221 final List<Dependency> d = scan(file);
222 if (d != null) {
223 deps.addAll(d);
224 }
225 }
226 return deps;
227 }
228
229
230
231
232
233
234
235
236
237
238 public List<Dependency> scan(Collection<File> files) {
239 final List<Dependency> deps = new ArrayList<Dependency>();
240 for (File file : files) {
241 final List<Dependency> d = scan(file);
242 if (d != null) {
243 deps.addAll(d);
244 }
245 }
246 return deps;
247 }
248
249
250
251
252
253
254
255
256
257
258 public List<Dependency> scan(File file) {
259 if (file.exists()) {
260 if (file.isDirectory()) {
261 return scanDirectory(file);
262 } else {
263 final Dependency d = scanFile(file);
264 if (d != null) {
265 final List<Dependency> deps = new ArrayList<Dependency>();
266 deps.add(d);
267 return deps;
268 }
269 }
270 }
271 return null;
272 }
273
274
275
276
277
278
279
280
281 protected List<Dependency> scanDirectory(File dir) {
282 final File[] files = dir.listFiles();
283 final List<Dependency> deps = new ArrayList<Dependency>();
284 if (files != null) {
285 for (File f : files) {
286 if (f.isDirectory()) {
287 final List<Dependency> d = scanDirectory(f);
288 if (d != null) {
289 deps.addAll(d);
290 }
291 } else {
292 final Dependency d = scanFile(f);
293 deps.add(d);
294 }
295 }
296 }
297 return deps;
298 }
299
300
301
302
303
304
305
306
307 protected Dependency scanFile(File file) {
308 Dependency dependency = null;
309 if (file.isFile()) {
310 if (accept(file)) {
311 dependency = new Dependency(file);
312 dependencies.add(dependency);
313 }
314 } else {
315 LOGGER.debug("Path passed to scanFile(File) is not a file: {}. Skipping the file.", file);
316 }
317 return dependency;
318 }
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335 public void analyzeDependencies() throws ExceptionCollection {
336 final List<Throwable> exceptions = new ArrayList<Throwable>();
337 boolean autoUpdate = true;
338 try {
339 autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE);
340 } catch (InvalidSettingException ex) {
341 LOGGER.debug("Invalid setting for auto-update; using true.");
342 exceptions.add(ex);
343 }
344 if (autoUpdate) {
345 try {
346 doUpdates();
347 } catch (UpdateException ex) {
348 exceptions.add(ex);
349 LOGGER.warn("Unable to update Cached Web DataSource, using local "
350 + "data instead. Results may not include recent vulnerabilities.");
351 LOGGER.debug("Update Error", ex);
352 }
353 }
354
355
356 try {
357 ensureDataExists();
358 } catch (NoDataException ex) {
359 LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
360 LOGGER.debug("", ex);
361 exceptions.add(ex);
362 throw new ExceptionCollection("Unable to continue dependency-check analysis.", exceptions, true);
363 } catch (DatabaseException ex) {
364 LOGGER.error("{}\n\nUnable to continue dependency-check analysis.", ex.getMessage());
365 LOGGER.debug("", ex);
366 exceptions.add(ex);
367 throw new ExceptionCollection("Unable to connect to the dependency-check database", exceptions, true);
368 }
369
370 LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------");
371 LOGGER.info("Analysis Started");
372 final long analysisStart = System.currentTimeMillis();
373
374
375 for (AnalysisPhase phase : AnalysisPhase.values()) {
376 final List<Analyzer> analyzerList = analyzers.get(phase);
377
378 for (Analyzer a : analyzerList) {
379 try {
380 a = initializeAnalyzer(a);
381 } catch (InitializationException ex) {
382 exceptions.add(ex);
383 continue;
384 }
385
386
387
388
389
390 LOGGER.debug("Begin Analyzer '{}'", a.getName());
391 final Set<Dependency> dependencySet = new HashSet<Dependency>(dependencies);
392 for (Dependency d : dependencySet) {
393 boolean shouldAnalyze = true;
394 if (a instanceof FileTypeAnalyzer) {
395 final FileTypeAnalyzer fAnalyzer = (FileTypeAnalyzer) a;
396 shouldAnalyze = fAnalyzer.accept(d.getActualFile());
397 }
398 if (shouldAnalyze) {
399 LOGGER.debug("Begin Analysis of '{}'", d.getActualFilePath());
400 try {
401 a.analyze(d, this);
402 } catch (AnalysisException ex) {
403 LOGGER.warn("An error occurred while analyzing '{}'.", d.getActualFilePath());
404 LOGGER.debug("", ex);
405 exceptions.add(ex);
406 } catch (Throwable ex) {
407
408 LOGGER.warn("An unexpected error occurred during analysis of '{}'", d.getActualFilePath());
409 LOGGER.debug("", ex);
410 exceptions.add(ex);
411 }
412 }
413 }
414 }
415 }
416 for (AnalysisPhase phase : AnalysisPhase.values()) {
417 final List<Analyzer> analyzerList = analyzers.get(phase);
418
419 for (Analyzer a : analyzerList) {
420 closeAnalyzer(a);
421 }
422 }
423
424 LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------");
425 LOGGER.info("Analysis Complete ({} ms)", System.currentTimeMillis() - analysisStart);
426 if (exceptions.size() > 0) {
427 throw new ExceptionCollection("One or more exceptions occured during dependency-check analysis", exceptions);
428 }
429 }
430
431
432
433
434
435
436
437
438
439 protected Analyzer initializeAnalyzer(Analyzer analyzer) throws InitializationException {
440 try {
441 LOGGER.debug("Initializing {}", analyzer.getName());
442 analyzer.initialize();
443 } catch (InitializationException ex) {
444 LOGGER.error("Exception occurred initializing {}.", analyzer.getName());
445 LOGGER.debug("", ex);
446 try {
447 analyzer.close();
448 } catch (Throwable ex1) {
449 LOGGER.trace("", ex1);
450 }
451 throw ex;
452 } catch (Throwable ex) {
453 LOGGER.error("Unexpected exception occurred initializing {}.", analyzer.getName());
454 LOGGER.debug("", ex);
455 try {
456 analyzer.close();
457 } catch (Throwable ex1) {
458 LOGGER.trace("", ex1);
459 }
460 throw new InitializationException("Unexpected Exception", ex);
461 }
462 return analyzer;
463 }
464
465
466
467
468
469
470 protected void closeAnalyzer(Analyzer analyzer) {
471 LOGGER.debug("Closing Analyzer '{}'", analyzer.getName());
472 try {
473 analyzer.close();
474 } catch (Throwable ex) {
475 LOGGER.trace("", ex);
476 }
477 }
478
479
480
481
482
483
484
485 public void doUpdates() throws UpdateException {
486 LOGGER.info("Checking for updates");
487 final long updateStart = System.currentTimeMillis();
488 final UpdateService service = new UpdateService(serviceClassLoader);
489 final Iterator<CachedWebDataSource> iterator = service.getDataSources();
490 while (iterator.hasNext()) {
491 final CachedWebDataSource source = iterator.next();
492 source.update();
493 }
494 LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart);
495 }
496
497
498
499
500
501
502
503 public List<Analyzer> getAnalyzers() {
504 final List<Analyzer> ret = new ArrayList<Analyzer>();
505 for (AnalysisPhase phase : AnalysisPhase.values()) {
506 final List<Analyzer> analyzerList = analyzers.get(phase);
507 ret.addAll(analyzerList);
508 }
509 return ret;
510 }
511
512
513
514
515
516
517
518
519 @Override
520 public boolean accept(File file) {
521 if (file == null) {
522 return false;
523 }
524 boolean scan = false;
525 for (FileTypeAnalyzer a : this.fileTypeAnalyzers) {
526
527
528 scan |= a.accept(file);
529 }
530 return scan;
531 }
532
533
534
535
536
537
538 public Set<FileTypeAnalyzer> getFileTypeAnalyzers() {
539 return this.fileTypeAnalyzers;
540 }
541
542
543
544
545
546
547
548
549
550 private void ensureDataExists() throws NoDataException, DatabaseException {
551 final CveDB cve = new CveDB();
552 try {
553 cve.open();
554 if (!cve.dataExists()) {
555 throw new NoDataException("No documents exist");
556 }
557 } catch (DatabaseException ex) {
558 throw new NoDataException(ex.getMessage(), ex);
559 } finally {
560 cve.close();
561 }
562 }
563 }