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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
21  import org.apache.commons.jcs3.JCS;
22  import org.jetbrains.annotations.NotNull;
23  import org.jetbrains.annotations.Nullable;
24  import org.owasp.dependencycheck.analyzer.AnalysisPhase;
25  import org.owasp.dependencycheck.analyzer.Analyzer;
26  import org.owasp.dependencycheck.analyzer.AnalyzerService;
27  import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer;
28  import org.owasp.dependencycheck.data.nvdcve.DatabaseManager;
29  import org.owasp.dependencycheck.data.nvdcve.CveDB;
30  import org.owasp.dependencycheck.data.nvdcve.DatabaseException;
31  import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties;
32  import org.owasp.dependencycheck.data.update.CachedWebDataSource;
33  import org.owasp.dependencycheck.data.update.UpdateService;
34  import org.owasp.dependencycheck.data.update.exception.UpdateException;
35  import org.owasp.dependencycheck.dependency.Dependency;
36  import org.owasp.dependencycheck.exception.ExceptionCollection;
37  import org.owasp.dependencycheck.exception.InitializationException;
38  import org.owasp.dependencycheck.exception.NoDataException;
39  import org.owasp.dependencycheck.exception.ReportException;
40  import org.owasp.dependencycheck.exception.WriteLockException;
41  import org.owasp.dependencycheck.reporting.ReportGenerator;
42  import org.owasp.dependencycheck.utils.FileUtils;
43  import org.owasp.dependencycheck.utils.Settings;
44  import org.owasp.dependencycheck.utils.WriteLock;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  import javax.annotation.concurrent.NotThreadSafe;
49  import java.io.File;
50  import java.io.FileFilter;
51  import java.io.IOException;
52  import java.nio.file.Files;
53  import java.util.ArrayList;
54  import java.util.Arrays;
55  import java.util.Collection;
56  import java.util.Collections;
57  import java.util.EnumMap;
58  import java.util.HashMap;
59  import java.util.HashSet;
60  import java.util.Iterator;
61  import java.util.List;
62  import java.util.Map;
63  import java.util.Objects;
64  import java.util.Set;
65  import java.util.concurrent.CancellationException;
66  import java.util.concurrent.ExecutionException;
67  import java.util.concurrent.ExecutorService;
68  import java.util.concurrent.Executors;
69  import java.util.concurrent.Future;
70  import java.util.concurrent.TimeUnit;
71  
72  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.FINAL;
73  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.FINDING_ANALYSIS;
74  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.FINDING_ANALYSIS_PHASE2;
75  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.IDENTIFIER_ANALYSIS;
76  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.INFORMATION_COLLECTION;
77  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.INFORMATION_COLLECTION2;
78  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.INITIAL;
79  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.POST_FINDING_ANALYSIS;
80  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.POST_IDENTIFIER_ANALYSIS;
81  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.POST_INFORMATION_COLLECTION1;
82  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.POST_INFORMATION_COLLECTION2;
83  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.POST_INFORMATION_COLLECTION3;
84  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.PRE_FINDING_ANALYSIS;
85  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.PRE_IDENTIFIER_ANALYSIS;
86  import static org.owasp.dependencycheck.analyzer.AnalysisPhase.PRE_INFORMATION_COLLECTION;
87  import org.owasp.dependencycheck.analyzer.DependencyBundlingAnalyzer;
88  import org.owasp.dependencycheck.dependency.naming.Identifier;
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  @NotThreadSafe
99  public class Engine implements FileFilter, AutoCloseable {
100 
101     
102 
103 
104     private static final Logger LOGGER = LoggerFactory.getLogger(Engine.class);
105     
106 
107 
108     private final List<Dependency> dependencies = Collections.synchronizedList(new ArrayList<>());
109     
110 
111 
112     private final Map<AnalysisPhase, List<Analyzer>> analyzers = new EnumMap<>(AnalysisPhase.class);
113     
114 
115 
116     private final Set<FileTypeAnalyzer> fileTypeAnalyzers = new HashSet<>();
117     
118 
119 
120 
121     private final Mode mode;
122     
123 
124 
125 
126     private final ClassLoader serviceClassLoader;
127     
128 
129 
130     private final Settings settings;
131     
132 
133 
134     private final Map<String, Object> objects = new HashMap<>();
135     
136 
137 
138     private Dependency[] dependenciesExternalView = null;
139     
140 
141 
142     private CveDB database = null;
143     
144 
145 
146 
147 
148 
149     private final String accessExternalSchema;
150 
151     
152 
153 
154 
155 
156     public Engine(@NotNull final Settings settings) {
157         this(Mode.STANDALONE, settings);
158     }
159 
160     
161 
162 
163 
164 
165 
166     public Engine(@NotNull final Mode mode, @NotNull final Settings settings) {
167         this(Thread.currentThread().getContextClassLoader(), mode, settings);
168     }
169 
170     
171 
172 
173 
174 
175 
176     public Engine(@NotNull final ClassLoader serviceClassLoader, @NotNull final Settings settings) {
177         this(serviceClassLoader, Mode.STANDALONE, settings);
178     }
179 
180     
181 
182 
183 
184 
185 
186 
187     public Engine(@NotNull final ClassLoader serviceClassLoader, @NotNull final Mode mode, @NotNull final Settings settings) {
188         this.settings = settings;
189         this.serviceClassLoader = serviceClassLoader;
190         this.mode = mode;
191         this.accessExternalSchema = System.getProperty("javax.xml.accessExternalSchema");
192 
193         initializeEngine();
194     }
195 
196     
197 
198 
199 
200 
201 
202 
203     protected final void initializeEngine() {
204         loadAnalyzers();
205     }
206 
207     
208 
209 
210     @Override
211     public void close() {
212         if (mode.isDatabaseRequired()) {
213             if (database != null) {
214                 database.close();
215                 database = null;
216             }
217         }
218         if (accessExternalSchema != null) {
219             System.setProperty("javax.xml.accessExternalSchema", accessExternalSchema);
220         } else {
221             System.clearProperty("javax.xml.accessExternalSchema");
222         }
223         JCS.shutdown();
224     }
225 
226     
227 
228 
229 
230     private void loadAnalyzers() {
231         if (!analyzers.isEmpty()) {
232             return;
233         }
234         mode.getPhases().forEach((phase) -> analyzers.put(phase, new ArrayList<>()));
235         final AnalyzerService service = new AnalyzerService(serviceClassLoader, settings);
236         final List<Analyzer> iterator = service.getAnalyzers(mode.getPhases());
237         iterator.forEach((a) -> {
238             a.initialize(this.settings);
239             analyzers.get(a.getAnalysisPhase()).add(a);
240             if (a instanceof FileTypeAnalyzer) {
241                 this.fileTypeAnalyzers.add((FileTypeAnalyzer) a);
242             }
243         });
244     }
245 
246     
247 
248 
249 
250 
251 
252     public List<Analyzer> getAnalyzers(AnalysisPhase phase) {
253         return analyzers.get(phase);
254     }
255 
256     
257 
258 
259 
260 
261 
262 
263     public synchronized void addDependency(Dependency dependency) {
264         if (dependency.isVirtual()) {
265             for (Dependency existing : dependencies) {
266                 if (existing.isVirtual()
267                         && existing.getSha256sum() != null
268                         && existing.getSha256sum().equals(dependency.getSha256sum())
269                         && existing.getDisplayFileName() != null
270                         && existing.getDisplayFileName().equals(dependency.getDisplayFileName())
271                         && identifiersMatch(existing.getSoftwareIdentifiers(), dependency.getSoftwareIdentifiers())) {
272                     DependencyBundlingAnalyzer.mergeDependencies(existing, dependency, null);
273                     return;
274                 }
275             }
276         }
277         dependencies.add(dependency);
278         dependenciesExternalView = null;
279     }
280 
281     
282 
283 
284     public synchronized void sortDependencies() {
285         
286 
287 
288     }
289 
290     
291 
292 
293 
294 
295     public synchronized void removeDependency(@NotNull final Dependency dependency) {
296         dependencies.remove(dependency);
297         dependenciesExternalView = null;
298     }
299 
300     
301 
302 
303 
304 
305     @SuppressFBWarnings(justification = "This is the intended external view of the dependencies", value = {"EI_EXPOSE_REP"})
306     public synchronized Dependency[] getDependencies() {
307         if (dependenciesExternalView == null) {
308             dependenciesExternalView = dependencies.toArray(new Dependency[0]);
309         }
310         return dependenciesExternalView;
311     }
312 
313     
314 
315 
316 
317 
318     public synchronized void setDependencies(@NotNull final List<Dependency> dependencies) {
319         this.dependencies.clear();
320         this.dependencies.addAll(dependencies);
321         dependenciesExternalView = null;
322     }
323 
324     
325 
326 
327 
328 
329 
330 
331 
332 
333     public List<Dependency> scan(@NotNull final String[] paths) {
334         return scan(paths, null);
335     }
336 
337     
338 
339 
340 
341 
342 
343 
344 
345 
346 
347 
348     public List<Dependency> scan(@NotNull final String[] paths, @Nullable final String projectReference) {
349         final List<Dependency> deps = new ArrayList<>();
350         for (String path : paths) {
351             final List<Dependency> d = scan(path, projectReference);
352             if (d != null) {
353                 deps.addAll(d);
354             }
355         }
356         return deps;
357     }
358 
359     
360 
361 
362 
363 
364 
365 
366 
367     public List<Dependency> scan(@NotNull final String path) {
368         return scan(path, null);
369     }
370 
371     
372 
373 
374 
375 
376 
377 
378 
379 
380 
381 
382     public List<Dependency> scan(@NotNull final String path, String projectReference) {
383         final File file = new File(path);
384         return scan(file, projectReference);
385     }
386 
387     
388 
389 
390 
391 
392 
393 
394 
395 
396     public List<Dependency> scan(File[] files) {
397         return scan(files, null);
398     }
399 
400     
401 
402 
403 
404 
405 
406 
407 
408 
409 
410 
411     public List<Dependency> scan(File[] files, String projectReference) {
412         final List<Dependency> deps = new ArrayList<>();
413         for (File file : files) {
414             final List<Dependency> d = scan(file, projectReference);
415             if (d != null) {
416                 deps.addAll(d);
417             }
418         }
419         return deps;
420     }
421 
422     
423 
424 
425 
426 
427 
428 
429 
430 
431     public List<Dependency> scan(Collection<File> files) {
432         return scan(files, null);
433     }
434 
435     
436 
437 
438 
439 
440 
441 
442 
443 
444 
445 
446     public List<Dependency> scan(Collection<File> files, String projectReference) {
447         final List<Dependency> deps = new ArrayList<>();
448         files.stream().map((file) -> scan(file, projectReference))
449                 .filter(Objects::nonNull)
450                 .forEach(deps::addAll);
451         return deps;
452     }
453 
454     
455 
456 
457 
458 
459 
460 
461 
462 
463     public List<Dependency> scan(File file) {
464         return scan(file, null);
465     }
466 
467     
468 
469 
470 
471 
472 
473 
474 
475 
476 
477 
478     @Nullable
479     public List<Dependency> scan(@NotNull final File file, String projectReference) {
480         if (file.exists()) {
481             if (file.isDirectory()) {
482                 return scanDirectory(file, projectReference);
483             } else {
484                 final Dependency d = scanFile(file, projectReference);
485                 if (d != null) {
486                     final List<Dependency> deps = new ArrayList<>();
487                     deps.add(d);
488                     return deps;
489                 }
490             }
491         }
492         return null;
493     }
494 
495     
496 
497 
498 
499 
500 
501 
502     protected List<Dependency> scanDirectory(File dir) {
503         return scanDirectory(dir, null);
504     }
505 
506     
507 
508 
509 
510 
511 
512 
513 
514 
515 
516     protected List<Dependency> scanDirectory(@NotNull final File dir, @Nullable final String projectReference) {
517         final File[] files = dir.listFiles();
518         final List<Dependency> deps = new ArrayList<>();
519         if (files != null) {
520             for (File f : files) {
521                 if (f.isDirectory()) {
522                     final List<Dependency> d = scanDirectory(f, projectReference);
523                     if (d != null) {
524                         deps.addAll(d);
525                     }
526                 } else {
527                     final Dependency d = scanFile(f, projectReference);
528                     if (d != null) {
529                         deps.add(d);
530                     }
531                 }
532             }
533         }
534         return deps;
535     }
536 
537     
538 
539 
540 
541 
542 
543 
544     protected Dependency scanFile(@NotNull final File file) {
545         return scanFile(file, null);
546     }
547 
548     
549     
550 
551 
552 
553 
554 
555 
556 
557 
558 
559     protected synchronized Dependency scanFile(@NotNull final File file, @Nullable final String projectReference) {
560         Dependency dependency = null;
561         if (file.isFile()) {
562             if (accept(file)) {
563                 dependency = new Dependency(file);
564                 if (projectReference != null) {
565                     dependency.addProjectReference(projectReference);
566                 }
567                 final String sha1 = dependency.getSha1sum();
568                 boolean found = false;
569 
570                 if (sha1 != null) {
571                     for (Dependency existing : dependencies) {
572                         if (sha1.equals(existing.getSha1sum())) {
573                             if (existing.getDisplayFileName().contains(": ")
574                                     || dependency.getDisplayFileName().contains(": ")
575                                     || dependency.getActualFilePath().contains("dctemp")) {
576                                 continue;
577                             }
578                             found = true;
579                             if (projectReference != null) {
580                                 existing.addProjectReference(projectReference);
581                             }
582                             if (existing.getActualFilePath() != null && dependency.getActualFilePath() != null
583                                     && !existing.getActualFilePath().equals(dependency.getActualFilePath())) {
584 
585                                 if (DependencyBundlingAnalyzer.firstPathIsShortest(existing.getFilePath(), dependency.getFilePath())) {
586                                     DependencyBundlingAnalyzer.mergeDependencies(existing, dependency, null);
587 
588                                     
589                                     return existing;
590                                 } else {
591                                     
592                                     
593                                     found = false;
594                                 }
595 
596                             } else { 
597                                 
598                                 return existing;
599                             }
600                             break;
601                         }
602                     }
603                 }
604                 if (!found) {
605                     dependencies.add(dependency);
606                     dependenciesExternalView = null;
607                 }
608             }
609         } else {
610             LOGGER.debug("Path passed to scanFile(File) is not a file that can be scanned by dependency-check: {}. Skipping the file.", file);
611         }
612         return dependency;
613     }
614     
615 
616     
617 
618 
619 
620 
621 
622 
623 
624 
625 
626 
627 
628 
629 
630 
631     public void analyzeDependencies() throws ExceptionCollection {
632         final List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>());
633 
634         initializeAndUpdateDatabase(exceptions);
635 
636         
637         try {
638             ensureDataExists();
639         } catch (NoDataException ex) {
640             throwFatalExceptionCollection("Unable to continue dependency-check analysis.", ex, exceptions);
641         }
642         LOGGER.info("\n\nDependency-Check is an open source tool performing a best effort analysis of 3rd party dependencies; false positives and "
643                 + "false negatives may exist in the analysis performed by the tool. Use of the tool and the reporting provided constitutes "
644                 + "acceptance for use in an AS IS condition, and there are NO warranties, implied or otherwise, with regard to the analysis "
645                 + "or its use. Any use of the tool and the reporting provided is at the user's risk. In no event shall the copyright holder "
646                 + "or OWASP be held liable for any damages whatsoever arising out of or in connection with the use of this tool, the analysis "
647                 + "performed, or the resulting report.\n\n\n"
648                 + "   About ODC: https://dependency-check.github.io/DependencyCheck/general/internals.html\n"
649                 + "   False Positives: https://dependency-check.github.io/DependencyCheck/general/suppression.html\n"
650                 + "\n");
651         LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------");
652         LOGGER.info("Analysis Started");
653         final long analysisStart = System.currentTimeMillis();
654 
655         
656         for (AnalysisPhase phase : mode.getPhases()) {
657             final List<Analyzer> analyzerList = analyzers.get(phase);
658 
659             for (final Analyzer analyzer : analyzerList) {
660                 final long analyzerStart = System.currentTimeMillis();
661                 try {
662                     initializeAnalyzer(analyzer);
663                 } catch (InitializationException ex) {
664                     exceptions.add(ex);
665                     if (ex.isFatal()) {
666                         continue;
667                     }
668                 }
669 
670                 if (analyzer.isEnabled()) {
671                     executeAnalysisTasks(analyzer, exceptions);
672 
673                     final long analyzerDurationMillis = System.currentTimeMillis() - analyzerStart;
674                     final long analyzerDurationSeconds = TimeUnit.MILLISECONDS.toSeconds(analyzerDurationMillis);
675                     LOGGER.info("Finished {} ({} seconds)", analyzer.getName(), analyzerDurationSeconds);
676                 } else {
677                     LOGGER.debug("Skipping {} (not enabled)", analyzer.getName());
678                 }
679             }
680         }
681         mode.getPhases().stream()
682                 .map(analyzers::get)
683                 .forEach((analyzerList) -> analyzerList.forEach(this::closeAnalyzer));
684 
685         LOGGER.debug("\n----------------------------------------------------\nEND ANALYSIS\n----------------------------------------------------");
686         final long analysisDurationSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - analysisStart);
687         LOGGER.info("Analysis Complete ({} seconds)", analysisDurationSeconds);
688         if (exceptions.size() > 0) {
689             throw new ExceptionCollection(exceptions);
690         }
691     }
692 
693     
694 
695 
696 
697 
698 
699     private void initializeAndUpdateDatabase(@NotNull final List<Throwable> exceptions) throws ExceptionCollection {
700         if (!mode.isDatabaseRequired()) {
701             return;
702         }
703         final boolean autoUpdate;
704         autoUpdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true);
705         if (autoUpdate) {
706             try {
707                 doUpdates(true);
708             } catch (UpdateException ex) {
709                 exceptions.add(ex);
710                 LOGGER.warn("Unable to update 1 or more Cached Web DataSource, using local "
711                         + "data instead. Results may not include recent vulnerabilities.");
712                 LOGGER.debug("Update Error", ex);
713             } catch (DatabaseException ex) {
714                 throwFatalDatabaseException(ex, exceptions);
715             }
716         } else {
717             try {
718                 if (DatabaseManager.isH2Connection(settings) && !DatabaseManager.h2DataFileExists(settings)) {
719                     throw new ExceptionCollection(new NoDataException("Autoupdate is disabled and the database does not exist"), true);
720                 } else {
721                     openDatabase(true, true);
722                 }
723             } catch (IOException ex) {
724                 throw new ExceptionCollection(new DatabaseException("Autoupdate is disabled and unable to connect to the database"), true);
725             } catch (DatabaseException ex) {
726                 throwFatalDatabaseException(ex, exceptions);
727             }
728         }
729     }
730 
731     
732 
733 
734 
735 
736 
737 
738 
739     private void throwFatalDatabaseException(DatabaseException ex, final List<Throwable> exceptions) throws ExceptionCollection {
740         final String msg;
741         if (ex.getMessage().contains("Unable to connect") && DatabaseManager.isH2Connection(settings)) {
742             msg = "Unable to connect to the database - if this error persists it may be "
743                     + "due to a corrupt database. Consider running `purge` to delete the existing database";
744         } else {
745             msg = "Unable to connect to the dependency-check database";
746         }
747         exceptions.add(new DatabaseException(msg, ex));
748         throw new ExceptionCollection(exceptions, true);
749     }
750 
751     
752 
753 
754 
755 
756 
757 
758 
759     protected void executeAnalysisTasks(@NotNull final Analyzer analyzer, List<Throwable> exceptions) throws ExceptionCollection {
760         LOGGER.debug("Starting {}", analyzer.getName());
761         final List<AnalysisTask> analysisTasks = getAnalysisTasks(analyzer, exceptions);
762         final ExecutorService executorService = getExecutorService(analyzer);
763 
764         try {
765             final int timeout = settings.getInt(Settings.KEYS.ANALYSIS_TIMEOUT, 180);
766             final List<Future<Void>> results = executorService.invokeAll(analysisTasks, timeout, TimeUnit.MINUTES);
767 
768             
769             for (Future<Void> result : results) {
770                 try {
771                     result.get();
772                 } catch (ExecutionException e) {
773                     throwFatalExceptionCollection("Analysis task failed with a fatal exception.", e, exceptions);
774                 } catch (CancellationException e) {
775                     throwFatalExceptionCollection("Analysis task was cancelled.", e, exceptions);
776                 }
777             }
778         } catch (InterruptedException e) {
779             Thread.currentThread().interrupt();
780             throwFatalExceptionCollection("Analysis has been interrupted.", e, exceptions);
781         } finally {
782             executorService.shutdown();
783         }
784     }
785 
786     
787 
788 
789 
790 
791 
792 
793     protected synchronized List<AnalysisTask> getAnalysisTasks(Analyzer analyzer, List<Throwable> exceptions) {
794         final List<AnalysisTask> result = new ArrayList<>();
795         dependencies.stream().map((dependency) -> new AnalysisTask(analyzer, dependency, this, exceptions)).forEach(result::add);
796         return result;
797     }
798 
799     
800 
801 
802 
803 
804 
805     protected ExecutorService getExecutorService(Analyzer analyzer) {
806         if (analyzer.supportsParallelProcessing()) {
807             final int maximumNumberOfThreads = Runtime.getRuntime().availableProcessors();
808             LOGGER.debug("Parallel processing with up to {} threads: {}.", maximumNumberOfThreads, analyzer.getName());
809             return Executors.newFixedThreadPool(maximumNumberOfThreads);
810         } else {
811             LOGGER.debug("Parallel processing is not supported: {}.", analyzer.getName());
812             return Executors.newSingleThreadExecutor();
813         }
814     }
815 
816     
817 
818 
819 
820 
821 
822 
823     protected void initializeAnalyzer(@NotNull final Analyzer analyzer) throws InitializationException {
824         try {
825             LOGGER.debug("Initializing {}", analyzer.getName());
826             analyzer.prepare(this);
827         } catch (InitializationException ex) {
828             LOGGER.error("Exception occurred initializing {}.", analyzer.getName());
829             LOGGER.debug("", ex);
830             if (ex.isFatal()) {
831                 try {
832                     analyzer.close();
833                 } catch (Throwable ex1) {
834                     LOGGER.trace("", ex1);
835                 }
836             }
837             throw ex;
838         } catch (Throwable ex) {
839             LOGGER.error("Unexpected exception occurred initializing {}.", analyzer.getName());
840             LOGGER.debug("", ex);
841             try {
842                 analyzer.close();
843             } catch (Throwable ex1) {
844                 LOGGER.trace("", ex1);
845             }
846             throw new InitializationException("Unexpected Exception", ex);
847         }
848     }
849 
850     
851 
852 
853 
854 
855     protected void closeAnalyzer(@NotNull final Analyzer analyzer) {
856         LOGGER.debug("Closing Analyzer '{}'", analyzer.getName());
857         try {
858             analyzer.close();
859         } catch (Throwable ex) {
860             LOGGER.trace("", ex);
861         }
862     }
863 
864     
865 
866 
867 
868 
869 
870 
871 
872 
873     public boolean doUpdates() throws UpdateException, DatabaseException {
874         return doUpdates(false);
875     }
876 
877     
878 
879 
880 
881 
882 
883 
884 
885 
886 
887 
888     public boolean doUpdates(boolean remainOpen) throws UpdateException, DatabaseException {
889         if (mode.isDatabaseRequired()) {
890             try (WriteLock dblock = new WriteLock(getSettings(), DatabaseManager.isH2Connection(getSettings()))) {
891                 
892                 openDatabase(false, false);
893                 LOGGER.info("Checking for updates");
894                 final long updateStart = System.currentTimeMillis();
895                 final UpdateService service = new UpdateService(serviceClassLoader);
896                 final Iterator<CachedWebDataSource> iterator = service.getDataSources();
897                 boolean dbUpdatesMade = false;
898                 UpdateException updateException = null;
899                 while (iterator.hasNext()) {
900                     try {
901                         final CachedWebDataSource source = iterator.next();
902                         dbUpdatesMade |= source.update(this);
903                     } catch (UpdateException ex) {
904                         updateException = ex;
905                         LOGGER.error(ex.getMessage(), ex);
906                     }
907                 }
908                 if (dbUpdatesMade) {
909                     database.defrag();
910                 }
911                 database.close();
912                 database = null;
913                 if (updateException != null) {
914                     throw updateException;
915                 }
916                 LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart);
917                 if (remainOpen) {
918                     
919                     openDatabase(true, false);
920                 }
921 
922                 return dbUpdatesMade;
923             } catch (WriteLockException ex) {
924                 throw new UpdateException("Unable to obtain an exclusive lock on the H2 database to perform updates", ex);
925             }
926         } else {
927             LOGGER.info("Skipping update check in evidence collection mode.");
928             return false;
929         }
930     }
931 
932     
933 
934 
935 
936 
937 
938     public boolean purge() {
939         boolean result = true;
940         final UpdateService service = new UpdateService(serviceClassLoader);
941         final Iterator<CachedWebDataSource> iterator = service.getDataSources();
942         while (iterator.hasNext()) {
943             result &= iterator.next().purge(this);
944         }
945         try {
946             final File cache = new File(settings.getDataDirectory(), "cache");
947             if (cache.exists()) {
948                 if (FileUtils.delete(cache)) {
949                     LOGGER.info("Cache directory purged");
950                 }
951             }
952         } catch (IOException ex) {
953             throw new RuntimeException(ex);
954         }
955         try {
956             final File cache = new File(settings.getDataDirectory(), "oss_cache");
957             if (cache.exists()) {
958                 if (FileUtils.delete(cache)) {
959                     LOGGER.info("OSS Cache directory purged");
960                 }
961             }
962         } catch (IOException ex) {
963             throw new RuntimeException(ex);
964         }
965 
966         return result;
967     }
968 
969     
970 
971 
972 
973 
974 
975 
976 
977 
978 
979     public void openDatabase() throws DatabaseException {
980         openDatabase(false, true);
981     }
982 
983     
984 
985 
986 
987 
988 
989 
990 
991 
992 
993 
994 
995 
996 
997     @SuppressWarnings("try")
998     public void openDatabase(boolean readOnly, boolean lockRequired) throws DatabaseException {
999         if (mode.isDatabaseRequired() && database == null) {
1000             try (WriteLock dblock = new WriteLock(getSettings(), lockRequired && DatabaseManager.isH2Connection(settings))) {
1001                 if (readOnly
1002                         && DatabaseManager.isH2Connection(settings)
1003                         && settings.getString(Settings.KEYS.DB_CONNECTION_STRING).contains("file:%s")) {
1004                     final File db = DatabaseManager.getH2DataFile(settings);
1005                     if (db.isFile()) {
1006                         final File temp = settings.getTempDirectory();
1007                         final File tempDB = new File(temp, db.getName());
1008                         LOGGER.debug("copying database {} to {}", db.toPath(), temp.toPath());
1009                         Files.copy(db.toPath(), tempDB.toPath());
1010                         settings.setString(Settings.KEYS.H2_DATA_DIRECTORY, temp.getPath());
1011                         final String connStr = settings.getString(Settings.KEYS.DB_CONNECTION_STRING);
1012                         if (!connStr.contains("ACCESS_MODE_DATA")) {
1013                             settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connStr + "ACCESS_MODE_DATA=r");
1014                         }
1015                         settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false);
1016                         database = new CveDB(settings);
1017                     } else {
1018                         throw new DatabaseException("Unable to open database - configured database file does not exist: " + db);
1019                     }
1020                 } else {
1021                     database = new CveDB(settings);
1022                 }
1023             } catch (IOException ex) {
1024                 throw new DatabaseException("Unable to open database in read only mode", ex);
1025             } catch (WriteLockException ex) {
1026                 throw new DatabaseException("Failed to obtain lock - unable to open database", ex);
1027             }
1028             database.open();
1029         }
1030     }
1031 
1032     
1033 
1034 
1035 
1036 
1037     public CveDB getDatabase() {
1038         return this.database;
1039     }
1040 
1041     
1042 
1043 
1044 
1045 
1046 
1047     @NotNull
1048     public List<Analyzer> getAnalyzers() {
1049         final List<Analyzer> analyzerList = new ArrayList<>();
1050         
1051         mode.getPhases().stream()
1052                 .map(analyzers::get)
1053                 .forEachOrdered(analyzerList::addAll);
1054         return analyzerList;
1055     }
1056 
1057     
1058 
1059 
1060 
1061 
1062 
1063 
1064     @Override
1065     public boolean accept(@Nullable final File file) {
1066         if (file == null) {
1067             return false;
1068         }
1069         
1070 
1071         return this.fileTypeAnalyzers.stream().map((a) -> a.accept(file)).reduce(false, (accumulator, result) -> accumulator || result);
1072     }
1073 
1074     
1075 
1076 
1077 
1078 
1079     public Set<FileTypeAnalyzer> getFileTypeAnalyzers() {
1080         return this.fileTypeAnalyzers;
1081     }
1082 
1083     
1084 
1085 
1086 
1087 
1088     public Settings getSettings() {
1089         return settings;
1090     }
1091 
1092     
1093 
1094 
1095 
1096 
1097 
1098     public Object getObject(String key) {
1099         return objects.get(key);
1100     }
1101 
1102     
1103 
1104 
1105 
1106 
1107 
1108     public void putObject(String key, Object object) {
1109         objects.put(key, object);
1110     }
1111 
1112     
1113 
1114 
1115 
1116 
1117 
1118 
1119     public boolean hasObject(String key) {
1120         return objects.containsKey(key);
1121     }
1122 
1123     
1124 
1125 
1126 
1127 
1128     public void removeObject(String key) {
1129         objects.remove(key);
1130     }
1131 
1132     
1133 
1134 
1135 
1136 
1137     public Mode getMode() {
1138         return mode;
1139     }
1140 
1141     
1142 
1143 
1144 
1145 
1146 
1147     protected void addFileTypeAnalyzer(@NotNull final FileTypeAnalyzer fta) {
1148         this.fileTypeAnalyzers.add(fta);
1149     }
1150 
1151     
1152 
1153 
1154 
1155 
1156 
1157     private void ensureDataExists() throws NoDataException {
1158         if (mode.isDatabaseRequired() && (database == null || !database.dataExists())) {
1159             throw new NoDataException("No documents exist");
1160         }
1161     }
1162 
1163     
1164 
1165 
1166 
1167 
1168 
1169 
1170 
1171 
1172     private void throwFatalExceptionCollection(String message, @NotNull final Throwable throwable,
1173             @NotNull final List<Throwable> exceptions) throws ExceptionCollection {
1174         LOGGER.error(message);
1175         LOGGER.debug("", throwable);
1176         exceptions.add(throwable);
1177         throw new ExceptionCollection(exceptions, true);
1178     }
1179 
1180     
1181 
1182 
1183 
1184 
1185 
1186 
1187 
1188 
1189 
1190 
1191     @Deprecated
1192     public void writeReports(String applicationName, File outputDir, String format) throws ReportException {
1193         writeReports(applicationName, null, null, null, outputDir, format, null);
1194     }
1195 
1196     
1197     
1198 
1199 
1200 
1201 
1202 
1203 
1204 
1205 
1206 
1207 
1208     public void writeReports(String applicationName, File outputDir, String format, ExceptionCollection exceptions) throws ReportException {
1209         writeReports(applicationName, null, null, null, outputDir, format, exceptions);
1210     }
1211     
1212 
1213     
1214 
1215 
1216 
1217 
1218 
1219 
1220 
1221 
1222 
1223 
1224 
1225 
1226 
1227     @Deprecated
1228     public synchronized void writeReports(String applicationName, @Nullable final String groupId,
1229             @Nullable final String artifactId, @Nullable final String version,
1230             @NotNull final File outputDir, String format) throws ReportException {
1231         writeReports(applicationName, groupId, artifactId, version, outputDir, format, null);
1232     }
1233 
1234     
1235     
1236 
1237 
1238 
1239 
1240 
1241 
1242 
1243 
1244 
1245 
1246 
1247 
1248 
1249     public synchronized void writeReports(String applicationName, @Nullable final String groupId,
1250             @Nullable final String artifactId, @Nullable final String version,
1251             @NotNull final File outputDir, String format, ExceptionCollection exceptions) throws ReportException {
1252         if (mode == Mode.EVIDENCE_COLLECTION) {
1253             throw new UnsupportedOperationException("Cannot generate report in evidence collection mode.");
1254         }
1255         final DatabaseProperties prop = database.getDatabaseProperties();
1256 
1257         final ReportGenerator r = new ReportGenerator(applicationName, groupId, artifactId, version,
1258                 dependencies, getAnalyzers(), prop, settings, exceptions);
1259         try {
1260             r.write(outputDir.getAbsolutePath(), format);
1261         } catch (ReportException ex) {
1262             final String msg = String.format("Error generating the report for %s", applicationName);
1263             LOGGER.debug(msg, ex);
1264             throw new ReportException(msg, ex);
1265         }
1266     }
1267     
1268 
1269     private boolean identifiersMatch(Set<Identifier> left, Set<Identifier> right) {
1270         if (left != null && right != null && left.size() > 0 && left.size() == right.size()) {
1271             int count = 0;
1272             for (Identifier l : left) {
1273                 for (Identifier r : right) {
1274                     if (l.getValue().equals(r.getValue())) {
1275                         count += 1;
1276                         break;
1277                     }
1278                 }
1279             }
1280             return count == left.size();
1281         }
1282         return false;
1283     }
1284 
1285     
1286 
1287 
1288     public enum Mode {
1289         
1290 
1291 
1292 
1293         EVIDENCE_COLLECTION(
1294                 false,
1295                 INITIAL,
1296                 PRE_INFORMATION_COLLECTION,
1297                 INFORMATION_COLLECTION,
1298                 INFORMATION_COLLECTION2,
1299                 POST_INFORMATION_COLLECTION1,
1300                 POST_INFORMATION_COLLECTION2,
1301                 POST_INFORMATION_COLLECTION3
1302         ),
1303         
1304 
1305 
1306 
1307 
1308 
1309         EVIDENCE_PROCESSING(
1310                 true,
1311                 PRE_IDENTIFIER_ANALYSIS,
1312                 IDENTIFIER_ANALYSIS,
1313                 POST_IDENTIFIER_ANALYSIS,
1314                 PRE_FINDING_ANALYSIS,
1315                 FINDING_ANALYSIS,
1316                 POST_FINDING_ANALYSIS,
1317                 FINDING_ANALYSIS_PHASE2,
1318                 FINAL
1319         ),
1320         
1321 
1322 
1323 
1324         STANDALONE(true, AnalysisPhase.values());
1325 
1326         
1327 
1328 
1329         private final boolean databaseRequired;
1330         
1331 
1332 
1333         private final List<AnalysisPhase> phases;
1334 
1335         
1336 
1337 
1338 
1339 
1340 
1341         Mode(boolean databaseRequired, AnalysisPhase... phases) {
1342             this.databaseRequired = databaseRequired;
1343             this.phases = Collections.unmodifiableList(Arrays.asList(phases));
1344         }
1345 
1346         
1347 
1348 
1349 
1350 
1351         private boolean isDatabaseRequired() {
1352             return databaseRequired;
1353         }
1354 
1355         
1356 
1357 
1358 
1359 
1360         public List<AnalysisPhase> getPhases() {
1361             return phases;
1362         }
1363     }
1364 }