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 }