diff --git a/.travis.yml b/.travis.yml index 7ca7bad83..460dd183f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,12 +29,12 @@ matrix: env: - JDK="JDK7" script: - - if [ ! -z "$TRAVIS_TAG" ]; then travis_wait 20 mvn install site site:stage -DreleaseTesting; else travis_wait 15 mvn install -DreleaseTesting; fi + - if [ ! -z "$TRAVIS_TAG" ]; then travis_wait 35 mvn install site site:stage -DreleaseTesting; else travis_wait 35 mvn install -DreleaseTesting; fi - jdk: oraclejdk8 env: - JDK="JDK8" script: - - travis_wait 15 mvn install -DreleaseTesting + - travis_wait 35 mvn install -DreleaseTesting after_success: - if [ "$JDK" == "JDK8" ]; then @@ -43,6 +43,21 @@ after_success: ./coverity_scan.sh; fi; +after_failure: + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/617-hierarchical-cross-deps/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/618-aggregator-purge/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/618-aggregator-update-only/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/629-jackson-dataformat/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/690-threadsafety/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/710-pom-parse-error/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/729-system-scope-resolved/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/729-system-scope-skipped/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/730-multiple-suppression-files/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/730-multiple-suppression-files-configs/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/815-broken-suppression-aggregate/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/846-site-plugin/build.log + - cat /home/travis/build/jeremylong/DependencyCheck/dependency-check-maven/target/it/false-positives/build.log + deploy: - provider: script script: mvn --settings .travis.settings.xml source:jar javadoc:jar package deploy -DskipTests=true -X diff --git a/build-reporting/pom.xml b/build-reporting/pom.xml index d994bb88a..7a564a9ba 100644 --- a/build-reporting/pom.xml +++ b/build-reporting/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2017 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT Dependency-Check Build-Reporting build-reporting diff --git a/dependency-check-ant/pom.xml b/dependency-check-ant/pom.xml index 89e63a04f..bf5842cc7 100644 --- a/dependency-check-ant/pom.xml +++ b/dependency-check-ant/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2013 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT dependency-check-ant diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java index 5f6ccc06d..294df935f 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Check.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.taskdefs; import java.io.File; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -46,6 +47,7 @@ import org.slf4j.impl.StaticLoggerBinder; * * @author Jeremy Long */ +@NotThreadSafe public class Check extends Update { /** @@ -161,6 +163,7 @@ public class Check extends Update { /** * Suppression file paths. */ + @SuppressWarnings("CanBeFinal") private List suppressionFiles = new ArrayList<>(); /** @@ -948,7 +951,7 @@ public class Check extends Update { dealWithReferences(); validateConfiguration(); populateSettings(); - try (Engine engine = new Engine(Check.class.getClassLoader())) { + try (Engine engine = new Engine(Check.class.getClassLoader(), getSettings())) { if (isUpdateOnly()) { log("Deprecated 'UpdateOnly' property set; please use the UpdateTask instead", Project.MSG_WARN); try { @@ -998,8 +1001,6 @@ public class Check extends Update { throw new BuildException(msg, ex); } log(msg, ex, Project.MSG_ERR); - } finally { - Settings.cleanup(true); } } @@ -1028,33 +1029,33 @@ public class Check extends Update { @Override protected void populateSettings() throws BuildException { super.populateSettings(); - Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate); - Settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFiles.toArray(new String[suppressionFiles.size()])); - Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, rubygemsAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, opensslAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CMAKE_ENABLED, cmakeAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED, swiftPackageManagerAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED, cocoapodsAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, bundleAuditAnalyzerEnabled); - Settings.setStringIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, bundleAuditPath); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, autoconfAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED, nspAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); - Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); + getSettings().setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate); + getSettings().setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFiles.toArray(new String[suppressionFiles.size()])); + getSettings().setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, rubygemsAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, opensslAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_CMAKE_ENABLED, cmakeAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED, swiftPackageManagerAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED, cocoapodsAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, bundleAuditAnalyzerEnabled); + getSettings().setStringIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, bundleAuditPath); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, autoconfAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED, nspAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled); + getSettings().setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); + getSettings().setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); + getSettings().setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions); + getSettings().setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); } /** @@ -1065,7 +1066,7 @@ public class Check extends Update { * @throws BuildException thrown if a CVSS score is found that is higher * than the threshold set */ - private void checkForFailure(List dependencies) throws BuildException { + private void checkForFailure(Dependency[] dependencies) throws BuildException { final StringBuilder ids = new StringBuilder(); for (Dependency d : dependencies) { for (Vulnerability v : d.getVulnerabilities()) { @@ -1092,7 +1093,7 @@ public class Check extends Update { * * @param dependencies a list of dependency objects */ - private void showSummary(List dependencies) { + private void showSummary(Dependency[] dependencies) { final StringBuilder summary = new StringBuilder(); for (Dependency d : dependencies) { boolean firstEntry = true; diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java index 23f520f35..30bdf8775 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Purge.java @@ -37,21 +37,35 @@ public class Purge extends Task { * The properties file location. */ private static final String PROPERTIES_FILE = "task.properties"; + /** + * The configured settings. + */ + private Settings settings; + + /** + * The location of the data directory that contains + */ + private String dataDirectory = null; + /** + * Indicates if dependency-check should fail the build if an exception + * occurs. + */ + private boolean failOnError = true; /** * Construct a new DependencyCheckTask. */ public Purge() { super(); + // Call this before Dependency Check Core starts logging anything - this way, all SLF4J messages from // core end up coming through this tasks logger StaticLoggerBinder.getSingleton().setTask(this); } - /** - * The location of the data directory that contains - */ - private String dataDirectory = null; + public Settings getSettings() { + return settings; + } /** * Get the value of dataDirectory. @@ -71,12 +85,6 @@ public class Purge extends Task { this.dataDirectory = dataDirectory; } - /** - * Indicates if dependency-check should fail the build if an exception - * occurs. - */ - private boolean failOnError = true; - /** * Get the value of failOnError. * @@ -106,7 +114,7 @@ public class Purge extends Task { populateSettings(); File db; try { - db = new File(Settings.getDataDirectory(), "dc.h2.db"); + db = new File(settings.getDataDirectory(), "dc.h2.db"); if (db.exists()) { if (db.delete()) { log("Database file purged; local copy of the NVD has been removed", Project.MSG_INFO); @@ -131,7 +139,7 @@ public class Purge extends Task { } log(msg, Project.MSG_ERR); } finally { - Settings.cleanup(true); + settings.cleanup(true); } } @@ -143,9 +151,9 @@ public class Purge extends Task { * @throws BuildException thrown if the properties file cannot be read. */ protected void populateSettings() throws BuildException { - Settings.initialize(); + settings = new Settings(); try (InputStream taskProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE)) { - Settings.mergeProperties(taskProperties); + settings.mergeProperties(taskProperties); } catch (IOException ex) { final String msg = "Unable to load the dependency-check ant task.properties file."; if (this.failOnError) { @@ -154,13 +162,13 @@ public class Purge extends Task { log(msg, ex, Project.MSG_WARN); } if (dataDirectory != null) { - Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); + settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); } else { final File jarPath = new File(Purge.class.getProtectionDomain().getCodeSource().getLocation().getPath()); final File base = jarPath.getParentFile(); - final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY); + final String sub = settings.getString(Settings.KEYS.DATA_DIRECTORY); final File dataDir = new File(base, sub); - Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); + settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); } } } diff --git a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java index a422962f2..a2e8fad7a 100644 --- a/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java +++ b/dependency-check-ant/src/main/java/org/owasp/dependencycheck/taskdefs/Update.java @@ -385,7 +385,7 @@ public class Update extends Purge { @Override public void execute() throws BuildException { populateSettings(); - try (Engine engine = new Engine(Update.class.getClassLoader())) { + try (Engine engine = new Engine(Update.class.getClassLoader(), getSettings())) { try { engine.doUpdates(); } catch (UpdateException ex) { @@ -400,8 +400,6 @@ public class Update extends Purge { throw new BuildException(msg, ex); } log(msg, Project.MSG_ERR); - } finally { - Settings.cleanup(true); } } @@ -415,23 +413,23 @@ public class Update extends Purge { @Override protected void populateSettings() throws BuildException { super.populateSettings(); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUsername); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPassword); - Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base); + getSettings().setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); + getSettings().setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); + getSettings().setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUsername); + getSettings().setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPassword); + getSettings().setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); + getSettings().setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); + getSettings().setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); + getSettings().setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); + getSettings().setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); + getSettings().setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); + getSettings().setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified); + getSettings().setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified); + getSettings().setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base); + getSettings().setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base); if (cveValidForHours != null) { if (cveValidForHours >= 0) { - Settings.setInt(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); + getSettings().setInt(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); } else { throw new BuildException("Invalid setting: `cpeValidForHours` must be 0 or greater"); } diff --git a/dependency-check-ant/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/dependency-check-ant/src/main/java/org/slf4j/impl/StaticLoggerBinder.java index 70423496b..e718950dd 100644 --- a/dependency-check-ant/src/main/java/org/slf4j/impl/StaticLoggerBinder.java +++ b/dependency-check-ant/src/main/java/org/slf4j/impl/StaticLoggerBinder.java @@ -48,7 +48,7 @@ public class StaticLoggerBinder implements LoggerFactoryBinder { * * @return the StaticLoggerBinder singleton */ - public static final StaticLoggerBinder getSingleton() { + public static StaticLoggerBinder getSingleton() { return SINGLETON; } diff --git a/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java b/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java index 657b284b2..96d6477e5 100644 --- a/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java +++ b/dependency-check-ant/src/test/java/org/owasp/dependencycheck/taskdefs/DependencyCheckTaskTest.java @@ -21,13 +21,12 @@ import java.io.File; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildFileRule; -import org.junit.After; +import org.apache.tools.ant.types.LogLevel; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.owasp.dependencycheck.BaseDBTestCase; -import org.owasp.dependencycheck.utils.Settings; import static org.junit.Assert.assertTrue; @@ -35,7 +34,7 @@ import static org.junit.Assert.assertTrue; * * @author Jeremy Long */ -public class DependencyCheckTaskTest { +public class DependencyCheckTaskTest extends BaseDBTestCase { @Rule public BuildFileRule buildFileRule = new BuildFileRule(); @@ -44,18 +43,11 @@ public class DependencyCheckTaskTest { public ExpectedException expectedException = ExpectedException.none(); @Before + @Override public void setUp() throws Exception { - Settings.initialize(); - BaseDBTestCase.ensureDBExists(); + super.setUp(); final String buildFile = this.getClass().getClassLoader().getResource("build.xml").getPath(); - buildFileRule.configureProject(buildFile); - } - - @After - public void tearDown() { - //no cleanup... - //executeTarget("cleanup"); - Settings.cleanup(true); + buildFileRule.configureProject(buildFile, LogLevel.VERBOSE.getLevel()); } /** @@ -125,6 +117,18 @@ public class DependencyCheckTaskTest { // WHEN executing the ant task buildFileRule.executeTarget(antTaskName); + System.out.println("----------------------------------------------------------"); + System.out.println("----------------------------------------------------------"); + System.out.println("----------------------------------------------------------"); + System.out.println("----------------------------------------------------------"); + System.out.println(buildFileRule.getError()); + System.out.println("----------------------------------------------------------"); + System.out.println("----------------------------------------------------------"); + System.out.println(buildFileRule.getFullLog()); + System.out.println("----------------------------------------------------------"); + System.out.println("----------------------------------------------------------"); + System.out.println("----------------------------------------------------------"); + System.out.println("----------------------------------------------------------"); // THEN the ant task executed without error final File report = new File("target/suppression-report.html"); diff --git a/dependency-check-cli/pom.xml b/dependency-check-cli/pom.xml index a503c2de1..f91139b49 100644 --- a/dependency-check-cli/pom.xml +++ b/dependency-check-cli/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT dependency-check-cli diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java index 39b564bab..b887d25be 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/App.java @@ -53,6 +53,10 @@ public class App { * The logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** + * The configured settings. + */ + private Settings settings = null; /** * The main method for the application. @@ -61,17 +65,28 @@ public class App { */ public static void main(String[] args) { int exitCode = 0; - try { - Settings.initialize(); - final App app = new App(); - exitCode = app.run(args); - LOGGER.debug("Exit code: {}", exitCode); - } finally { - Settings.cleanup(true); - } + final App app = new App(); + exitCode = app.run(args); + LOGGER.debug("Exit code: {}", exitCode); System.exit(exitCode); } + /** + * Builds the App object. + */ + public App() { + settings = new Settings(); + } + + /** + * Builds the App object; this method is used for testing. + * + * @param settings the configured settings + */ + protected App(Settings settings) { + this.settings = settings; + } + /** * Main CLI entry-point into the application. * @@ -80,7 +95,7 @@ public class App { */ public int run(String[] args) { int exitCode = 0; - final CliParser cli = new CliParser(); + final CliParser cli = new CliParser(settings); try { cli.parse(args); @@ -109,10 +124,11 @@ public class App { LOGGER.error(ex.getMessage()); LOGGER.debug("Error loading properties file", ex); exitCode = -4; + return exitCode; } File db; try { - db = new File(Settings.getDataDirectory(), Settings.getString(Settings.KEYS.DB_FILE_NAME, "dc.h2.db")); + db = new File(settings.getDataDirectory(), settings.getString(Settings.KEYS.DB_FILE_NAME, "dc.h2.db")); if (db.exists()) { if (db.delete()) { LOGGER.info("Database file purged; local copy of the NVD has been removed"); @@ -127,6 +143,8 @@ public class App { } catch (IOException ex) { LOGGER.error("Unable to delete the database"); exitCode = -7; + } finally { + settings.cleanup(); } } } else if (cli.isGetVersion()) { @@ -138,6 +156,7 @@ public class App { LOGGER.error(ex.getMessage()); LOGGER.debug("Error loading properties file", ex); exitCode = -4; + return exitCode; } try { runUpdateOnly(); @@ -147,6 +166,8 @@ public class App { } catch (DatabaseException ex) { LOGGER.error(ex.getMessage()); exitCode = -9; + } finally { + settings.cleanup(); } } else if (cli.isRunScan()) { try { @@ -155,6 +176,7 @@ public class App { LOGGER.error(ex.getMessage()); LOGGER.debug("Error loading properties file", ex); exitCode = -4; + return exitCode; } try { final String[] scanFiles = cli.getScanFiles(); @@ -183,6 +205,8 @@ public class App { for (Throwable e : ex.getExceptions()) { LOGGER.error(e.getMessage()); } + } finally { + settings.cleanup(); } } else { cli.printHelp(); @@ -221,7 +245,7 @@ public class App { final List antStylePaths = getPaths(files); final Set paths = scanAntStylePaths(antStylePaths, symLinkDepth, excludes); - engine = new Engine(); + engine = new Engine(settings); engine.scan(paths); ExceptionCollection exCol = null; @@ -250,7 +274,7 @@ public class App { return determineReturnCode(engine, cvssFailScore); } finally { if (engine != null) { - engine.cleanup(); + engine.close(); } } } @@ -288,7 +312,6 @@ public class App { * @param excludes an array of ant style excludes * @return returns the set of identified files * @throws InvalidScanPathException thrown when the scan path is invalid - * @throws IllegalStateException */ private Set scanAntStylePaths(List antStylePaths, int symLinkDepth, String[] excludes) throws InvalidScanPathException { @@ -359,7 +382,7 @@ public class App { * connection to the database could not be established */ private void runUpdateOnly() throws UpdateException, DatabaseException { - try (Engine engine = new Engine()) { + try (Engine engine = new Engine(settings)) { engine.doUpdates(); } } @@ -401,7 +424,7 @@ public class App { if (propertiesFile != null) { try { - Settings.mergeProperties(propertiesFile); + settings.mergeProperties(propertiesFile); } catch (FileNotFoundException ex) { throw new InvalidSettingException("Unable to find properties file '" + propertiesFile.getPath() + "'", ex); } catch (IOException ex) { @@ -413,65 +436,65 @@ public class App { // on the command line. This is true of other boolean values set below not using the setBooleanIfNotNull. final boolean nexusUsesProxy = cli.isNexusUsesProxy(); if (dataDirectory != null) { - Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); + settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); } else if (System.getProperty("basedir") != null) { final File dataDir = new File(System.getProperty("basedir"), "data"); - Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); + settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); } else { final File jarPath = new File(App.class.getProtectionDomain().getCodeSource().getLocation().getPath()); final File base = jarPath.getParentFile(); - final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY); + final String sub = settings.getString(Settings.KEYS.DATA_DIRECTORY); final File dataDir = new File(base, sub); - Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); + settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); } - Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPass); - Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); - Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile); - Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); + settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPass); + settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); + settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile); + settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); - Settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFiles); + settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFiles); //File Type Analyzer Settings - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, experimentalEnabled); - Settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !cli.isPythonPackageDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !cli.isAutoconfDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED, !cli.isCmakeDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !cli.isNuspecDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !cli.isAssemblyDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, !cli.isBundleAuditDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, !cli.isOpenSSLDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, !cli.isComposerDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, !cli.isNodeJsDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED, !cli.isNspDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED, !cli.isSwiftPackageAnalyzerDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED, !cli.isCocoapodsAnalyzerDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, !cli.isRubyGemspecDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !cli.isCentralDisabled()); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !cli.isNexusDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_JAR_ENABLED, !cli.isJarDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, !cli.isPythonPackageDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, !cli.isAutoconfDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_CMAKE_ENABLED, !cli.isCmakeDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, !cli.isNuspecDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, !cli.isAssemblyDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, !cli.isBundleAuditDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, !cli.isOpenSSLDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, !cli.isComposerDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, !cli.isNodeJsDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED, !cli.isNspDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED, !cli.isSwiftPackageAnalyzerDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED, !cli.isCocoapodsAnalyzerDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, !cli.isRubyGemspecDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, !cli.isCentralDisabled()); + settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, !cli.isNexusDisabled()); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, cli.getPathToBundleAudit()); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); - Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, additionalZipExtensions); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, cli.getPathToBundleAudit()); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); + settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); + settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); + settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); + settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); + settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); + settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); + settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, additionalZipExtensions); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); if (cveBase12 != null && !cveBase12.isEmpty()) { - Settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveBase12); - Settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveBase20); - Settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, cveMod12); - Settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, cveMod20); + settings.setString(Settings.KEYS.CVE_SCHEMA_1_2, cveBase12); + settings.setString(Settings.KEYS.CVE_SCHEMA_2_0, cveBase20); + settings.setString(Settings.KEYS.CVE_MODIFIED_12_URL, cveMod12); + settings.setString(Settings.KEYS.CVE_MODIFIED_20_URL, cveMod20); } } diff --git a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java index 080826b12..4fd56bad3 100644 --- a/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java +++ b/dependency-check-cli/src/main/java/org/owasp/dependencycheck/CliParser.java @@ -53,6 +53,19 @@ public final class CliParser { * Indicates whether the arguments are valid. */ private boolean isValid = true; + /** + * The configured settings. + */ + private final Settings settings; + + /** + * Constructs a new CLI Parser object with the configured settings. + * + * @param settings the configured settings + */ + public CliParser(Settings settings) { + this.settings = settings; + } /** * Parses the arguments passed in and captures the results for later use. @@ -582,7 +595,7 @@ public final class CliParser { private boolean hasDisableOption(String argument, String setting) { if (line == null || !line.hasOption(argument)) { try { - return !Settings.getBoolean(setting); + return !settings.getBoolean(setting); } catch (InvalidSettingException ise) { LOGGER.warn("Invalid property setting '{}' defaulting to false", setting); return false; @@ -801,7 +814,7 @@ public final class CliParser { // still honor the property if it's set. if (line == null || !line.hasOption(ARGUMENT.NEXUS_USES_PROXY)) { try { - return Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY); + return settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY); } catch (InvalidSettingException ise) { return true; } @@ -823,10 +836,10 @@ public final class CliParser { final String helpMsg = String.format("%n%s" + " can be used to identify if there are any known CVE vulnerabilities in libraries utilized by an application. " + "%s will automatically update required data from the Internet, such as the CVE and CPE data files from nvd.nist.gov.%n%n", - Settings.getString("application.name", "DependencyCheck"), - Settings.getString("application.name", "DependencyCheck")); + settings.getString("application.name", "DependencyCheck"), + settings.getString("application.name", "DependencyCheck")); - formatter.printHelp(Settings.getString("application.name", "DependencyCheck"), + formatter.printHelp(settings.getString("application.name", "DependencyCheck"), helpMsg, options, "", @@ -1054,8 +1067,8 @@ public final class CliParser { */ public void printVersionInfo() { final String version = String.format("%s version %s", - Settings.getString(Settings.KEYS.APPLICATION_NAME, "dependency-check"), - Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown")); + settings.getString(Settings.KEYS.APPLICATION_NAME, "dependency-check"), + settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown")); System.out.println(version); } diff --git a/dependency-check-cli/src/test/java/org/owasp/dependencycheck/AppTest.java b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/AppTest.java index 4a2346a31..4aebdefed 100644 --- a/dependency-check-cli/src/test/java/org/owasp/dependencycheck/AppTest.java +++ b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/AppTest.java @@ -30,8 +30,6 @@ import java.util.Map; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.UnrecognizedOptionException; -import org.junit.After; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -42,7 +40,7 @@ import org.owasp.dependencycheck.utils.Settings.KEYS; /** * Tests for the {@link AppTest} class. */ -public class AppTest { +public class AppTest extends BaseTest { /** * Test rule for asserting exceptions and their contents. @@ -50,29 +48,13 @@ public class AppTest { @Rule public ExpectedException expectedException = ExpectedException.none(); - /** - * Initialize the {@link Settings} singleton. - */ - @Before - public void setUp() { - Settings.initialize(); - } - - /** - * Clean the {@link Settings} singleton. - */ - @After - public void tearDown() { - Settings.cleanup(); - } - /** * Test of ensureCanonicalPath method, of class App. */ @Test public void testEnsureCanonicalPath() { String file = "../*.jar"; - App instance = new App(); + App instance = new App(getSettings()); String result = instance.ensureCanonicalPath(file); assertFalse(result.contains("..")); assertTrue(result.endsWith("*.jar")); @@ -85,7 +67,7 @@ public class AppTest { /** * Assert that boolean properties can be set on the CLI and parsed into the - * {@link Settings} singleton. + * {@link Settings}. * * @throws Exception the unexpected {@link Exception}. */ @@ -165,13 +147,13 @@ public class AppTest { String[] args = {"-P", prop.getAbsolutePath(), "--suppression", "another-file.xml"}; // WHEN parsing the CLI arguments - final CliParser cli = new CliParser(); + final CliParser cli = new CliParser(getSettings()); cli.parse(args); - final App classUnderTest = new App(); + final App classUnderTest = new App(getSettings()); classUnderTest.populateSettings(cli); - // THEN the suppression file is set in the settings singleton for use in the application core - assertThat("Expected the suppression file to be set in the Settings singleton", Settings.getString(KEYS.SUPPRESSION_FILE), is("another-file.xml")); + // THEN the suppression file is set in the settings for use in the application core + assertThat("Expected the suppression file to be set in the Settings", getSettings().getString(KEYS.SUPPRESSION_FILE), is("another-file.xml")); } /** @@ -188,31 +170,25 @@ public class AppTest { String[] args = {"-P", prop.getAbsolutePath(), "--suppression", "first-file.xml", "another-file.xml"}; // WHEN parsing the CLI arguments - final CliParser cli = new CliParser(); + final CliParser cli = new CliParser(getSettings()); cli.parse(args); - final App classUnderTest = new App(); + final App classUnderTest = new App(getSettings()); classUnderTest.populateSettings(cli); - // THEN the suppression file is set in the settings singleton for use in the application core - assertThat("Expected the suppression files to be set in the Settings singleton with a separator", Settings.getString(KEYS.SUPPRESSION_FILE), is("first-file.xml,another-file.xml")); + // THEN the suppression file is set in the settings for use in the application core + assertThat("Expected the suppression files to be set in the Settings with a separator", getSettings().getString(KEYS.SUPPRESSION_FILE), is("first-file.xml,another-file.xml")); } private boolean testBooleanProperties(String[] args, Map expected) throws URISyntaxException, FileNotFoundException, ParseException, InvalidSettingException { - Settings.initialize(); - try { - final CliParser cli = new CliParser(); - cli.parse(args); - App instance = new App(); - instance.populateSettings(cli); - boolean results = true; - for (Map.Entry entry : expected.entrySet()) { - results &= Settings.getBoolean(entry.getKey()) == entry.getValue(); - } - - return results; - } finally { - Settings.cleanup(); + this.reloadSettings(); + final CliParser cli = new CliParser(getSettings()); + cli.parse(args); + App instance = new App(getSettings()); + instance.populateSettings(cli); + boolean results = true; + for (Map.Entry entry : expected.entrySet()) { + results &= getSettings().getBoolean(entry.getKey()) == entry.getValue(); } + return results; } - } diff --git a/dependency-check-cli/src/test/java/org/owasp/dependencycheck/BaseTest.java b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/BaseTest.java new file mode 100644 index 000000000..cf8dec880 --- /dev/null +++ b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/BaseTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014 OWASP. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.owasp.dependencycheck; + +import org.junit.After; +import org.junit.Before; +import org.owasp.dependencycheck.utils.Settings; + +/** + * + * @author Jeremy Long + */ +public class BaseTest { + + /** + * The configured settings. + */ + private Settings settings; + + /** + * Initialize the {@link Settings}. + */ + @Before + public void setUp() { + settings = new Settings(); + } + + /** + * Clean the {@link Settings}. + */ + @After + public void tearDown() { + settings.cleanup(true); + } + + /** + * Returns the settings for the test cases. + * + * @return + */ + protected Settings getSettings() { + return settings; + } + + protected void reloadSettings() { + tearDown(); + setUp(); + } +} diff --git a/dependency-check-cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java index c564d7c79..30c43992a 100644 --- a/dependency-check-cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java +++ b/dependency-check-cli/src/test/java/org/owasp/dependencycheck/CliParserTest.java @@ -33,17 +33,7 @@ import org.owasp.dependencycheck.utils.Settings; * * @author Jeremy Long */ -public class CliParserTest { - - @BeforeClass - public static void setUpClass() throws Exception { - Settings.initialize(); - } - - @AfterClass - public static void tearDownClass() throws Exception { - Settings.cleanup(true); - } +public class CliParserTest extends BaseTest { /** * Test of parse method, of class CliParser. @@ -59,7 +49,7 @@ public class CliParserTest { ByteArrayOutputStream baos = new ByteArrayOutputStream(); System.setOut(new PrintStream(baos)); - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); instance.parse(args); Assert.assertFalse(instance.isGetVersion()); @@ -78,7 +68,7 @@ public class CliParserTest { String[] args = {"-help"}; PrintStream out = System.out; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); instance.parse(args); Assert.assertFalse(instance.isGetVersion()); @@ -96,7 +86,7 @@ public class CliParserTest { String[] args = {"-version"}; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); instance.parse(args); Assert.assertTrue(instance.isGetVersion()); Assert.assertFalse(instance.isGetHelp()); @@ -114,7 +104,7 @@ public class CliParserTest { String[] args = {"--failOnCVSS"}; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); try { instance.parse(args); } catch (ParseException ex) { @@ -135,7 +125,7 @@ public class CliParserTest { String[] args = {"--failOnCVSS","bad"}; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); instance.parse(args); Assert.assertEquals("Default should be 11", 11, instance.getFailOnCVSS()); Assert.assertFalse(instance.isGetVersion()); @@ -153,7 +143,7 @@ public class CliParserTest { String[] args = {"--failOnCVSS","6"}; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); instance.parse(args); Assert.assertEquals(6, instance.getFailOnCVSS()); Assert.assertFalse(instance.isGetVersion()); @@ -178,7 +168,7 @@ public class CliParserTest { System.setOut(new PrintStream(baos_out)); System.setErr(new PrintStream(baos_err)); - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); try { instance.parse(args); @@ -200,7 +190,7 @@ public class CliParserTest { String[] args = {"-scan"}; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); try { instance.parse(args); @@ -223,7 +213,7 @@ public class CliParserTest { String[] args = {"-scan", "jar.that.does.not.exist", "-app", "test"}; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); try { instance.parse(args); } catch (FileNotFoundException ex) { @@ -245,7 +235,7 @@ public class CliParserTest { File path = new File(this.getClass().getClassLoader().getResource("checkSumTest.file").toURI().getPath()); String[] args = {"-scan", path.getCanonicalPath(), "-out", "./", "-app", "test"}; - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); instance.parse(args); Assert.assertEquals(path.getCanonicalPath(), instance.getScanFiles()[0]); @@ -267,7 +257,7 @@ public class CliParserTest { ByteArrayOutputStream baos = new ByteArrayOutputStream(); System.setOut(new PrintStream(baos)); - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); instance.printVersionInfo(); try { baos.flush(); @@ -296,7 +286,7 @@ public class CliParserTest { ByteArrayOutputStream baos = new ByteArrayOutputStream(); System.setOut(new PrintStream(baos)); - CliParser instance = new CliParser(); + CliParser instance = new CliParser(getSettings()); String[] args = {"-h"}; instance.parse(args); instance.printHelp(); diff --git a/dependency-check-cli/src/test/resources/sample.properties b/dependency-check-cli/src/test/resources/sample.properties index 0b45d5d04..51fdaa872 100644 --- a/dependency-check-cli/src/test/resources/sample.properties +++ b/dependency-check-cli/src/test/resources/sample.properties @@ -1,5 +1,5 @@ autoupdate=false - +somethingmadeup=test analyzer.experimental.enabled=false analyzer.jar.enabled=true analyzer.archive.enabled=true diff --git a/dependency-check-core/pom.xml b/dependency-check-core/pom.xml index efb7f15d6..3755b4afe 100644 --- a/dependency-check-core/pom.xml +++ b/dependency-check-core/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT dependency-check-core diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/AnalysisTask.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/AnalysisTask.java index 7f96e269c..9a09bcce6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/AnalysisTask.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/AnalysisTask.java @@ -21,12 +21,12 @@ import org.owasp.dependencycheck.analyzer.Analyzer; import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.Callable; +import javax.annotation.concurrent.ThreadSafe; /** * Task to support parallelism of dependency-check analysis. Analyses a single @@ -34,6 +34,7 @@ import java.util.concurrent.Callable; * * @author Stefan Neuhaus */ +@ThreadSafe public class AnalysisTask implements Callable { /** @@ -57,10 +58,6 @@ public class AnalysisTask implements Callable { * The list of exceptions that may occur during analysis. */ private final List exceptions; - /** - * A reference to the global settings object. - */ - private final Settings settings; /** * Creates a new analysis task. @@ -70,16 +67,12 @@ public class AnalysisTask implements Callable { * @param engine the dependency-check engine * @param exceptions exceptions that occur during analysis will be added to * this collection of exceptions - * @param settings a reference to the global settings object; this is - * necessary so that when the thread is started the dependencies have a - * correct reference to the global settings. */ - AnalysisTask(Analyzer analyzer, Dependency dependency, Engine engine, List exceptions, Settings settings) { + AnalysisTask(Analyzer analyzer, Dependency dependency, Engine engine, List exceptions) { this.analyzer = analyzer; this.dependency = dependency; this.engine = engine; this.exceptions = exceptions; - this.settings = settings; } /** @@ -89,26 +82,20 @@ public class AnalysisTask implements Callable { */ @Override public Void call() { - try { - Settings.setInstance(settings); - - if (shouldAnalyze()) { - LOGGER.debug("Begin Analysis of '{}' ({})", dependency.getActualFilePath(), analyzer.getName()); - try { - analyzer.analyze(dependency, engine); - } catch (AnalysisException ex) { - LOGGER.warn("An error occurred while analyzing '{}' ({}).", dependency.getActualFilePath(), analyzer.getName()); - LOGGER.debug("", ex); - exceptions.add(ex); - } catch (Throwable ex) { - LOGGER.warn("An unexpected error occurred during analysis of '{}' ({}): {}", - dependency.getActualFilePath(), analyzer.getName(), ex.getMessage()); - LOGGER.debug("", ex); - exceptions.add(ex); - } + if (shouldAnalyze()) { + LOGGER.debug("Begin Analysis of '{}' ({})", dependency.getActualFilePath(), analyzer.getName()); + try { + analyzer.analyze(dependency, engine); + } catch (AnalysisException ex) { + LOGGER.warn("An error occurred while analyzing '{}' ({}).", dependency.getActualFilePath(), analyzer.getName()); + LOGGER.debug("", ex); + exceptions.add(ex); + } catch (Throwable ex) { + LOGGER.warn("An unexpected error occurred during analysis of '{}' ({}): {}", + dependency.getActualFilePath(), analyzer.getName(), ex.getMessage()); + LOGGER.debug("", ex); + exceptions.add(ex); } - } finally { - Settings.cleanup(false); } return null; } @@ -123,7 +110,6 @@ public class AnalysisTask implements Callable { final FileTypeAnalyzer fileTypeAnalyzer = (FileTypeAnalyzer) analyzer; return fileTypeAnalyzer.accept(dependency.getActualFile()); } - return true; } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java index 116dfe43e..f4c23b905 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/Engine.java @@ -42,6 +42,7 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -57,8 +58,14 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import javax.annotation.concurrent.NotThreadSafe; +import org.owasp.dependencycheck.exception.H2DBLockException; +import org.owasp.dependencycheck.utils.H2DBLock; +//CSOFF: AvoidStarImport import static org.owasp.dependencycheck.analyzer.AnalysisPhase.*; +//CSON: AvoidStarImport + /** * Scans files, directories, etc. for Dependencies. Analyzers are loaded and @@ -68,6 +75,7 @@ import static org.owasp.dependencycheck.analyzer.AnalysisPhase.*; * * @author Jeremy Long */ +@NotThreadSafe public class Engine implements FileFilter, AutoCloseable { /** @@ -150,6 +158,10 @@ public class Engine implements FileFilter, AutoCloseable { * The list of dependencies. */ private final List dependencies = Collections.synchronizedList(new ArrayList()); + /** + * The external view of the dependency list. + */ + private Dependency[] dependenciesExternalView = null; /** * A Map of analyzers grouped by Analysis phase. */ @@ -170,7 +182,7 @@ public class Engine implements FileFilter, AutoCloseable { * The ClassLoader to use when dynamically loading Analyzer and Update * services. */ - private ClassLoader serviceClassLoader; + private final ClassLoader serviceClassLoader; /** * A reference to the database. */ @@ -179,30 +191,38 @@ public class Engine implements FileFilter, AutoCloseable { * The Logger for use throughout the class. */ private static final Logger LOGGER = LoggerFactory.getLogger(Engine.class); + /** + * The configured settings. + */ + private final Settings settings; /** * Creates a new {@link Mode#STANDALONE} Engine. + * + * @param settings reference to the configured settings */ - public Engine() { - this(Mode.STANDALONE); + public Engine(Settings settings) { + this(Mode.STANDALONE, settings); } /** * Creates a new Engine. * * @param mode the mode of operation + * @param settings reference to the configured settings */ - public Engine(Mode mode) { - this(Thread.currentThread().getContextClassLoader(), mode); + public Engine(Mode mode, Settings settings) { + this(Thread.currentThread().getContextClassLoader(), mode, settings); } /** * Creates a new {@link Mode#STANDALONE} Engine. * * @param serviceClassLoader a reference the class loader being used + * @param settings reference to the configured settings */ - public Engine(ClassLoader serviceClassLoader) { - this(serviceClassLoader, Mode.STANDALONE); + public Engine(ClassLoader serviceClassLoader, Settings settings) { + this(serviceClassLoader, Mode.STANDALONE, settings); } /** @@ -210,8 +230,10 @@ public class Engine implements FileFilter, AutoCloseable { * * @param serviceClassLoader a reference the class loader being used * @param mode the mode of the engine + * @param settings reference to the configured settings */ - public Engine(ClassLoader serviceClassLoader, Mode mode) { + public Engine(ClassLoader serviceClassLoader, Mode mode, Settings settings) { + this.settings = settings; this.serviceClassLoader = serviceClassLoader; this.mode = mode; initializeEngine(); @@ -225,30 +247,22 @@ public class Engine implements FileFilter, AutoCloseable { * database */ protected final void initializeEngine() { - if (mode.isDatabseRequired()) { - ConnectionFactory.initialize(); - } loadAnalyzers(); } /** * Properly cleans up resources allocated during analysis. */ - public void cleanup() { + @Override + public void close() { if (mode.isDatabseRequired()) { if (database != null) { database.close(); database = null; } - ConnectionFactory.cleanup(); } } - @Override - public void close() { - cleanup(); - } - /** * Loads the analyzers specified in the configuration file (or system * properties). @@ -260,10 +274,16 @@ public class Engine implements FileFilter, AutoCloseable { for (AnalysisPhase phase : mode.getPhases()) { analyzers.put(phase, new ArrayList()); } - - final AnalyzerService service = new AnalyzerService(serviceClassLoader); + boolean loadExperimental = false; + try { + loadExperimental = settings.getBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, false); + } catch (InvalidSettingException ex) { + LOGGER.trace("Experimenal setting not configured; defaulting to false"); + } + final AnalyzerService service = new AnalyzerService(serviceClassLoader, loadExperimental); final List iterator = service.getAnalyzers(mode.getPhases()); for (Analyzer a : iterator) { + a.initialize(this.settings); analyzers.get(a.getAnalysisPhase()).add(a); if (a instanceof FileTypeAnalyzer) { this.fileTypeAnalyzers.add((FileTypeAnalyzer) a); @@ -282,18 +302,44 @@ public class Engine implements FileFilter, AutoCloseable { } /** - * Get the dependencies identified. The returned list is a reference to the - * engine's synchronized list. You must synchronize on the returned - * list when you modify and iterate over it from multiple threads. E.g. - * this holds for analyzers supporting parallel processing during their - * analysis phase. + * Adds a dependency. + * + * @param dependency the dependency to add + */ + public synchronized void addDependency(Dependency dependency) { + dependencies.add(dependency); + dependenciesExternalView = null; + } + + /** + * Sorts the dependency list. + */ + public synchronized void sortDependencies() { + //TODO - is this actually necassary???? + Collections.sort(dependencies); + dependenciesExternalView = null; + } + + /** + * Removes the dependency. + * + * @param dependency the dependency to remove. + */ + public synchronized void removeDependency(Dependency dependency) { + dependencies.remove(dependency); + dependenciesExternalView = null; + } + + /** + * Returns a copy of the dependencies as an array. * * @return the dependencies identified - * @see Collections#synchronizedList(List) - * @see Analyzer#supportsParallelProcessing() */ - public synchronized List getDependencies() { - return dependencies; + public synchronized Dependency[] getDependencies() { + if (dependenciesExternalView == null) { + dependenciesExternalView = dependencies.toArray(new Dependency[dependencies.size()]); + } + return dependenciesExternalView; } /** @@ -304,6 +350,7 @@ public class Engine implements FileFilter, AutoCloseable { public synchronized void setDependencies(List dependencies) { this.dependencies.clear(); this.dependencies.addAll(dependencies); + dependenciesExternalView = null; } /** @@ -540,7 +587,7 @@ public class Engine implements FileFilter, AutoCloseable { * @return the scanned dependency * @since v1.4.4 */ - protected Dependency scanFile(File file, String projectReference) { + protected synchronized Dependency scanFile(File file, String projectReference) { Dependency dependency = null; if (file.isFile()) { if (accept(file)) { @@ -550,31 +597,31 @@ public class Engine implements FileFilter, AutoCloseable { } final String sha1 = dependency.getSha1sum(); boolean found = false; - synchronized (dependencies) { - if (sha1 != null) { - for (Dependency existing : dependencies) { - if (sha1.equals(existing.getSha1sum())) { - found = true; - if (projectReference != null) { - existing.addProjectReference(projectReference); - } - if (existing.getActualFilePath() != null && dependency.getActualFilePath() != null - && !existing.getActualFilePath().equals(dependency.getActualFilePath())) { - existing.addRelatedDependency(dependency); - } else { - dependency = existing; - } - break; + + if (sha1 != null) { + for (Dependency existing : dependencies) { + if (sha1.equals(existing.getSha1sum())) { + found = true; + if (projectReference != null) { + existing.addProjectReference(projectReference); } + if (existing.getActualFilePath() != null && dependency.getActualFilePath() != null + && !existing.getActualFilePath().equals(dependency.getActualFilePath())) { + existing.addRelatedDependency(dependency); + } else { + dependency = existing; + } + break; } } - if (!found) { - dependencies.add(dependency); - } } - } else { - LOGGER.debug("Path passed to scanFile(File) is not a file that can be scanned by dependency-check: {}. Skipping the file.", file); + if (!found) { + dependencies.add(dependency); + dependenciesExternalView = null; + } } + } else { + LOGGER.debug("Path passed to scanFile(File) is not a file that can be scanned by dependency-check: {}. Skipping the file.", file); } return dependency; } @@ -662,15 +709,14 @@ public class Engine implements FileFilter, AutoCloseable { } boolean autoUpdate = true; try { - autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); + autoUpdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE); } catch (InvalidSettingException ex) { LOGGER.debug("Invalid setting for auto-update; using true."); exceptions.add(ex); } if (autoUpdate) { try { - database = CveDB.getInstance(); - doUpdates(); + doUpdates(true); } catch (UpdateException ex) { exceptions.add(ex); LOGGER.warn("Unable to update Cached Web DataSource, using local " @@ -681,10 +727,10 @@ public class Engine implements FileFilter, AutoCloseable { } } else { try { - if (ConnectionFactory.isH2Connection() && !ConnectionFactory.h2DataFileExists()) { + if (ConnectionFactory.isH2Connection(settings) && !ConnectionFactory.h2DataFileExists(settings)) { throw new ExceptionCollection(new NoDataException("Autoupdate is disabled and the database does not exist"), true); } else { - database = CveDB.getInstance(); + openDatabase(true, true); } } catch (IOException ex) { throw new ExceptionCollection(new DatabaseException("Autoupdate is disabled and unable to connect to the database"), true); @@ -735,13 +781,11 @@ public class Engine implements FileFilter, AutoCloseable { * @param exceptions the collection of exceptions to collect * @return a collection of analysis tasks */ - protected List getAnalysisTasks(Analyzer analyzer, List exceptions) { + protected synchronized List getAnalysisTasks(Analyzer analyzer, List exceptions) { final List result = new ArrayList<>(); - synchronized (dependencies) { - for (final Dependency dependency : dependencies) { - final AnalysisTask task = new AnalysisTask(analyzer, dependency, this, exceptions, Settings.getInstance()); - result.add(task); - } + for (final Dependency dependency : dependencies) { + final AnalysisTask task = new AnalysisTask(analyzer, dependency, this, exceptions); + result.add(task); } return result; } @@ -766,14 +810,14 @@ public class Engine implements FileFilter, AutoCloseable { /** * Initializes the given analyzer. * - * @param analyzer the analyzer to initialize + * @param analyzer the analyzer to prepare * @throws InitializationException thrown when there is a problem * initializing the analyzer */ protected void initializeAnalyzer(Analyzer analyzer) throws InitializationException { try { LOGGER.debug("Initializing {}", analyzer.getName()); - analyzer.initialize(); + analyzer.prepare(this); } catch (InitializationException ex) { LOGGER.error("Exception occurred initializing {}.", analyzer.getName()); LOGGER.debug("", ex); @@ -816,21 +860,126 @@ public class Engine implements FileFilter, AutoCloseable { * @throws UpdateException thrown if the operation fails */ public void doUpdates() throws UpdateException { + doUpdates(false); + } + + /** + * Cycles through the cached web data sources and calls update on all of + * them. + * + * @param remainOpen whether or not the database connection should remain + * open + * @throws UpdateException thrown if the operation fails + */ + public void doUpdates(boolean remainOpen) throws UpdateException { if (mode.isDatabseRequired()) { - LOGGER.info("Checking for updates"); - final long updateStart = System.currentTimeMillis(); - final UpdateService service = new UpdateService(serviceClassLoader); - final Iterator iterator = service.getDataSources(); - while (iterator.hasNext()) { - final CachedWebDataSource source = iterator.next(); - source.update(); + H2DBLock dblock = null; + try { + if (ConnectionFactory.isH2Connection(settings)) { + dblock = new H2DBLock(settings); + LOGGER.debug("locking for update"); + dblock.lock(); + } + openDatabase(false, false); + LOGGER.info("Checking for updates"); + final long updateStart = System.currentTimeMillis(); + final UpdateService service = new UpdateService(serviceClassLoader); + final Iterator iterator = service.getDataSources(); + while (iterator.hasNext()) { + final CachedWebDataSource source = iterator.next(); + source.update(this); + } + database.close(); + database = null; + LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart); + if (remainOpen) { + openDatabase(true, false); + } + } catch (H2DBLockException ex) { + throw new UpdateException("Unable to obtain an exclusive lock on the H2 database to perform updates", ex); + } finally { + if (dblock != null) { + dblock.release(); + } } - LOGGER.info("Check for updates complete ({} ms)", System.currentTimeMillis() - updateStart); } else { LOGGER.info("Skipping update check in evidence collection mode."); } } + /** + *

+ * This method is only public for unit/integration testing. This method + * should not be called by any integration that uses + * dependency-check-core.

+ *

+ * Opens the database connection.

+ */ + public void openDatabase() { + openDatabase(false, true); + } + + /** + *

+ * This method is only public for unit/integration testing. This method + * should not be called by any integration that uses + * dependency-check-core.

+ *

+ * Opens the database connection; if readOnly is true a copy of the database + * will be made.

+ * + * @param readOnly whether or not the database connection should be readonly + * @param lockRequired whether or not a lock needs to be acquired when + * opening the database + */ + public void openDatabase(boolean readOnly, boolean lockRequired) { + if (mode.isDatabseRequired() && database == null) { + //needed to update schema any required schema changes + database = new CveDB(settings); + if (readOnly + && ConnectionFactory.isH2Connection(settings) + && settings.getString(Settings.KEYS.DB_CONNECTION_STRING).contains("file:%s")) { + H2DBLock lock = null; + try { + final File db = ConnectionFactory.getH2DataFile(settings); + if (db.isFile()) { + database.close(); + if (lockRequired) { + lock = new H2DBLock(settings); + lock.lock(); + } + LOGGER.debug("copying database"); + final File temp = settings.getTempDirectory(); + final File tempDB = new File(temp, db.getName()); + Files.copy(db.toPath(), tempDB.toPath()); + LOGGER.debug("copying complete '{}'", temp.toPath()); + settings.setString(Settings.KEYS.DATA_DIRECTORY, temp.getPath()); + final String connStr = settings.getString(Settings.KEYS.DB_CONNECTION_STRING); + settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connStr + "ACCESS_MODE_DATA=r"); + database = new CveDB(settings); + } + } catch (IOException ex) { + LOGGER.debug("Unable to open db in read only mode", ex); + } catch (H2DBLockException ex) { + LOGGER.debug("Failed to obtain lock - unable to open db in read only mode", ex); + } finally { + if (lock != null) { + lock.release(); + } + } + } + } + } + + /** + * Returns a reference to the database. + * + * @return a reference to the database + */ + public CveDB getDatabase() { + return this.database; + } + /** * Returns a full list of all of the analyzers. This is useful for reporting * which analyzers where used. @@ -876,6 +1025,15 @@ public class Engine implements FileFilter, AutoCloseable { return this.fileTypeAnalyzers; } + /** + * Returns the configured settings. + * + * @return the configured settings + */ + public Settings getSettings() { + return settings; + } + /** * Adds a file type analyzer. This has been added solely to assist in unit * testing the Engine. @@ -932,7 +1090,7 @@ public class Engine implements FileFilter, AutoCloseable { throw new UnsupportedOperationException("Cannot generate report in evidence collection mode."); } final DatabaseProperties prop = database.getDatabaseProperties(); - final ReportGenerator r = new ReportGenerator(applicationName, groupId, artifactId, version, dependencies, getAnalyzers(), prop); + final ReportGenerator r = new ReportGenerator(applicationName, groupId, artifactId, version, dependencies, getAnalyzers(), prop, settings); try { r.write(outputDir.getAbsolutePath(), format); } catch (ReportException ex) { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java index c7f5b1021..442700b58 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/agent/DependencyCheckScanAgent.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.agent; import java.io.File; import java.io.IOException; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.update.exception.UpdateException; @@ -61,6 +62,7 @@ import org.slf4j.LoggerFactory; * @author Steve Springett */ @SuppressWarnings("unused") +@NotThreadSafe public class DependencyCheckScanAgent { // @@ -103,8 +105,8 @@ public class DependencyCheckScanAgent { */ private boolean autoUpdate = true; /** - * Sets whether the data directory should be updated without performing a scan. - * Default is false. + * Sets whether the data directory should be updated without performing a + * scan. Default is false. */ private boolean updateOnly = false; /** @@ -215,8 +217,12 @@ public class DependencyCheckScanAgent { */ private String pathToMono; /** - * The path to optional dependency-check properties file. This will be - * used to side-load additional user-defined properties. + * The configured settings. + */ + private Settings settings; + /** + * The path to optional dependency-check properties file. This will be used + * to side-load additional user-defined properties. * {@link Settings#mergeProperties(String)} */ private String propertiesFilePath; @@ -872,7 +878,7 @@ public class DependencyCheckScanAgent { populateSettings(); final Engine engine; try { - engine = new Engine(); + engine = new Engine(settings); } catch (DatabaseException ex) { throw new ExceptionCollection(ex, true); } @@ -912,20 +918,19 @@ public class DependencyCheckScanAgent { * proxy server, port, and connection timeout. */ private void populateSettings() { - Settings.initialize(); + settings = new Settings(); if (dataDirectory != null) { - Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); + settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); } else { final File jarPath = new File(DependencyCheckScanAgent.class.getProtectionDomain().getCodeSource().getLocation().getPath()); final File base = jarPath.getParentFile(); - final String sub = Settings.getString(Settings.KEYS.DATA_DIRECTORY); + final String sub = settings.getString(Settings.KEYS.DATA_DIRECTORY); final File dataDir = new File(base, sub); - Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); + settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); } - if (propertiesFilePath != null) { try { - Settings.mergeProperties(propertiesFilePath); + settings.mergeProperties(propertiesFilePath); LOGGER.info("Successfully loaded user-defined properties"); } catch (IOException e) { LOGGER.error("Unable to merge user-defined properties", e); @@ -933,30 +938,29 @@ public class DependencyCheckScanAgent { } } - LOGGER.info("Populating settings"); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUsername); - Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPassword); - Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); - Settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_CENTRAL_URL, centralUrl); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); - Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); + settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUsername); + settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PASSWORD, proxyPassword); + settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); + settings.setStringIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressionFile); + settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_CENTRAL_URL, centralUrl); + settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); + settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); + settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); + settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); + settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); + settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); + settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); + settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); } /** @@ -988,9 +992,9 @@ public class DependencyCheckScanAgent { } throw new ScanAgentException("One or more exceptions occurred during analysis; please see the debug log for more details.", ex); } finally { - Settings.cleanup(true); + settings.cleanup(true); if (engine != null) { - engine.cleanup(); + engine.close(); } } return engine; @@ -1004,7 +1008,7 @@ public class DependencyCheckScanAgent { * @throws org.owasp.dependencycheck.exception.ScanAgentException thrown if * there is an exception executing the scan. */ - private void checkForFailure(List dependencies) throws ScanAgentException { + private void checkForFailure(Dependency[] dependencies) throws ScanAgentException { final StringBuilder ids = new StringBuilder(); for (Dependency d : dependencies) { boolean addName = true; @@ -1024,7 +1028,6 @@ public class DependencyCheckScanAgent { final String msg = String.format("%n%nDependency-Check Failure:%n" + "One or more dependencies were identified with vulnerabilities that have a CVSS score greater than '%.1f': %s%n" + "See the dependency-check report for more details.%n%n", failBuildOnCVSS, ids.toString()); - throw new ScanAgentException(msg); } } @@ -1035,7 +1038,7 @@ public class DependencyCheckScanAgent { * * @param dependencies a list of dependency objects */ - private void showSummary(List dependencies) { + private void showSummary(Dependency[] dependencies) { final StringBuilder summary = new StringBuilder(); for (Dependency d : dependencies) { boolean firstEntry = true; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractAnalyzer.java index 891ed6835..e208803a0 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractAnalyzer.java @@ -23,15 +23,17 @@ import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; +import javax.annotation.concurrent.ThreadSafe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Base class for analyzers to avoid code duplication of initialize and close as + * Base class for analyzers to avoid code duplication of prepare and close as * most analyzers do not need these methods. * * @author Jeremy Long */ +@ThreadSafe public abstract class AbstractAnalyzer implements Analyzer { /** @@ -42,6 +44,10 @@ public abstract class AbstractAnalyzer implements Analyzer { * A flag indicating whether or not the analyzer is enabled. */ private volatile boolean enabled = true; + /** + * The configured settings. + */ + private Settings settings; /** * Get the value of enabled. @@ -63,41 +69,57 @@ public abstract class AbstractAnalyzer implements Analyzer { } /** - *

- * Returns the setting key to determine if the analyzer is enabled.

+ * Returns the configured settings. * - * @return the key for the analyzer's enabled property + * @return the configured settings */ - protected abstract String getAnalyzerEnabledSettingKey(); - - /** - * Analyzes a given dependency. If the dependency is an archive, such as a - * WAR or EAR, the contents are extracted, scanned, and added to the list of - * dependencies within the engine. - * - * @param dependency the dependency to analyze - * @param engine the engine scanning - * @throws AnalysisException thrown if there is an analysis exception - */ - protected abstract void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException; - - /** - * Initializes a given Analyzer. This will be skipped if the analyzer is - * disabled. - * - * @throws InitializationException thrown if there is an exception - */ - protected void initializeAnalyzer() throws InitializationException { + protected Settings getSettings() { + return settings; } /** - * Closes a given Analyzer. This will be skipped if the analyzer is + * Initializes the analyzer with the configured settings. + * + * @param settings the configured settings to use + */ + @Override + public void initialize(Settings settings) { + this.settings = settings; + } + + /** + * Initialize the abstract analyzer. + * + * @param engine a reference to the dependency-check engine + * @throws InitializationException thrown if there is an exception + */ + @Override + public final void prepare(Engine engine) throws InitializationException { + final String key = getAnalyzerEnabledSettingKey(); + try { + this.setEnabled(settings.getBoolean(key, true)); + } catch (InvalidSettingException ex) { + final String msg = String.format("Invalid setting for property '%s'", key); + LOGGER.warn(msg); + LOGGER.debug(msg, ex); + } + + if (isEnabled()) { + prepareAnalyzer(engine); + } else { + LOGGER.debug("{} has been disabled", getName()); + } + } + + /** + * Prepares a given Analyzer. This will be skipped if the analyzer is * disabled. * - * @throws Exception thrown if there is an exception + * @param engine a reference to the dependency-check engine + * @throws InitializationException thrown if there is an exception */ - protected void closeAnalyzer() throws Exception { - // Intentionally empty, analyzer will override this if they must close a resource. + protected void prepareAnalyzer(Engine engine) throws InitializationException { + // Intentionally empty, analyzer will override this if they must prepare anything. } /** @@ -117,26 +139,15 @@ public abstract class AbstractAnalyzer implements Analyzer { } /** - * The initialize method does nothing for this Analyzer. + * Analyzes a given dependency. If the dependency is an archive, such as a + * WAR or EAR, the contents are extracted, scanned, and added to the list of + * dependencies within the engine. * - * @throws InitializationException thrown if there is an exception + * @param dependency the dependency to analyze + * @param engine the engine scanning + * @throws AnalysisException thrown if there is an analysis exception */ - @Override - public final void initialize() throws InitializationException { - final String key = getAnalyzerEnabledSettingKey(); - try { - this.setEnabled(Settings.getBoolean(key, true)); - } catch (InvalidSettingException ex) { - LOGGER.warn("Invalid setting for property '{}'", key); - LOGGER.debug("", ex); - } - - if (isEnabled()) { - initializeAnalyzer(); - } else { - LOGGER.debug("{} has been disabled", getName()); - } - } + protected abstract void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException; /** * The close method does nothing for this Analyzer. @@ -150,6 +161,16 @@ public abstract class AbstractAnalyzer implements Analyzer { } } + /** + * Closes a given Analyzer. This will be skipped if the analyzer is + * disabled. + * + * @throws Exception thrown if there is an exception + */ + protected void closeAnalyzer() throws Exception { + // Intentionally empty, analyzer will override this if they must close a resource. + } + /** * The default is to support parallel processing. * @@ -157,8 +178,15 @@ public abstract class AbstractAnalyzer implements Analyzer { */ @Override public boolean supportsParallelProcessing() { - //temporarily removing parallel processing from all analyzders until further examination of thread safety occurs. - //return true; - return false; + return true; } + + /** + *

+ * Returns the setting key to determine if the analyzer is enabled.

+ * + * @return the key for the analyzer's enabled property + */ + protected abstract String getAnalyzerEnabledSettingKey(); + } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractDependencyComparingAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractDependencyComparingAnalyzer.java new file mode 100644 index 000000000..f8efe205d --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractDependencyComparingAnalyzer.java @@ -0,0 +1,125 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2017 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.analyzer; + +import java.util.HashSet; +import java.util.Set; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import org.owasp.dependencycheck.dependency.Dependency; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

+ * This analyzer ensures dependencies that should be grouped together, to remove + * excess noise from the report, are grouped. An example would be Spring, Spring + * Beans, Spring MVC, etc. If they are all for the same version and have the + * same relative path then these should be grouped into a single dependency + * under the core/main library.

+ *

+ * Note, this grouping only works on dependencies with identified CVE + * entries

+ * + * @author Jeremy Long + */ +@ThreadSafe +public abstract class AbstractDependencyComparingAnalyzer extends AbstractAnalyzer { + + /** + * The Logger. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDependencyComparingAnalyzer.class); + + /** + * a flag indicating if this analyzer has run. This analyzer only runs once. + */ + private boolean analyzed = false; + + /** + * Returns a flag indicating if this analyzer has run. This analyzer only + * runs once. Note this is currently only used in the unit tests. + * + * @return a flag indicating if this analyzer has run. This analyzer only + * runs once + */ + protected synchronized boolean getAnalyzed() { + return analyzed; + } + + /** + * Does not support parallel processing as it only runs once and then + * operates on all dependencies. + * + * @return whether or not parallel processing is enabled + * @see #analyze(Dependency, Engine) + */ + @Override + public final boolean supportsParallelProcessing() { + return false; + } + + /** + * Analyzes a set of dependencies. If they have been found to have the same + * base path and the same set of identifiers they are likely related. The + * related dependencies are bundled into a single reportable item. + * + * @param ignore this analyzer ignores the dependency being analyzed + * @param engine the engine that is scanning the dependencies + * @throws AnalysisException is thrown if there is an error reading the JAR + * file. + */ + @Override + protected synchronized void analyzeDependency(Dependency ignore, Engine engine) throws AnalysisException { + if (!analyzed) { + analyzed = true; + final Set dependenciesToRemove = new HashSet<>(); + + final Dependency[] dependencies = engine.getDependencies(); + if (dependencies.length < 2) { + return; + } + for (int x = 0; x < dependencies.length - 1; x++) { + final Dependency dependency = dependencies[x]; + if (!dependenciesToRemove.contains(dependency)) { + for (int y = x + 1; y < dependencies.length; y++) { + final Dependency nextDependency = dependencies[y]; + if (evaluateDependencies(dependency, nextDependency, dependenciesToRemove)) { + break; + } + } + } + } + for (Dependency d : dependenciesToRemove) { + engine.removeDependency(d); + } + } + } + + /** + * Evaluates the dependencies + * + * @param dependency a dependency to compare + * @param nextDependency a dependency to compare + * @param dependenciesToRemove a set of dependencies that will be removed + * @return true if a dependency is removed; otherwise false + */ + protected abstract boolean evaluateDependencies(final Dependency dependency, + final Dependency nextDependency, final Set dependenciesToRemove); +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java index 74e87bd0e..02a3021a5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractFileTypeAnalyzer.java @@ -25,6 +25,8 @@ import java.io.FileFilter; import java.util.Collections; import java.util.HashSet; import java.util.Set; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.exception.InitializationException; /** @@ -33,6 +35,7 @@ import org.owasp.dependencycheck.exception.InitializationException; * * @author Jeremy Long */ +@ThreadSafe public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implements FileTypeAnalyzer { // @@ -45,16 +48,6 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen */ private boolean filesMatched = false; - /** - * Get the value of filesMatched. A flag indicating whether the scan - * included any file types this analyzer supports. - * - * @return the value of filesMatched - */ - protected boolean isFilesMatched() { - return filesMatched; - } - /** * Set the value of filesMatched. A flag indicating whether the scan * included any file types this analyzer supports. @@ -70,13 +63,14 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen /** * Initializes the analyzer. * + * @param engine a reference to the dependency-check engine * @throws InitializationException thrown if there is an exception during * initialization */ @Override - protected final void initializeAnalyzer() throws InitializationException { + protected final void prepareAnalyzer(Engine engine) throws InitializationException { if (filesMatched) { - initializeFileTypeAnalyzer(); + prepareFileTypeAnalyzer(engine); } else { this.setEnabled(false); } @@ -99,12 +93,13 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen protected abstract FileFilter getFileFilter(); /** - * Initializes the file type analyzer. + * Prepares the file type analyzer for dependency analysis. * + * @param engine a reference to the dependency-check engine * @throws InitializationException thrown if there is an exception during * initialization */ - protected abstract void initializeFileTypeAnalyzer() throws InitializationException; + protected abstract void prepareFileTypeAnalyzer(Engine engine) throws InitializationException; // /** @@ -135,7 +130,7 @@ public abstract class AbstractFileTypeAnalyzer extends AbstractAnalyzer implemen * constructs a new Set that can be used in a final static declaration.

*

* This implementation was copied from - * http://stackoverflow.com/questions/2041778/initialize-java-hashset-values-by-construction

+ * http://stackoverflow.com/questions/2041778/prepare-java-hashset-values-by-construction

* * @param strings a list of strings to add to the set. * @return a Set of strings. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java index bab7dcfd1..6452f35a2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzer.java @@ -22,10 +22,11 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; @@ -47,6 +48,7 @@ import org.xml.sax.SAXException; * * @author Jeremy Long */ +@ThreadSafe public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { /** @@ -56,15 +58,15 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { /** * The list of suppression rules */ - private List rules; + private SuppressionRule[] rules = null; /** * Get the number of suppression rules. * * @return the number of suppression rules */ - protected synchronized int getRuleCount() { - return rules.size(); + protected int getRuleCount() { + return rules.length; } /** @@ -77,22 +79,25 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { } /** - * The initialize method loads the suppression XML file. + * The prepare method loads the suppression XML file. * + * @param engine a reference the dependency-check engine * @throws InitializationException thrown if there is an exception */ @Override - public void initializeAnalyzer() throws InitializationException { - try { - loadSuppressionData(); - } catch (SuppressionParseException ex) { - throw new InitializationException("Error initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex); + public synchronized void prepareAnalyzer(Engine engine) throws InitializationException { + if (rules == null) { + try { + rules = loadSuppressionData(); + } catch (SuppressionParseException ex) { + throw new InitializationException("Error initializing the suppression analyzer: " + ex.getLocalizedMessage(), ex); + } } } @Override - protected synchronized void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { - if (rules == null || rules.size() <= 0) { + protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + if (rules == null || rules.length <= 0) { return; } for (final SuppressionRule rule : rules) { @@ -101,55 +106,59 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { } /** - * Loads all the suppression rules files configured in the {@link Settings} - * singleton. + * Loads all the suppression rules files configured in the {@link Settings}. * + * @return the array of rules that were loaded * @throws SuppressionParseException thrown if the XML cannot be parsed. */ - private synchronized void loadSuppressionData() throws SuppressionParseException { + private SuppressionRule[] loadSuppressionData() throws SuppressionParseException { + List ruleList; final SuppressionParser parser = new SuppressionParser(); try { final InputStream in = FileUtils.getResourceAsStream("dependencycheck-base-suppression.xml"); - rules = Collections.synchronizedList(parser.parseSuppressionRules(in)); + ruleList = parser.parseSuppressionRules(in); } catch (SAXException ex) { throw new SuppressionParseException("Unable to parse the base suppression data file", ex); } - final String[] suppressionFilePaths = Settings.getArray(Settings.KEYS.SUPPRESSION_FILE); - if (suppressionFilePaths == null || suppressionFilePaths.length == 0) { - return; + final String[] suppressionFilePaths = getSettings().getArray(Settings.KEYS.SUPPRESSION_FILE); + if (suppressionFilePaths != null && suppressionFilePaths.length > 0) { + // Load all the suppression file paths + for (final String suppressionFilePath : suppressionFilePaths) { + ruleList.addAll(loadSuppressionFile(parser, suppressionFilePath)); + } } - - // Load all the suppression file paths - for (final String suppressionFilePath : suppressionFilePaths) { - loadSuppressionFile(parser, suppressionFilePath); - } - LOGGER.debug("{} suppression rules were loaded.", rules.size()); + LOGGER.debug("{} suppression rules were loaded.", ruleList.size()); + return ruleList.toArray(new SuppressionRule[ruleList.size()]); } /** * Load a single suppression rules file from the path provided using the * parser provided. * - * @param parser the parser to use for loading the file. - * @param suppressionFilePath the path to load. + * @param parser the parser to use for loading the file + * @param suppressionFilePath the path to load + * @return the list of loaded suppression rules * @throws SuppressionParseException thrown if the suppression file cannot * be loaded and parsed. */ - private synchronized void loadSuppressionFile(final SuppressionParser parser, final String suppressionFilePath) throws SuppressionParseException { + private List loadSuppressionFile(final SuppressionParser parser, + final String suppressionFilePath) throws SuppressionParseException { LOGGER.debug("Loading suppression rules from '{}'", suppressionFilePath); - + final List list = new ArrayList<>(); File file = null; boolean deleteTempFile = false; try { final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE); if (uriRx.matcher(suppressionFilePath).matches()) { deleteTempFile = true; - file = FileUtils.getTempFile("suppression", "xml"); + file = getSettings().getTempFile("suppression", "xml"); final URL url = new URL(suppressionFilePath); + final Downloader downloader = new Downloader(getSettings()); try { - Downloader.fetchFile(url, file, false); + downloader.fetchFile(url, file, false); } catch (DownloadFailedException ex) { - Downloader.fetchFile(url, file, true); + LOGGER.trace("Failed download - first attempt", ex); + downloader.fetchFile(url, file, true); } } else { file = new File(suppressionFilePath); @@ -158,7 +167,7 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { try (InputStream suppressionsFromClasspath = FileUtils.getResourceAsStream(suppressionFilePath)) { if (suppressionsFromClasspath != null) { deleteTempFile = true; - file = FileUtils.getTempFile("suppression", "xml"); + file = getSettings().getTempFile("suppression", "xml"); try { org.apache.commons.io.FileUtils.copyInputStreamToFile(suppressionsFromClasspath, file); } catch (IOException ex) { @@ -175,7 +184,7 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { throw new SuppressionParseException(msg); } try { - rules.addAll(parser.parseSuppressionRules(file)); + list.addAll(parser.parseSuppressionRules(file)); } catch (SuppressionParseException ex) { LOGGER.warn("Unable to parse suppression xml file '{}'", file.getPath()); LOGGER.warn(ex.getMessage()); @@ -195,6 +204,7 @@ public abstract class AbstractSuppressionAnalyzer extends AbstractAnalyzer { FileUtils.delete(file); } } + return list; } /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/Analyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/Analyzer.java index 0180a0d01..2e3067703 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/Analyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/Analyzer.java @@ -21,11 +21,22 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.exception.InitializationException; +import org.owasp.dependencycheck.utils.Settings; /** + *

* An interface that defines an Analyzer that is used to identify Dependencies. * An analyzer will collect information about the dependency in the form of - * Evidence. + * Evidence.

+ *

+ * When the {@link org.owasp.dependencycheck.Engine} executes it will load the + * analyzers and call the methods in the following order:

+ *
    + *
  1. {@link #initialize(org.owasp.dependencycheck.utils.Settings)}
  2. + *
  3. {@link #prepare(org.owasp.dependencycheck.Engine)}
  4. + *
  5. {@link #analyze(org.owasp.dependencycheck.dependency.Dependency, org.owasp.dependencycheck.Engine)}
  6. + *
  7. {@link #close()}
  8. + *
* * @author Jeremy Long */ @@ -60,13 +71,21 @@ public interface Analyzer { AnalysisPhase getAnalysisPhase(); /** - * The initialize method is called (once) prior to the analyze method being + * Initializes the analyzer with the configured settings. + * + * @param settings the configured settings + */ + void initialize(Settings settings); + + /** + * The prepare method is called (once) prior to the analyze method being * called on all of the dependencies. * + * @param engine a reference to the dependency-check engine * @throws InitializationException is thrown if an exception occurs * initializing the analyzer. */ - void initialize() throws InitializationException; + void prepare(Engine engine) throws InitializationException; /** * The close method is called after all of the dependencies have been @@ -77,16 +96,20 @@ public interface Analyzer { void close() throws Exception; /** - * Returns whether multiple instances of the same type of analyzer can run in parallel. - * Note that running analyzers of different types in parallel is not supported at all. + * Returns whether multiple instances of the same type of analyzer can run + * in parallel. Note that running analyzers of different types in parallel + * is not supported at all. * - * @return {@code true} if the analyzer supports parallel processing, {@code false} else + * @return {@code true} if the analyzer supports parallel processing, + * {@code false} else */ boolean supportsParallelProcessing(); + /** * Get the value of enabled. * * @return the value of enabled */ boolean isEnabled(); + } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalyzerService.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalyzerService.java index 4e136aa6f..ff0b19428 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalyzerService.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AnalyzerService.java @@ -18,21 +18,21 @@ package org.owasp.dependencycheck.analyzer; import java.util.ArrayList; -import org.owasp.dependencycheck.utils.InvalidSettingException; -import org.owasp.dependencycheck.utils.Settings; import org.slf4j.LoggerFactory; import static java.util.Arrays.asList; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; +import javax.annotation.concurrent.ThreadSafe; /** * The Analyzer Service Loader. This class loads all services that implement - * org.owasp.dependencycheck.analyzer.Analyzer. + * {@link org.owasp.dependencycheck.analyzer.Analyzer}. * * @author Jeremy Long */ +@ThreadSafe public class AnalyzerService { /** @@ -44,14 +44,20 @@ public class AnalyzerService { * The service loader for analyzers. */ private final ServiceLoader service; + /** + * The configured settings. + */ + private final boolean loadExperimental; /** * Creates a new instance of AnalyzerService. * * @param classLoader the ClassLoader to use when dynamically loading * Analyzer and Update services + * @param loadExperimental whether or not to load the experimental analyzers */ - public AnalyzerService(ClassLoader classLoader) { + public AnalyzerService(ClassLoader classLoader, boolean loadExperimental) { + this.loadExperimental = loadExperimental; service = ServiceLoader.load(Analyzer.class, classLoader); } @@ -85,18 +91,12 @@ public class AnalyzerService { private List getAnalyzers(List phases) { final List analyzers = new ArrayList<>(); final Iterator iterator = service.iterator(); - boolean experimentalEnabled = false; - try { - experimentalEnabled = Settings.getBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, false); - } catch (InvalidSettingException ex) { - LOGGER.error("invalid experimental setting", ex); - } while (iterator.hasNext()) { final Analyzer a = iterator.next(); if (!phases.contains(a.getAnalysisPhase())) { continue; } - if (!experimentalEnabled && a.getClass().isAnnotationPresent(Experimental.class)) { + if (!loadExperimental && a.getClass().isAnnotationPresent(Experimental.class)) { continue; } LOGGER.debug("Loaded Analyzer {}", a.getName()); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java index ffd2bc2c2..d542171e6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzer.java @@ -28,6 +28,8 @@ import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; @@ -61,6 +63,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -71,7 +74,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { * The count of directories created during analysis. This is used for * creating temporary directories. */ - private static int dirCount = 0; + private static final AtomicInteger DIRECTORY_COUNT = new AtomicInteger(0); /** * The parent directory for the individual directories per archive. */ @@ -80,21 +83,11 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { * The max scan depth that the analyzer will recursively extract nested * archives. */ - private static final int MAX_SCAN_DEPTH = Settings.getInt("archive.scan.depth", 3); + private int maxScanDepth; /** - * Tracks the current scan/extraction depth for nested archives. + * The file filter used to filter supported files. */ - private int scanDepth = 0; - - // - /** - * The name of the analyzer. - */ - private static final String ANALYZER_NAME = "Archive Analyzer"; - /** - * The phase that this analyzer is intended to run in. - */ - private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INITIAL; + private FileFilter fileFilter = null; /** * The set of things we can handle with Zip methods */ @@ -106,35 +99,41 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { */ private static final Set EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2"); - static { - final String additionalZipExt = Settings.getString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS); - if (additionalZipExt != null) { - final String[] ext = additionalZipExt.split("\\s*,\\s*"); - Collections.addAll(KNOWN_ZIP_EXT, ext); - } - EXTENSIONS.addAll(KNOWN_ZIP_EXT); - } - /** * Detects files with extensions to remove from the engine's collection of * dependencies. */ private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance() .addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2").build(); - - /** - * The file filter used to filter supported files. - */ - private static final FileFilter FILTER = FileFilterBuilder.newInstance().addExtensions(EXTENSIONS).build(); - /** * Detects files with .zip extension. */ private static final FileFilter ZIP_FILTER = FileFilterBuilder.newInstance().addExtensions("zip").build(); + // + /** + * The name of the analyzer. + */ + private static final String ANALYZER_NAME = "Archive Analyzer"; + /** + * The phase that this analyzer is intended to run in. + */ + private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INITIAL; + + /** + * Initializes the analyzer with the configured settings. + * + * @param settings the configured settings to use + */ + @Override + public void initialize(Settings settings) { + super.initialize(settings); + initializeSettings(); + } + @Override protected FileFilter getFileFilter() { - return FILTER; + return fileFilter; } /** @@ -170,15 +169,16 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { } /** - * The initialize method does nothing for this Analyzer. + * The prepare method does nothing for this Analyzer. * + * @param engine a reference to the dependency-check engine * @throws InitializationException is thrown if there is an exception * deleting or creating temporary files */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { try { - final File baseDir = Settings.getTempDirectory(); + final File baseDir = getSettings().getTempDirectory(); tempFileLocation = File.createTempFile("check", "tmp", baseDir); if (!tempFileLocation.delete()) { setEnabled(false); @@ -228,7 +228,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { */ @Override public boolean supportsParallelProcessing() { - return false; + return true; } /** @@ -242,6 +242,22 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { */ @Override public void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + extractAndAnalyze(dependency, engine, 0); + engine.sortDependencies(); + } + + /** + * Extracts the contents of the archive dependency and scans for additional + * dependencies. + * + * @param dependency the dependency being analyzed + * @param engine the engine doing the analysis + * @param scanDepth the current scan depth; extracctAndAnalyze is recursive + * and will, be default, only go 3 levels deep + * @throws AnalysisException thrown if there is a problem analyzing the + * dependencies + */ + private void extractAndAnalyze(Dependency dependency, Engine engine, int scanDepth) throws AnalysisException { final File f = new File(dependency.getActualFilePath()); final File tmpDir = getNextTempDirectory(); extractFiles(f, tmpDir, engine); @@ -261,14 +277,12 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { d.getFileName()); d.setFilePath(displayPath); d.setFileName(displayName); - d.setProjectReferences(dependency.getProjectReferences()); + d.addAllProjectReferences(dependency.getProjectReferences()); //TODO - can we get more evidence from the parent? EAR contains module name, etc. //analyze the dependency (i.e. extract files) if it is a supported type. - if (this.accept(d.getActualFile()) && scanDepth < MAX_SCAN_DEPTH) { - scanDepth += 1; - analyze(d, engine); - scanDepth -= 1; + if (this.accept(d.getActualFile()) && scanDepth < maxScanDepth) { + extractAndAnalyze(d, engine, scanDepth + 1); } } else { for (Dependency sub : dependencySet) { @@ -288,9 +302,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { } if (REMOVE_FROM_ANALYSIS.accept(dependency.getActualFile())) { addDisguisedJarsToDependencies(dependency, engine); - engine.getDependencies().remove(dependency); + engine.removeDependency(dependency); } - Collections.sort(engine.getDependencies()); } /** @@ -357,8 +370,7 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { * @throws AnalysisException thrown if unable to create temporary directory */ private File getNextTempDirectory() throws AnalysisException { - dirCount += 1; - final File directory = new File(tempFileLocation, String.valueOf(dirCount)); + final File directory = new File(tempFileLocation, String.valueOf(DIRECTORY_COUNT.incrementAndGet())); //getting an exception for some directories not being able to be created; might be because the directory already exists? if (directory.exists()) { return getNextTempDirectory(); @@ -603,4 +615,19 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer { } return isJar; } + + /** + * Initializes settings used by the scanning functions of the archive + * analyzer. + */ + private void initializeSettings() { + maxScanDepth = getSettings().getInt("archive.scan.depth", 3); + final String additionalZipExt = getSettings().getString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS); + if (additionalZipExt != null) { + final String[] ext = additionalZipExt.split("\\s*,\\s*"); + Collections.addAll(KNOWN_ZIP_EXT, ext); + } + EXTENSIONS.addAll(KNOWN_ZIP_EXT); + fileFilter = FileFilterBuilder.newInstance().addExtensions(EXTENSIONS).build(); + } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java index d0b53be6f..e4bbf9595 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzer.java @@ -28,7 +28,6 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.Evidence; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.Settings; @@ -43,9 +42,11 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; import javax.xml.parsers.ParserConfigurationException; import org.owasp.dependencycheck.exception.InitializationException; import org.apache.commons.lang3.SystemUtils; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.XmlUtils; /** @@ -55,6 +56,7 @@ import org.owasp.dependencycheck.utils.XmlUtils; * @author colezlaw * */ +@ThreadSafe public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -91,8 +93,8 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer { // Use file.separator as a wild guess as to whether this is Windows final List args = new ArrayList<>(); if (!SystemUtils.IS_OS_WINDOWS) { - if (Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) { - args.add(Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH)); + if (getSettings().getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH) != null) { + args.add(getSettings().getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH)); } else if (isInPath("mono")) { args.add("mono"); } else { @@ -111,20 +113,16 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer { * @throws AnalysisException if anything goes sideways */ @Override - public void analyzeDependency(Dependency dependency, Engine engine) - throws AnalysisException { - + public void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { final File test = new File(dependency.getActualFilePath()); if (!test.isFile()) { throw new AnalysisException(String.format("%s does not exist and cannot be analyzed by dependency-check", dependency.getActualFilePath())); } - if (grokAssemblyExe == null) { LOGGER.warn("GrokAssembly didn't get deployed"); return; } - final List args = buildArgumentList(); if (args == null) { LOGGER.warn("Assembly Analyzer was unable to execute"); @@ -172,20 +170,17 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer { final String version = xpath.evaluate("/assembly/version", doc); if (version != null) { - dependency.getVersionEvidence().addEvidence(new Evidence("grokassembly", "version", - version, Confidence.HIGHEST)); + dependency.addEvidence(EvidenceType.VERSION, "grokassembly", "version", version, Confidence.HIGHEST); } final String vendor = xpath.evaluate("/assembly/company", doc); if (vendor != null) { - dependency.getVendorEvidence().addEvidence(new Evidence("grokassembly", "vendor", - vendor, Confidence.HIGH)); + dependency.addEvidence(EvidenceType.VENDOR, "grokassembly", "vendor", vendor, Confidence.HIGH); } final String product = xpath.evaluate("/assembly/product", doc); if (product != null) { - dependency.getProductEvidence().addEvidence(new Evidence("grokassembly", "product", - product, Confidence.HIGH)); + dependency.addEvidence(EvidenceType.PRODUCT, "grokassembly", "product", product, Confidence.HIGH); } } catch (ParserConfigurationException pce) { @@ -199,22 +194,21 @@ public class AssemblyAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.error("----------------------------------------------------"); throw new AnalysisException("Couldn't parse Assembly Analyzer results (GrokAssembly)", saxe); } - // This shouldn't happen - } /** * Initialize the analyzer. In this case, extract GrokAssembly.exe to a * temporary location. * + * @param engine a reference to the dependency-check engine * @throws InitializationException thrown if anything goes wrong */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { final File tempFile; final File cfgFile; try { - tempFile = File.createTempFile("GKA", ".exe", Settings.getTempDirectory()); + tempFile = File.createTempFile("GKA", ".exe", getSettings().getTempDirectory()); cfgFile = new File(tempFile.getPath() + ".config"); } catch (IOException ex) { setEnabled(false); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzer.java index 88beaa168..1f85effc3 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzer.java @@ -22,7 +22,6 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.UrlStringUtils; @@ -33,6 +32,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; /** @@ -168,15 +168,14 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer { final String contents = getFileContents(actualFile); if (!contents.isEmpty()) { if (isOutputScript) { - extractConfigureScriptEvidence(dependency, name, - contents); + extractConfigureScriptEvidence(dependency, name, contents); } else { gatherEvidence(dependency, name, contents); } } } } else { - engine.getDependencies().remove(dependency); + engine.removeDependency(dependency); } } @@ -195,17 +194,13 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer { final String value = matcher.group(2); if (!value.isEmpty()) { if (variable.endsWith("NAME")) { - dependency.getProductEvidence().addEvidence(name, variable, - value, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.PRODUCT, name, variable, value, Confidence.HIGHEST); } else if ("VERSION".equals(variable)) { - dependency.getVersionEvidence().addEvidence(name, variable, - value, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, name, variable, value, Confidence.HIGHEST); } else if ("BUGREPORT".equals(variable)) { - dependency.getVendorEvidence().addEvidence(name, variable, - value, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, name, variable, value, Confidence.HIGH); } else if ("URL".equals(variable)) { - dependency.getVendorEvidence().addEvidence(name, variable, - value, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, name, variable, value, Confidence.HIGH); } } } @@ -239,27 +234,19 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer { String contents) { final Matcher matcher = AC_INIT_PATTERN.matcher(contents); if (matcher.find()) { - final EvidenceCollection productEvidence = dependency - .getProductEvidence(); - productEvidence.addEvidence(name, "Package", matcher.group(1), - Confidence.HIGHEST); - dependency.getVersionEvidence().addEvidence(name, - "Package Version", matcher.group(2), Confidence.HIGHEST); - final EvidenceCollection vendorEvidence = dependency - .getVendorEvidence(); + dependency.addEvidence(EvidenceType.PRODUCT, name, "Package", matcher.group(1), Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, name, "Package Version", matcher.group(2), Confidence.HIGHEST); + if (null != matcher.group(3)) { - vendorEvidence.addEvidence(name, "Bug report address", - matcher.group(4), Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, name, "Bug report address", matcher.group(4), Confidence.HIGH); } if (null != matcher.group(5)) { - productEvidence.addEvidence(name, "Tarname", matcher.group(6), - Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, name, "Tarname", matcher.group(6), Confidence.HIGH); } if (null != matcher.group(7)) { final String url = matcher.group(8); if (UrlStringUtils.isUrl(url)) { - vendorEvidence.addEvidence(name, "URL", url, - Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, name, "URL", url, Confidence.HIGH); } } } @@ -268,11 +255,12 @@ public class AutoconfAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the file type analyzer. * + * @param engine a reference to the dependency-check engine * @throws InitializationException thrown if there is an exception during * initialization */ @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { // No initialization needed. } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzer.java index f24a468c4..303180dfa 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzer.java @@ -38,6 +38,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; /** @@ -125,11 +126,12 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the analyzer. * + * @param engine a reference to the dependency-check engine * @throws InitializationException thrown if an exception occurs getting an * instance of SHA1 */ @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { try { getSha1MessageDigest(); } catch (IllegalStateException ex) { @@ -171,8 +173,8 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer { m.groupCount(), m.group(0))); final String group = m.group(1); LOGGER.debug("Group 1: {}", group); - dependency.getProductEvidence().addEvidence(name, "Project", - group, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, name, "Project", group, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, name, "Project", group, Confidence.HIGH); } LOGGER.debug("Found {} matches.", count); analyzeSetVersionCommand(dependency, engine, contents); @@ -223,13 +225,12 @@ public class CMakeAnalyzer extends AbstractFileTypeAnalyzer { } final MessageDigest sha1 = getSha1MessageDigest(); currentDep.setSha1sum(Checksum.getHex(sha1.digest(path))); - engine.getDependencies().add(currentDep); + engine.addDependency(currentDep); } final String source = currentDep.getDisplayFileName(); - currentDep.getProductEvidence().addEvidence(source, "Product", - product, Confidence.MEDIUM); - currentDep.getVersionEvidence().addEvidence(source, "Version", - version, Confidence.MEDIUM); + currentDep.addEvidence(EvidenceType.PRODUCT, source, "Product", product, Confidence.MEDIUM); + currentDep.addEvidence(EvidenceType.VENDOR, source, "Vendor", product, Confidence.MEDIUM); + currentDep.addEvidence(EvidenceType.VERSION, source, "Version", version, Confidence.MEDIUM); } LOGGER.debug("Found {} matches.", count); } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java index c23ceccc4..ae2a55d35 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CPEAnalyzer.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.lucene.analysis.util.CharArraySet; import org.apache.lucene.document.Document; @@ -46,7 +47,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Evidence; -import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.VulnerableSoftware; import org.owasp.dependencycheck.exception.InitializationException; @@ -63,6 +64,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class CPEAnalyzer extends AbstractAnalyzer { /** @@ -92,6 +94,10 @@ public class CPEAnalyzer extends AbstractAnalyzer { * data that will be written into the string. */ private static final int STRING_BUILDER_BUFFER = 20; + /** + * The URL to perform a search of the NVD CVE data at NIST. + */ + public static final String NVD_SEARCH_URL = "https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=%s"; /** * The CPE in memory index. */ @@ -101,11 +107,6 @@ public class CPEAnalyzer extends AbstractAnalyzer { */ private CveDB cve; - /** - * The URL to perform a search of the NVD CVE data at NIST. - */ - public static final String NVD_SEARCH_URL = "https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=%s"; - /** * Returns the name of this analyzer. * @@ -126,26 +127,17 @@ public class CPEAnalyzer extends AbstractAnalyzer { return AnalysisPhase.IDENTIFIER_ANALYSIS; } - /** - * The default is to support parallel processing. - * - * @return false - */ - @Override - public boolean supportsParallelProcessing() { - return false; - } - /** * Creates the CPE Lucene Index. * + * @param engine a reference to the dependency-check engine * @throws InitializationException is thrown if there is an issue opening * the index. */ @Override - public void initializeAnalyzer() throws InitializationException { + public void prepareAnalyzer(Engine engine) throws InitializationException { try { - this.open(); + this.open(engine.getDatabase()); } catch (IOException ex) { LOGGER.debug("Exception initializing the Lucene Index", ex); throw new InitializationException("An exception occurred initializing the Lucene Index", ex); @@ -158,24 +150,23 @@ public class CPEAnalyzer extends AbstractAnalyzer { /** * Opens the data source. * + * @param cve a reference to the NVD CVE database * @throws IOException when the Lucene directory to be queried does not * exist or is corrupt. * @throws DatabaseException when the database throws an exception. This * usually occurs when the database is in use by another process. */ - public void open() throws IOException, DatabaseException { - if (!isOpen()) { - cve = CveDB.getInstance(); - cpe = CpeMemoryIndex.getInstance(); - try { - final long creationStart = System.currentTimeMillis(); - cpe.open(cve); - final long creationSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - creationStart); - LOGGER.info("Created CPE Index ({} seconds)", creationSeconds); - } catch (IndexException ex) { - LOGGER.debug("IndexException", ex); - throw new DatabaseException(ex); - } + public void open(CveDB cve) throws IOException, DatabaseException { + this.cve = cve; + this.cpe = CpeMemoryIndex.getInstance(); + try { + final long creationStart = System.currentTimeMillis(); + cpe.open(cve); + final long creationSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - creationStart); + LOGGER.info("Created CPE Index ({} seconds)", creationSeconds); + } catch (IndexException ex) { + LOGGER.debug("IndexException", ex); + throw new DatabaseException(ex); } } @@ -184,25 +175,12 @@ public class CPEAnalyzer extends AbstractAnalyzer { */ @Override public void closeAnalyzer() { - if (cve != null) { - cve.close(); - cve = null; - } if (cpe != null) { cpe.close(); cpe = null; } } - /** - * Returns whether or not the analyzer is open. - * - * @return true if the analyzer is open - */ - public boolean isOpen() { - return cpe != null && cpe.isOpen(); - } - /** * Searches the data store of CPE entries, trying to identify the CPE for * the given dependency based on the evidence contained within. The @@ -217,17 +195,17 @@ public class CPEAnalyzer extends AbstractAnalyzer { String vendors = ""; String products = ""; for (Confidence confidence : Confidence.values()) { - if (dependency.getVendorEvidence().contains(confidence)) { - vendors = addEvidenceWithoutDuplicateTerms(vendors, dependency.getVendorEvidence(), confidence); + if (dependency.contains(EvidenceType.VENDOR, confidence)) { + vendors = addEvidenceWithoutDuplicateTerms(vendors, dependency.getIterator(EvidenceType.VENDOR, confidence)); LOGGER.debug("vendor search: {}", vendors); } - if (dependency.getProductEvidence().contains(confidence)) { - products = addEvidenceWithoutDuplicateTerms(products, dependency.getProductEvidence(), confidence); + if (dependency.contains(EvidenceType.PRODUCT, confidence)) { + products = addEvidenceWithoutDuplicateTerms(products, dependency.getIterator(EvidenceType.PRODUCT, confidence)); LOGGER.debug("product search: {}", products); } if (!vendors.isEmpty() && !products.isEmpty()) { - final List entries = searchCPE(vendors, products, dependency.getVendorEvidence().getWeighting(), - dependency.getProductEvidence().getWeighting()); + final List entries = searchCPE(vendors, products, dependency.getVendorWeightings(), + dependency.getProductWeightings()); if (entries == null) { continue; } @@ -254,26 +232,24 @@ public class CPEAnalyzer extends AbstractAnalyzer { * attempts to prevent duplicate terms from being added.
Note, if * the evidence is longer then 200 characters it will be truncated. * - * @param text the base text. - * @param ec an EvidenceCollection - * @param confidenceFilter a Confidence level to filter the evidence by. + * @param text the base text + * @param evidence an iterable set of evidence to concatenate * @return the new evidence text */ - private String addEvidenceWithoutDuplicateTerms(final String text, final EvidenceCollection ec, Confidence confidenceFilter) { + private String addEvidenceWithoutDuplicateTerms(final String text, final Iterable evidence) { final String txt = (text == null) ? "" : text; - final StringBuilder sb = new StringBuilder(txt.length() + (20 * ec.size())); + final StringBuilder sb = new StringBuilder(); sb.append(' ').append(txt).append(' '); - for (Evidence e : ec.iterator(confidenceFilter)) { - String value = e.getValue(); - - //hack to get around the fact that lucene does a really good job of recognizing domains and not - // splitting them. TODO - put together a better lucene analyzer specific to the domain. - if (value.startsWith("http://")) { - value = value.substring(7).replaceAll("\\.", " "); - } - if (value.startsWith("https://")) { - value = value.substring(8).replaceAll("\\.", " "); - } + for (Evidence e : evidence) { + final String value = e.getValue(); + //removed as the URLTokenizingFilter was created + //hack to get around the fact that lucene does a really good job of recognizing domains and not splitting them. +// if (value.startsWith("http://")) { +// value = value.substring(7).replaceAll("\\.", " "); +// } +// if (value.startsWith("https://")) { +// value = value.substring(8).replaceAll("\\.", " "); +// } if (sb.indexOf(" " + value + " ") < 0) { sb.append(value).append(' '); } @@ -466,8 +442,8 @@ public class CPEAnalyzer extends AbstractAnalyzer { //TODO - does this nullify some of the fuzzy matching that happens in the lucene search? // for instance CPE some-component and in the evidence we have SomeComponent. - if (collectionContainsString(dependency.getProductEvidence(), entry.getProduct()) - && collectionContainsString(dependency.getVendorEvidence(), entry.getVendor())) { + if (collectionContainsString(dependency.getEvidence(EvidenceType.PRODUCT), entry.getProduct()) + && collectionContainsString(dependency.getEvidence(EvidenceType.VENDOR), entry.getVendor())) { //&& collectionContainsVersion(dependency.getVersionEvidence(), entry.getVersion()) isValid = true; } @@ -477,11 +453,11 @@ public class CPEAnalyzer extends AbstractAnalyzer { /** * Used to determine if the EvidenceCollection contains a specific string. * - * @param ec an EvidenceCollection + * @param evidence an of evidence object to check * @param text the text to search for * @return whether or not the EvidenceCollection contains the string */ - private boolean collectionContainsString(EvidenceCollection ec, String text) { + private boolean collectionContainsString(Set evidence, String text) { //TODO - likely need to change the split... not sure if this will work for CPE with special chars if (text == null) { return false; @@ -489,7 +465,7 @@ public class CPEAnalyzer extends AbstractAnalyzer { final String[] words = text.split("[\\s_-]"); final List list = new ArrayList<>(); String tempWord = null; - CharArraySet stopWords = SearchFieldAnalyzer.getStopWords(); + final CharArraySet stopWords = SearchFieldAnalyzer.getStopWords(); for (String word : words) { if (stopWords.contains(word)) { continue; @@ -518,11 +494,24 @@ public class CPEAnalyzer extends AbstractAnalyzer { if (list.isEmpty()) { return false; } - boolean contains = true; + boolean isValid = true; for (String word : list) { - contains &= ec.containsUsedString(word); + boolean found = false; + for (Evidence e : evidence) { + if (e.getValue().toLowerCase().contains(word.toLowerCase())) { + if ("http".equals(word) && e.getValue().contains("http:")) { + continue; + } + found = true; + break; + } + } + isValid &= found; + if (!isValid) { + break; + } } - return contains; + return isValid; } /** @@ -535,7 +524,7 @@ public class CPEAnalyzer extends AbstractAnalyzer { * dependency. */ @Override - protected synchronized void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { try { determineCPE(dependency); } catch (CorruptIndexException ex) { @@ -578,7 +567,7 @@ public class CPEAnalyzer extends AbstractAnalyzer { // if there lower confidence evidence when the current (highest) version number // is newer then anything in the NVD. for (Confidence conf : Confidence.values()) { - for (Evidence evidence : dependency.getVersionEvidence().iterator(conf)) { + for (Evidence evidence : dependency.getIterator(EvidenceType.VERSION, conf)) { final DependencyVersion evVer = DependencyVersionUtil.parseVersion(evidence.getValue()); if (evVer == null) { continue; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CentralAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CentralAnalyzer.java index c38070e30..55d267832 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CentralAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CentralAnalyzer.java @@ -36,6 +36,8 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.DownloadFailedException; import org.owasp.dependencycheck.utils.Downloader; @@ -49,6 +51,7 @@ import org.owasp.dependencycheck.utils.Settings; * * @author colezlaw */ +@ThreadSafe public class CentralAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -84,7 +87,18 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer { /** * Field indicating if the analyzer is enabled. */ - private final boolean enabled = checkEnabled(); + private boolean enabled = true; + + /** + * Initializes the analyzer with the configured settings. + * + * @param settings the configured settings to use + */ + @Override + public void initialize(Settings settings) { + super.initialize(settings); + enabled = checkEnabled(); + } /** * Determine whether to enable this analyzer or not. @@ -106,9 +120,9 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer { boolean retVal = false; try { - if (Settings.getBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED)) { - if (!Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED) - || NexusAnalyzer.DEFAULT_URL.equals(Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL))) { + if (getSettings().getBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED)) { + if (!getSettings().getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED) + || NexusAnalyzer.DEFAULT_URL.equals(getSettings().getString(Settings.KEYS.ANALYZER_NEXUS_URL))) { LOGGER.debug("Enabling the Central analyzer"); retVal = true; } else { @@ -126,20 +140,19 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the analyzer once before any analysis is performed. * + * @param engine a reference to the dependency-check engine * @throws InitializationException if there's an error during initialization */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { LOGGER.debug("Initializing Central analyzer"); LOGGER.debug("Central analyzer enabled: {}", isEnabled()); if (isEnabled()) { - final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL); - LOGGER.debug("Central Analyzer URL: {}", searchUrl); try { - searcher = new CentralSearch(new URL(searchUrl)); + searcher = new CentralSearch(getSettings()); } catch (MalformedURLException ex) { setEnabled(false); - throw new InitializationException("The configured URL to Maven Central is malformed: " + searchUrl, ex); + throw new InitializationException("The configured URL to Maven Central is malformed", ex); } } } @@ -205,7 +218,7 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.debug("Central analyzer found artifact ({}) for dependency ({})", ma, dependency.getFileName()); dependency.addAsEvidence("central", ma, confidence); boolean pomAnalyzed = false; - for (Evidence e : dependency.getVendorEvidence()) { + for (Evidence e : dependency.getEvidence(EvidenceType.VENDOR)) { if ("pom".equals(e.getSource())) { pomAnalyzed = true; break; @@ -214,7 +227,7 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer { if (!pomAnalyzed && ma.getPomUrl() != null) { File pomFile = null; try { - final File baseDir = Settings.getTempDirectory(); + final File baseDir = getSettings().getTempDirectory(); pomFile = File.createTempFile("pom", ".xml", baseDir); if (!pomFile.delete()) { LOGGER.warn("Unable to fetch pom.xml for {} from Central; " @@ -222,7 +235,8 @@ public class CentralAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.debug("Unable to delete temp file"); } LOGGER.debug("Downloading {}", ma.getPomUrl()); - Downloader.fetchFile(new URL(ma.getPomUrl()), pomFile); + final Downloader downloader = new Downloader(getSettings()); + downloader.fetchFile(new URL(ma.getPomUrl()), pomFile); PomUtils.analyzePOM(dependency, pomFile); } catch (DownloadFailedException ex) { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java index a8a33121e..1d2d3a476 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CocoaPodsAnalyzer.java @@ -23,13 +23,14 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.io.FileUtils; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; @@ -41,6 +42,7 @@ import org.owasp.dependencycheck.utils.Settings; * @author Bianca Jiang (https://twitter.com/biancajiang) */ @Experimental +@ThreadSafe public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -83,7 +85,7 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { } @Override - protected void initializeFileTypeAnalyzer() { + protected void prepareFileTypeAnalyzer(Engine engine) { // NO-OP } @@ -134,21 +136,34 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { contents = contents.substring(matcher.end()); final String blockVariable = matcher.group(1); - final EvidenceCollection vendor = dependency.getVendorEvidence(); - final EvidenceCollection product = dependency.getProductEvidence(); - final EvidenceCollection version = dependency.getVersionEvidence(); - - final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST); + final String name = determineEvidence(contents, blockVariable, "name"); if (!name.isEmpty()) { - vendor.addEvidence(PODSPEC, "name_project", name, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.PRODUCT, PODSPEC, "name_project", name, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, PODSPEC, "name_project", name, Confidence.HIGHEST); + } + final String summary = determineEvidence(contents, blockVariable, "summary"); + if (!summary.isEmpty()) { + dependency.addEvidence(EvidenceType.PRODUCT, PODSPEC, "summary", summary, Confidence.HIGHEST); } - addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.HIGHEST); - addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST); - addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); - addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST); + final String author = determineEvidence(contents, blockVariable, "authors?"); + if (!author.isEmpty()) { + dependency.addEvidence(EvidenceType.VENDOR, PODSPEC, "author", author, Confidence.HIGHEST); + } + final String homepage = determineEvidence(contents, blockVariable, "homepage"); + if (!homepage.isEmpty()) { + dependency.addEvidence(EvidenceType.VENDOR, PODSPEC, "homepage", homepage, Confidence.HIGHEST); + } + final String license = determineEvidence(contents, blockVariable, "licen[cs]es?"); + if (!license.isEmpty()) { + dependency.addEvidence(EvidenceType.VENDOR, PODSPEC, "license", license, Confidence.HIGHEST); + } + + final String version = determineEvidence(contents, blockVariable, "version"); + if (!version.isEmpty()) { + dependency.addEvidence(EvidenceType.VERSION, PODSPEC, "version", version, Confidence.HIGHEST); + } - addStringEvidence(version, contents, blockVariable, "version", "version", Confidence.HIGHEST); } setPackagePath(dependency); @@ -158,16 +173,12 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { * Extracts evidence from the contents and adds it to the given evidence * collection. * - * @param evidences the evidence collection to update * @param contents the text to extract evidence from * @param blockVariable the block variable within the content to search for - * @param field the name of the field being searched for * @param fieldPattern the field pattern within the contents to search for - * @param confidence the confidence level of the evidence if found - * @return the string that was added as evidence + * @return the evidence */ - private String addStringEvidence(EvidenceCollection evidences, String contents, - String blockVariable, String field, String fieldPattern, Confidence confidence) { + private String determineEvidence(String contents, String blockVariable, String fieldPattern) { String value = ""; //capture array value between [ ] @@ -184,9 +195,6 @@ public class CocoaPodsAnalyzer extends AbstractFileTypeAnalyzer { value = matcher.group(2); } } - if (value.length() > 0) { - evidences.addEvidence(PODSPEC, field, value, confidence); - } return value; } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java index cccfeb010..5613b47e4 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzer.java @@ -37,6 +37,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Used to analyze a composer.lock file for a composer PHP app. @@ -79,11 +80,12 @@ public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the analyzer. * + * @param engine a reference to the dependency-check engine * @throws InitializationException thrown if an exception occurs getting an * instance of SHA1 */ @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { try { getSha1MessageDigest(); } catch (IllegalStateException ex) { @@ -112,11 +114,11 @@ public class ComposerLockAnalyzer extends AbstractFileTypeAnalyzer { final MessageDigest sha1 = getSha1MessageDigest(); d.setFilePath(filePath); d.setSha1sum(Checksum.getHex(sha1.digest(filePath.getBytes(Charset.defaultCharset())))); - d.getVendorEvidence().addEvidence(COMPOSER_LOCK, "vendor", dep.getGroup(), Confidence.HIGHEST); - d.getProductEvidence().addEvidence(COMPOSER_LOCK, "product", dep.getProject(), Confidence.HIGHEST); - d.getVersionEvidence().addEvidence(COMPOSER_LOCK, "version", dep.getVersion(), Confidence.HIGHEST); + d.addEvidence(EvidenceType.VENDOR, COMPOSER_LOCK, "vendor", dep.getGroup(), Confidence.HIGHEST); + d.addEvidence(EvidenceType.PRODUCT, COMPOSER_LOCK, "product", dep.getProject(), Confidence.HIGHEST); + d.addEvidence(EvidenceType.VERSION, COMPOSER_LOCK, "version", dep.getVersion(), Confidence.HIGHEST); LOGGER.info("Adding dependency {}", d); - engine.getDependencies().add(d); + engine.addDependency(d); } } catch (IOException ex) { LOGGER.warn("Error opening dependency {}", dependency.getActualFilePath()); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java index d563b7248..f243f0e9b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/CpeSuppressionAnalyzer.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.analyzer; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.utils.Settings; /** @@ -26,6 +27,7 @@ import org.owasp.dependencycheck.utils.Settings; * * @author Jeremy Long */ +@ThreadSafe public class CpeSuppressionAnalyzer extends AbstractSuppressionAnalyzer { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java index 21e3cc5e0..30125e419 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzer.java @@ -18,14 +18,10 @@ package org.owasp.dependencycheck.analyzer; import java.io.File; -import java.util.HashSet; -import java.util.Iterator; -import java.util.ListIterator; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.owasp.dependencycheck.Engine; -import org.owasp.dependencycheck.analyzer.exception.AnalysisException; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.utils.DependencyVersion; @@ -47,37 +43,19 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ -public class DependencyBundlingAnalyzer extends AbstractAnalyzer { +@ThreadSafe +public class DependencyBundlingAnalyzer extends AbstractDependencyComparingAnalyzer { /** * The Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(DependencyBundlingAnalyzer.class); - // /** * A pattern for obtaining the first part of a filename. */ private static final Pattern STARTING_TEXT_PATTERN = Pattern.compile("^[a-zA-Z0-9]*"); - /** - * a flag indicating if this analyzer has run. This analyzer only runs once. - */ - private boolean analyzed = false; - - /** - * Returns a flag indicating if this analyzer has run. This analyzer only - * runs once. Note this is currently only used in the unit tests. - * - * @return a flag indicating if this analyzer has run. This analyzer only - * runs once - */ - protected synchronized boolean getAnalyzed() { - return analyzed; - } - - // - // /** * The name of the analyzer. */ @@ -106,19 +84,6 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer { public AnalysisPhase getAnalysisPhase() { return ANALYSIS_PHASE; } - // - - /** - * Does not support parallel processing as it only runs once and then - * operates on all dependencies. - * - * @return whether or not parallel processing is enabled - * @see #analyze(Dependency, Engine) - */ - @Override - public boolean supportsParallelProcessing() { - return false; - } /** *

@@ -132,65 +97,46 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer { } /** - * Analyzes a set of dependencies. If they have been found to have the same - * base path and the same set of identifiers they are likely related. The - * related dependencies are bundled into a single reportable item. + * Evaluates the dependencies * - * @param ignore this analyzer ignores the dependency being analyzed - * @param engine the engine that is scanning the dependencies - * @throws AnalysisException is thrown if there is an error reading the JAR - * file. + * @param dependency a dependency to compare + * @param nextDependency a dependency to compare + * @param dependenciesToRemove a set of dependencies that will be removed + * @return true if a dependency is removed; otherwise false */ @Override - protected synchronized void analyzeDependency(Dependency ignore, Engine engine) throws AnalysisException { - if (!analyzed) { - analyzed = true; - final Set dependenciesToRemove = new HashSet<>(); - final ListIterator mainIterator = engine.getDependencies().listIterator(); - //for (Dependency nextDependency : engine.getDependencies()) { - while (mainIterator.hasNext()) { - final Dependency dependency = mainIterator.next(); - if (mainIterator.hasNext() && !dependenciesToRemove.contains(dependency)) { - final ListIterator subIterator = engine.getDependencies().listIterator(mainIterator.nextIndex()); - while (subIterator.hasNext()) { - final Dependency nextDependency = subIterator.next(); - if (hashesMatch(dependency, nextDependency)) { - if (!containedInWar(dependency.getFilePath()) - && !containedInWar(nextDependency.getFilePath())) { - if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) { - mergeDependencies(dependency, nextDependency, dependenciesToRemove); - } else { - mergeDependencies(nextDependency, dependency, dependenciesToRemove); - break; //since we merged into the next dependency - skip forward to the next in mainIterator - } - } - } else if (isShadedJar(dependency, nextDependency)) { - if (dependency.getFileName().toLowerCase().endsWith("pom.xml")) { - mergeDependencies(nextDependency, dependency, dependenciesToRemove); - nextDependency.getRelatedDependencies().remove(dependency); - break; - } else { - mergeDependencies(dependency, nextDependency, dependenciesToRemove); - dependency.getRelatedDependencies().remove(nextDependency); - } - } else if (cpeIdentifiersMatch(dependency, nextDependency) - && hasSameBasePath(dependency, nextDependency) - && vulnCountMatches(dependency, nextDependency) - && fileNameMatch(dependency, nextDependency)) { - if (isCore(dependency, nextDependency)) { - mergeDependencies(dependency, nextDependency, dependenciesToRemove); - } else { - mergeDependencies(nextDependency, dependency, dependenciesToRemove); - break; //since we merged into the next dependency - skip forward to the next in mainIterator - } - } - } + protected boolean evaluateDependencies(final Dependency dependency, final Dependency nextDependency, final Set dependenciesToRemove) { + if (hashesMatch(dependency, nextDependency)) { + if (!containedInWar(dependency.getFilePath()) + && !containedInWar(nextDependency.getFilePath())) { + if (firstPathIsShortest(dependency.getFilePath(), nextDependency.getFilePath())) { + mergeDependencies(dependency, nextDependency, dependenciesToRemove); + } else { + mergeDependencies(nextDependency, dependency, dependenciesToRemove); + return true; //since we merged into the next dependency - skip forward to the next in mainIterator } } - //removing dependencies here as ensuring correctness and avoiding ConcurrentUpdateExceptions - // was difficult because of the inner iterator. - engine.getDependencies().removeAll(dependenciesToRemove); + } else if (isShadedJar(dependency, nextDependency)) { + if (dependency.getFileName().toLowerCase().endsWith("pom.xml")) { + mergeDependencies(nextDependency, dependency, dependenciesToRemove); + nextDependency.removeRelatedDependencies(dependency); + return true; + } else { + mergeDependencies(dependency, nextDependency, dependenciesToRemove); + dependency.removeRelatedDependencies(nextDependency); + } + } else if (cpeIdentifiersMatch(dependency, nextDependency) + && hasSameBasePath(dependency, nextDependency) + && vulnCountMatches(dependency, nextDependency) + && fileNameMatch(dependency, nextDependency)) { + if (isCore(dependency, nextDependency)) { + mergeDependencies(dependency, nextDependency, dependenciesToRemove); + } else { + mergeDependencies(nextDependency, dependency, dependenciesToRemove); + return true; //since we merged into the next dependency - skip forward to the next in mainIterator + } } + return false; } /** @@ -205,10 +151,9 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer { */ private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set dependenciesToRemove) { dependency.addRelatedDependency(relatedDependency); - final Iterator i = relatedDependency.getRelatedDependencies().iterator(); - while (i.hasNext()) { - dependency.addRelatedDependency(i.next()); - i.remove(); + for (Dependency d : relatedDependency.getRelatedDependencies()) { + dependency.addRelatedDependency(d); + relatedDependency.removeRelatedDependencies(d); } if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) { dependency.addAllProjectReferences(relatedDependency.getProjectReferences()); @@ -239,7 +184,7 @@ public class DependencyBundlingAnalyzer extends AbstractAnalyzer { } //below is always true //if (tmp > 0) { - pos = tmp + 1; + pos = tmp + 1; //} tmp = path.indexOf(File.separator, pos); if (tmp > 0) { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyMergingAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyMergingAnalyzer.java index a2f88542e..49db7fd7c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyMergingAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/DependencyMergingAnalyzer.java @@ -18,13 +18,10 @@ package org.owasp.dependencycheck.analyzer; import java.io.File; -import java.util.HashSet; -import java.util.Iterator; -import java.util.ListIterator; import java.util.Set; -import org.owasp.dependencycheck.Engine; -import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,31 +33,12 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ -public class DependencyMergingAnalyzer extends AbstractAnalyzer { +public class DependencyMergingAnalyzer extends AbstractDependencyComparingAnalyzer { - // /** * The Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(DependencyMergingAnalyzer.class); - /** - * a flag indicating if this analyzer has run. This analyzer only runs once. - */ - private boolean analyzed = false; - - /** - * Returns a flag indicating if this analyzer has run. This analyzer only - * runs once. Note this is currently only used in the unit tests. - * - * @return a flag indicating if this analyzer has run. This analyzer only - * runs once - */ - protected synchronized boolean getAnalyzed() { - return analyzed; - } - - // - // /** * The name of the analyzer. */ @@ -90,18 +68,6 @@ public class DependencyMergingAnalyzer extends AbstractAnalyzer { return ANALYSIS_PHASE; } - /** - * Does not support parallel processing as it only runs once and then - * operates on all dependencies. - * - * @return whether or not parallel processing is enabled - * @see #analyze(Dependency, Engine) - */ - @Override - public boolean supportsParallelProcessing() { - return false; - } - /** *

* Returns the setting key to determine if the analyzer is enabled.

@@ -112,55 +78,34 @@ public class DependencyMergingAnalyzer extends AbstractAnalyzer { protected String getAnalyzerEnabledSettingKey() { return Settings.KEYS.ANALYZER_DEPENDENCY_MERGING_ENABLED; } - //
/** - * Analyzes a set of dependencies. If they have been found to be the same - * dependency created by more multiple FileTypeAnalyzers (i.e. a gemspec - * dependency and a dependency from the Bundle Audit Analyzer. The - * dependencies are then merged into a single reportable item. + * Evaluates the dependencies * - * @param ignore this analyzer ignores the dependency being analyzed - * @param engine the engine that is scanning the dependencies - * @throws AnalysisException is thrown if there is an error reading the JAR - * file. + * @param dependency a dependency to compare + * @param nextDependency a dependency to compare + * @param dependenciesToRemove a set of dependencies that will be removed + * @return true if a dependency is removed; otherwise false */ @Override - protected synchronized void analyzeDependency(Dependency ignore, Engine engine) throws AnalysisException { - if (!analyzed) { - analyzed = true; - final Set dependenciesToRemove = new HashSet<>(); - final ListIterator mainIterator = engine.getDependencies().listIterator(); - //for (Dependency nextDependency : engine.getDependencies()) { - while (mainIterator.hasNext()) { - final Dependency dependency = mainIterator.next(); - if (mainIterator.hasNext() && !dependenciesToRemove.contains(dependency)) { - final ListIterator subIterator = engine.getDependencies().listIterator(mainIterator.nextIndex()); - while (subIterator.hasNext()) { - final Dependency nextDependency = subIterator.next(); - Dependency main; - if ((main = getMainGemspecDependency(dependency, nextDependency)) != null) { - if (main == dependency) { - mergeDependencies(dependency, nextDependency, dependenciesToRemove); - } else { - mergeDependencies(nextDependency, dependency, dependenciesToRemove); - break; //since we merged into the next dependency - skip forward to the next in mainIterator - } - } else if ((main = getMainSwiftDependency(dependency, nextDependency)) != null) { - if (main == dependency) { - mergeDependencies(dependency, nextDependency, dependenciesToRemove); - } else { - mergeDependencies(nextDependency, dependency, dependenciesToRemove); - break; //since we merged into the next dependency - skip forward to the next in mainIterator - } - } - } - } + protected boolean evaluateDependencies(final Dependency dependency, final Dependency nextDependency, final Set dependenciesToRemove) { + Dependency main; + if ((main = getMainGemspecDependency(dependency, nextDependency)) != null) { + if (main == dependency) { + mergeDependencies(dependency, nextDependency, dependenciesToRemove); + } else { + mergeDependencies(nextDependency, dependency, dependenciesToRemove); + return true; //since we merged into the next dependency - skip forward to the next in mainIterator + } + } else if ((main = getMainSwiftDependency(dependency, nextDependency)) != null) { + if (main == dependency) { + mergeDependencies(dependency, nextDependency, dependenciesToRemove); + } else { + mergeDependencies(nextDependency, dependency, dependenciesToRemove); + return true; //since we merged into the next dependency - skip forward to the next in mainIterator } - //removing dependencies here as ensuring correctness and avoiding ConcurrentUpdateExceptions - // was difficult because of the inner iterator. - engine.getDependencies().removeAll(dependenciesToRemove); } + return false; } /** @@ -176,14 +121,19 @@ public class DependencyMergingAnalyzer extends AbstractAnalyzer { private void mergeDependencies(final Dependency dependency, final Dependency relatedDependency, final Set dependenciesToRemove) { LOGGER.debug("Merging '{}' into '{}'", relatedDependency.getFilePath(), dependency.getFilePath()); dependency.addRelatedDependency(relatedDependency); - dependency.getVendorEvidence().getEvidence().addAll(relatedDependency.getVendorEvidence().getEvidence()); - dependency.getProductEvidence().getEvidence().addAll(relatedDependency.getProductEvidence().getEvidence()); - dependency.getVersionEvidence().getEvidence().addAll(relatedDependency.getVersionEvidence().getEvidence()); + for (Evidence e : relatedDependency.getEvidence(EvidenceType.VENDOR)) { + dependency.addEvidence(EvidenceType.VENDOR, e); + } + for (Evidence e : relatedDependency.getEvidence(EvidenceType.PRODUCT)) { + dependency.addEvidence(EvidenceType.PRODUCT, e); + } + for (Evidence e : relatedDependency.getEvidence(EvidenceType.VERSION)) { + dependency.addEvidence(EvidenceType.VERSION, e); + } - final Iterator i = relatedDependency.getRelatedDependencies().iterator(); - while (i.hasNext()) { - dependency.addRelatedDependency(i.next()); - i.remove(); + for (Dependency d : relatedDependency.getRelatedDependencies()) { + dependency.addRelatedDependency(d); + relatedDependency.removeRelatedDependencies(d); } if (dependency.getSha1sum().equals(relatedDependency.getSha1sum())) { dependency.addAllProjectReferences(relatedDependency.getProjectReferences()); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java index 90390234c..54bc21a04 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzer.java @@ -22,15 +22,18 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.VulnerableSoftware; import org.owasp.dependencycheck.utils.FileFilterBuilder; @@ -44,6 +47,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class FalsePositiveAnalyzer extends AbstractAnalyzer { /** @@ -155,19 +159,19 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { } } } - if (mustContain - != null) { - final Iterator itr = dependency.getIdentifiers().iterator(); - while (itr.hasNext()) { - final Identifier i = itr.next(); + if (mustContain != null) { + final Set removalSet = new HashSet<>(); + for (Identifier i : dependency.getIdentifiers()) { if ("cpe".contains(i.getType()) && i.getValue() != null && i.getValue().startsWith("cpe:/a:springsource:") && !i.getValue().toLowerCase().contains(mustContain)) { - itr.remove(); - //dependency.getIdentifiers().remove(i); + removalSet.add(i); } } + for (Identifier i : removalSet) { + dependency.removeIdentifier(i); + } } } @@ -218,15 +222,15 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { //how did we get here? LOGGER.debug("currentVersion and nextVersion are both null?"); } else if (currentVersion == null && nextVersion != null) { - dependency.getIdentifiers().remove(currentId); + dependency.removeIdentifier(currentId); } else if (nextVersion == null && currentVersion != null) { - dependency.getIdentifiers().remove(nextId); + dependency.removeIdentifier(nextId); } else if (currentVersion.length() < nextVersion.length()) { if (nextVersion.startsWith(currentVersion) || "-".equals(currentVersion)) { - dependency.getIdentifiers().remove(currentId); + dependency.removeIdentifier(currentId); } } else if (currentVersion.startsWith(nextVersion) || "-".equals(nextVersion)) { - dependency.getIdentifiers().remove(nextId); + dependency.removeIdentifier(nextId); } } } @@ -241,21 +245,22 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { * @param dependency the dependency to remove JRE CPEs from */ private void removeJreEntries(Dependency dependency) { - final Set identifiers = dependency.getIdentifiers(); - final Iterator itr = identifiers.iterator(); - while (itr.hasNext()) { - final Identifier i = itr.next(); + final Set removalSet = new HashSet<>(); + for (Identifier i : dependency.getIdentifiers()) { final Matcher coreCPE = CORE_JAVA.matcher(i.getValue()); final Matcher coreFiles = CORE_FILES.matcher(dependency.getFileName()); if (coreCPE.matches() && !coreFiles.matches()) { - itr.remove(); + removalSet.add(i); } final Matcher coreJsfCPE = CORE_JAVA_JSF.matcher(i.getValue()); final Matcher coreJsfFiles = CORE_JSF_FILES.matcher(dependency.getFileName()); if (coreJsfCPE.matches() && !coreJsfFiles.matches()) { - itr.remove(); + removalSet.add(i); } } + for (Identifier i : removalSet) { + dependency.removeIdentifier(i); + } } /** @@ -286,9 +291,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { * * @param dependency the dependency to analyze */ - private void removeBadMatches(Dependency dependency) { - final Set identifiers = dependency.getIdentifiers(); - final Iterator itr = identifiers.iterator(); + protected void removeBadMatches(Dependency dependency) { /* TODO - can we utilize the pom's groupid and artifactId to filter??? most of * these are due to low quality data. Other idea would be to say any CPE @@ -297,8 +300,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { */ //Set groupId = dependency.getVendorEvidence().getEvidence("pom", "groupid"); //Set artifactId = dependency.getVendorEvidence().getEvidence("pom", "artifactid"); - while (itr.hasNext()) { - final Identifier i = itr.next(); + for (Identifier i : dependency.getIdentifiers()) { //TODO move this startsWith expression to the base suppression file if ("cpe".equals(i.getType())) { if ((i.getValue().matches(".*c\\+\\+.*") @@ -322,7 +324,8 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { || dependency.getFileName().toLowerCase().endsWith(".tgz") || dependency.getFileName().toLowerCase().endsWith(".ear") || dependency.getFileName().toLowerCase().endsWith(".war"))) { - itr.remove(); + //itr.remove(); + dependency.removeIdentifier(i); } else if ((i.getValue().startsWith("cpe:/a:jquery:jquery") || i.getValue().startsWith("cpe:/a:prototypejs:prototype") || i.getValue().startsWith("cpe:/a:yahoo:yui")) @@ -330,7 +333,8 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { || dependency.getFileName().toLowerCase().endsWith("pom.xml") || dependency.getFileName().toLowerCase().endsWith(".dll") || dependency.getFileName().toLowerCase().endsWith(".exe"))) { - itr.remove(); + //itr.remove(); + dependency.removeIdentifier(i); } else if ((i.getValue().startsWith("cpe:/a:microsoft:excel") || i.getValue().startsWith("cpe:/a:microsoft:word") || i.getValue().startsWith("cpe:/a:microsoft:visio") @@ -341,16 +345,36 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { || dependency.getFileName().toLowerCase().endsWith(".ear") || dependency.getFileName().toLowerCase().endsWith(".war") || dependency.getFileName().toLowerCase().endsWith("pom.xml"))) { - itr.remove(); + //itr.remove(); + dependency.removeIdentifier(i); } else if (i.getValue().startsWith("cpe:/a:apache:maven") && !dependency.getFileName().toLowerCase().matches("maven-core-[\\d\\.]+\\.jar")) { - itr.remove(); - } else if (i.getValue().startsWith("cpe:/a:m-core:m-core") - && !dependency.getEvidenceUsed().containsUsedString("m-core")) { - itr.remove(); + //itr.remove(); + dependency.removeIdentifier(i); + } else if (i.getValue().startsWith("cpe:/a:m-core:m-core")) { + boolean found = false; + for (Evidence e : dependency.getEvidence(EvidenceType.PRODUCT)) { + if ("m-core".equalsIgnoreCase(e.getValue())) { + found = true; + break; + } + } + if (!found) { + for (Evidence e : dependency.getEvidence(EvidenceType.VENDOR)) { + if ("m-core".equalsIgnoreCase(e.getValue())) { + found = true; + break; + } + } + } + if (!found) { + //itr.remove(); + dependency.removeIdentifier(i); + } } else if (i.getValue().startsWith("cpe:/a:jboss:jboss") && !dependency.getFileName().toLowerCase().matches("jboss-?[\\d\\.-]+(GA)?\\.jar")) { - itr.remove(); + //itr.remove(); + dependency.removeIdentifier(i); } } } @@ -363,31 +387,30 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { * @param dependency the dependency to analyze */ private void removeWrongVersionMatches(Dependency dependency) { - final Set identifiers = dependency.getIdentifiers(); - final Iterator itr = identifiers.iterator(); - + final Set identifiersToRemove = new HashSet<>(); final String fileName = dependency.getFileName(); if (fileName != null && fileName.contains("axis2")) { - while (itr.hasNext()) { - final Identifier i = itr.next(); + for (Identifier i : dependency.getIdentifiers()) { if ("cpe".equals(i.getType())) { final String cpe = i.getValue(); if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis:") || "cpe:/a:apache:axis".equals(cpe))) { - itr.remove(); + identifiersToRemove.add(i); } } } } else if (fileName != null && fileName.contains("axis")) { - while (itr.hasNext()) { - final Identifier i = itr.next(); + for (Identifier i : dependency.getIdentifiers()) { if ("cpe".equals(i.getType())) { final String cpe = i.getValue(); if (cpe != null && (cpe.startsWith("cpe:/a:apache:axis2:") || "cpe:/a:apache:axis2".equals(cpe))) { - itr.remove(); + identifiersToRemove.add(i); } } } } + for (Identifier i : identifiersToRemove) { + dependency.removeIdentifier(i); + } } /** @@ -411,17 +434,13 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { final String newCpe3 = String.format("cpe:/a:sun:opensso:%s", identifier.getValue().substring(22)); final String newCpe4 = String.format("cpe:/a:oracle:opensso:%s", identifier.getValue().substring(22)); try { - dependency.addIdentifier("cpe", - newCpe, + dependency.addIdentifier("cpe", newCpe, String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe, "UTF-8"))); - dependency.addIdentifier("cpe", - newCpe2, + dependency.addIdentifier("cpe", newCpe2, String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe2, "UTF-8"))); - dependency.addIdentifier("cpe", - newCpe3, + dependency.addIdentifier("cpe", newCpe3, String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe3, "UTF-8"))); - dependency.addIdentifier("cpe", - newCpe4, + dependency.addIdentifier("cpe", newCpe4, String.format(CPEAnalyzer.NVD_SEARCH_URL, URLEncoder.encode(newCpe4, "UTF-8"))); } catch (UnsupportedEncodingException ex) { LOGGER.debug("", ex); @@ -444,7 +463,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { String parentPath = dependency.getFilePath().toLowerCase(); if (parentPath.contains(".jar")) { parentPath = parentPath.substring(0, parentPath.indexOf(".jar") + 4); - final List dependencies = engine.getDependencies(); + final Dependency[] dependencies = engine.getDependencies(); final Dependency parent = findDependency(parentPath, dependencies); if (parent != null) { boolean remove = false; @@ -462,7 +481,7 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { } } if (remove) { - dependencies.remove(dependency); + engine.removeDependency(dependency); } } } @@ -474,10 +493,10 @@ public class FalsePositiveAnalyzer extends AbstractAnalyzer { * dependencies. * * @param dependencyPath the path of the dependency to return - * @param dependencies the collection of dependencies to search + * @param dependencies the array of dependencies to search * @return the dependency object for the given path, otherwise null */ - private Dependency findDependency(String dependencyPath, List dependencies) { + private Dependency findDependency(String dependencyPath, Dependency[] dependencies) { for (Dependency d : dependencies) { if (d.getFilePath().equalsIgnoreCase(dependencyPath)) { return d; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java index 709423512..8ada06d22 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzer.java @@ -18,6 +18,7 @@ package org.owasp.dependencycheck.analyzer; import java.io.File; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.filefilter.NameFileFilter; @@ -25,16 +26,17 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.DependencyVersion; import org.owasp.dependencycheck.utils.DependencyVersionUtil; import org.owasp.dependencycheck.utils.Settings; /** - * * Takes a dependency and analyzes the filename and determines the hashes. * * @author Jeremy Long */ +@ThreadSafe public class FileNameAnalyzer extends AbstractAnalyzer { /** @@ -76,6 +78,7 @@ public class FileNameAnalyzer extends AbstractAnalyzer { public AnalysisPhase getAnalysisPhase() { return ANALYSIS_PHASE; } + /** *

* Returns the setting key to determine if the analyzer is enabled.

@@ -111,21 +114,16 @@ public class FileNameAnalyzer extends AbstractAnalyzer { // a shade. This should hopefully correct for cases like log4j.jar or // struts2-core.jar if (version.getVersionParts() == null || version.getVersionParts().size() < 2) { - dependency.getVersionEvidence().addEvidence("file", "version", - version.toString(), Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.MEDIUM); } else { - dependency.getVersionEvidence().addEvidence("file", "version", - version.toString(), Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "file", "version", version.toString(), Confidence.HIGHEST); } - dependency.getVersionEvidence().addEvidence("file", "name", - packageName, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VERSION, "file", "name", packageName, Confidence.MEDIUM); } if (!IGNORED_FILES.accept(f)) { - dependency.getProductEvidence().addEvidence("file", "name", - packageName, Confidence.HIGH); - dependency.getVendorEvidence().addEvidence("file", "name", - packageName, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, "file", "name", packageName, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, "file", "name", packageName, Confidence.HIGH); } } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java index 29bf2a875..9f9f53967 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/HintAnalyzer.java @@ -26,10 +26,12 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.xml.suppression.PropertyType; import org.owasp.dependencycheck.utils.DownloadFailedException; @@ -40,7 +42,6 @@ import org.owasp.dependencycheck.xml.hints.VendorDuplicatingHintRule; import org.owasp.dependencycheck.xml.hints.HintParseException; import org.owasp.dependencycheck.xml.hints.HintParser; import org.owasp.dependencycheck.xml.hints.HintRule; -import org.owasp.dependencycheck.xml.hints.Hints; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; @@ -51,6 +52,7 @@ import org.xml.sax.SAXException; * * @author Jeremy Long */ +@ThreadSafe public class HintAnalyzer extends AbstractAnalyzer { /** @@ -62,11 +64,13 @@ public class HintAnalyzer extends AbstractAnalyzer { */ private static final String HINT_RULE_FILE_NAME = "dependencycheck-base-hint.xml"; /** - * The collection of hints. + * The array of hint rules. */ - private Hints hints; - - // + private HintRule[] hints = null; + /** + * The array of vendor duplicating hint rules. + */ + private VendorDuplicatingHintRule[] vendorHints; /** * The name of the analyzer. */ @@ -108,12 +112,13 @@ public class HintAnalyzer extends AbstractAnalyzer { } /** - * The initialize method does nothing for this Analyzer. + * The prepare method does nothing for this Analyzer. * + * @param engine a reference the dependency-check engine * @throws InitializationException thrown if there is an exception */ @Override - public void initializeAnalyzer() throws InitializationException { + public void prepareAnalyzer(Engine engine) throws InitializationException { try { loadHintRules(); } catch (HintParseException ex) { @@ -121,7 +126,6 @@ public class HintAnalyzer extends AbstractAnalyzer { throw new InitializationException("Unable to parse the hint file", ex); } } - // /** * The HintAnalyzer uses knowledge about a dependency to add additional @@ -134,17 +138,17 @@ public class HintAnalyzer extends AbstractAnalyzer { */ @Override protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { - for (HintRule hint : hints.getHintRules()) { + for (HintRule hint : hints) { boolean matchFound = false; for (Evidence given : hint.getGivenVendor()) { - if (dependency.getVendorEvidence().getEvidence().contains(given)) { + if (dependency.contains(EvidenceType.VENDOR, given)) { matchFound = true; break; } } if (!matchFound) { for (Evidence given : hint.getGivenProduct()) { - if (dependency.getProductEvidence().getEvidence().contains(given)) { + if (dependency.contains(EvidenceType.PRODUCT, given)) { matchFound = true; break; } @@ -152,7 +156,7 @@ public class HintAnalyzer extends AbstractAnalyzer { } if (!matchFound) { for (Evidence given : hint.getGivenVersion()) { - if (dependency.getVersionEvidence().getEvidence().contains(given)) { + if (dependency.contains(EvidenceType.VERSION, given)) { matchFound = true; break; } @@ -168,45 +172,45 @@ public class HintAnalyzer extends AbstractAnalyzer { } if (matchFound) { for (Evidence e : hint.getAddVendor()) { - dependency.getVendorEvidence().addEvidence(e); + dependency.addEvidence(EvidenceType.VENDOR, e); } for (Evidence e : hint.getAddProduct()) { - dependency.getProductEvidence().addEvidence(e); + dependency.addEvidence(EvidenceType.PRODUCT, e); } for (Evidence e : hint.getAddVersion()) { - dependency.getVersionEvidence().addEvidence(e); + dependency.addEvidence(EvidenceType.VERSION, e); } for (Evidence e : hint.getRemoveVendor()) { - if (dependency.getVendorEvidence().getEvidence().contains(e)) { - dependency.getVendorEvidence().getEvidence().remove(e); + if (dependency.contains(EvidenceType.VENDOR, e)) { + dependency.removeEvidence(EvidenceType.VENDOR, e); } } for (Evidence e : hint.getRemoveProduct()) { - if (dependency.getProductEvidence().getEvidence().contains(e)) { - dependency.getProductEvidence().getEvidence().remove(e); + if (dependency.contains(EvidenceType.PRODUCT, e)) { + dependency.removeEvidence(EvidenceType.PRODUCT, e); } } for (Evidence e : hint.getRemoveVersion()) { - if (dependency.getVersionEvidence().getEvidence().contains(e)) { - dependency.getVersionEvidence().getEvidence().remove(e); + if (dependency.contains(EvidenceType.VERSION, e)) { + dependency.removeEvidence(EvidenceType.VERSION, e); } } } } - final Iterator itr = dependency.getVendorEvidence().iterator(); + final Iterator itr = dependency.getEvidence(EvidenceType.VENDOR).iterator(); final List newEntries = new ArrayList<>(); while (itr.hasNext()) { final Evidence e = itr.next(); - for (VendorDuplicatingHintRule dhr : hints.getVendorDuplicatingHintRules()) { - if (dhr.getValue().equalsIgnoreCase(e.getValue(false))) { + for (VendorDuplicatingHintRule dhr : vendorHints) { + if (dhr.getValue().equalsIgnoreCase(e.getValue())) { newEntries.add(new Evidence(e.getSource() + " (hint)", e.getName(), dhr.getDuplicate(), e.getConfidence())); } } } for (Evidence e : newEntries) { - dependency.getVendorEvidence().addEvidence(e); + dependency.addEvidence(EvidenceType.VENDOR, e); } } @@ -216,71 +220,81 @@ public class HintAnalyzer extends AbstractAnalyzer { * @throws HintParseException thrown if the XML cannot be parsed. */ private void loadHintRules() throws HintParseException { + List localHints; + List localVendorHints; final HintParser parser = new HintParser(); File file = null; try { - hints = parser.parseHints(FileUtils.getResourceAsStream(HINT_RULE_FILE_NAME)); - } catch (HintParseException | SAXException ex) { - LOGGER.error("Unable to parse the base hint data file"); - LOGGER.debug("Unable to parse the base hint data file", ex); + parser.parseHints(FileUtils.getResourceAsStream(HINT_RULE_FILE_NAME)); + } catch (SAXException ex) { + throw new HintParseException("Error parsing hinits: " + ex.getMessage(), ex); } - final String filePath = Settings.getString(Settings.KEYS.HINTS_FILE); - if (filePath == null) { - return; - } - boolean deleteTempFile = false; - try { - final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE); - if (uriRx.matcher(filePath).matches()) { - deleteTempFile = true; - file = FileUtils.getTempFile("hint", "xml"); - final URL url = new URL(filePath); - try { - Downloader.fetchFile(url, file, false); - } catch (DownloadFailedException ex) { - Downloader.fetchFile(url, file, true); - } - } else { - file = new File(filePath); - if (!file.exists()) { - try (InputStream fromClasspath = FileUtils.getResourceAsStream(filePath)) { - if (fromClasspath != null) { - deleteTempFile = true; - file = FileUtils.getTempFile("hint", "xml"); - try { - org.apache.commons.io.FileUtils.copyInputStreamToFile(fromClasspath, file); - } catch (IOException ex) { - throw new HintParseException("Unable to locate hints file in classpath", ex); + localHints = parser.getHintRules(); + localVendorHints = parser.getVendorDuplicatingHintRules(); + + final String filePath = getSettings().getString(Settings.KEYS.HINTS_FILE); + if (filePath != null) { + boolean deleteTempFile = false; + try { + final Pattern uriRx = Pattern.compile("^(https?|file)\\:.*", Pattern.CASE_INSENSITIVE); + if (uriRx.matcher(filePath).matches()) { + deleteTempFile = true; + file = getSettings().getTempFile("hint", "xml"); + final URL url = new URL(filePath); + final Downloader downloader = new Downloader(getSettings()); + try { + downloader.fetchFile(url, file, false); + } catch (DownloadFailedException ex) { + downloader.fetchFile(url, file, true); + } + } else { + file = new File(filePath); + if (!file.exists()) { + try (InputStream fromClasspath = FileUtils.getResourceAsStream(filePath)) { + if (fromClasspath != null) { + deleteTempFile = true; + file = getSettings().getTempFile("hint", "xml"); + try { + org.apache.commons.io.FileUtils.copyInputStreamToFile(fromClasspath, file); + } catch (IOException ex) { + throw new HintParseException("Unable to locate hints file in classpath", ex); + } } } } } - } - if (file != null) { - try { - final Hints newHints = parser.parseHints(file); - hints.getHintRules().addAll(newHints.getHintRules()); - hints.getVendorDuplicatingHintRules().addAll(newHints.getVendorDuplicatingHintRules()); - LOGGER.debug("{} hint rules were loaded.", hints.getHintRules().size()); - LOGGER.debug("{} duplicating hint rules were loaded.", hints.getVendorDuplicatingHintRules().size()); - } catch (HintParseException ex) { - LOGGER.warn("Unable to parse hint rule xml file '{}'", file.getPath()); - LOGGER.warn(ex.getMessage()); - LOGGER.debug("", ex); - throw ex; + if (file != null) { + try { + parser.parseHints(file); + if (parser.getHintRules() != null && !parser.getHintRules().isEmpty()) { + localHints.addAll(parser.getHintRules()); + } + if (parser.getVendorDuplicatingHintRules() != null && !parser.getVendorDuplicatingHintRules().isEmpty()) { + localVendorHints.addAll(parser.getVendorDuplicatingHintRules()); + } + } catch (HintParseException ex) { + LOGGER.warn("Unable to parse hint rule xml file '{}'", file.getPath()); + LOGGER.warn(ex.getMessage()); + LOGGER.debug("", ex); + throw ex; + } + } + } catch (DownloadFailedException ex) { + throw new HintParseException("Unable to fetch the configured hint file", ex); + } catch (MalformedURLException ex) { + throw new HintParseException("Configured hint file has an invalid URL", ex); + } catch (IOException ex) { + throw new HintParseException("Unable to create temp file for hints", ex); + } finally { + if (deleteTempFile && file != null) { + FileUtils.delete(file); } } - } catch (DownloadFailedException ex) { - throw new HintParseException("Unable to fetch the configured hint file", ex); - } catch (MalformedURLException ex) { - throw new HintParseException("Configured hint file has an invalid URL", ex); - } catch (IOException ex) { - throw new HintParseException("Unable to create temp file for hints", ex); - } finally { - if (deleteTempFile && file != null) { - FileUtils.delete(file); - } } + hints = (HintRule[]) localHints.toArray(new HintRule[localHints.size()]); + vendorHints = (VendorDuplicatingHintRule[]) localVendorHints.toArray(new VendorDuplicatingHintRule[localVendorHints.size()]); + LOGGER.debug("{} hint rules were loaded.", hints.length); + LOGGER.debug("{} duplicating hint rules were loaded.", vendorHints.length); } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java index db54d1ab6..730af7ced 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/JarAnalyzer.java @@ -53,7 +53,7 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.FileUtils; @@ -251,7 +251,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { || fileName.endsWith("-doc.jar") || isMacOSMetaDataFile(dependency, engine)) || !isZipFile(dependency)) { - engine.getDependencies().remove(dependency); + engine.removeDependency(dependency); return; } final boolean hasManifest = parseManifest(dependency, classNames); @@ -264,13 +264,15 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { } /** - * Checks if the given dependency appears to be a macOS metadata file, returning true if its filename starts with a - * ._ prefix and if there is another dependency with the same filename minus the ._ prefix, otherwise it returns - * false. + * Checks if the given dependency appears to be a macOS metadata file, + * returning true if its filename starts with a ._ prefix and if there is + * another dependency with the same filename minus the ._ prefix, otherwise + * it returns false. * * @param dependency the dependency to check if it's a macOS metadata file - * @param engine the engine that is scanning the dependencies - * @return whether or not the given dependency appears to be a macOS metadata file + * @param engine the engine that is scanning the dependencies + * @return whether or not the given dependency appears to be a macOS + * metadata file */ private boolean isMacOSMetaDataFile(final Dependency dependency, final Engine engine) { final String fileName = Paths.get(dependency.getActualFilePath()).getFileName().toString(); @@ -278,17 +280,19 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { } /** - * Iterates through the given list of dependencies and returns true when it finds a dependency with a filename - * matching the given filename, otherwise returns false. + * Iterates through the given list of dependencies and returns true when it + * finds a dependency with a filename matching the given filename, otherwise + * returns false. * * @param dependencies the dependencies to search within - * @param fileName the filename to search for - * @return whether or not the given dependencies contain a dependency with the given filename + * @param fileName the filename to search for + * @return whether or not the given dependencies contain a dependency with + * the given filename */ - private boolean hasDependencyWithFilename(final List dependencies, final String fileName) { + private boolean hasDependencyWithFilename(final Dependency[] dependencies, final String fileName) { for (final Dependency dependency : dependencies) { if (Paths.get(dependency.getActualFilePath()).getFileName().toString().toLowerCase() - .equals(fileName.toLowerCase())) { + .equals(fileName.toLowerCase())) { return true; } } @@ -296,23 +300,25 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { } /** - * Attempts to read the first bytes of the given dependency (using its actual file path) and returns true if they - * match the expected first bytes of a zip file, which may be empty or spanned. If they don't match, or if the file - * could not be read, then it returns false. + * Attempts to read the first bytes of the given dependency (using its + * actual file path) and returns true if they match the expected first bytes + * of a zip file, which may be empty or spanned. If they don't match, or if + * the file could not be read, then it returns false. * * @param dependency the dependency to check if it's a zip file - * @return whether or not the given dependency appears to be a zip file from its first bytes + * @return whether or not the given dependency appears to be a zip file from + * its first bytes */ private boolean isZipFile(final Dependency dependency) { final byte[] buffer = new byte[4]; try (final FileInputStream fileInputStream = new FileInputStream(dependency.getActualFilePath())) { - fileInputStream.read(buffer); - if (Arrays.equals(buffer, ZIP_FIRST_BYTES) || Arrays.equals(buffer, ZIP_EMPTY_FIRST_BYTES) || - Arrays.equals(buffer, ZIP_SPANNED_FIRST_BYTES)) { + if (fileInputStream.read(buffer) > 0 + && (Arrays.equals(buffer, ZIP_FIRST_BYTES) + || Arrays.equals(buffer, ZIP_EMPTY_FIRST_BYTES) + || Arrays.equals(buffer, ZIP_SPANNED_FIRST_BYTES))) { return true; } - } - catch (Exception e) { + } catch (Exception e) { LOGGER.warn("Unable to check if '{}' is a zip file", dependency.getActualFilePath()); LOGGER.trace("", e); } @@ -381,7 +387,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { newDependency.setFileName(displayName); newDependency.setFilePath(displayPath); setPomEvidence(newDependency, pom, null); - engine.getDependencies().add(newDependency); + engine.addDependency(newDependency); } catch (AnalysisException ex) { LOGGER.warn("An error occurred while analyzing '{}'.", dependency.getActualFilePath()); LOGGER.trace("", ex); @@ -521,15 +527,15 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { if (groupid != null && !groupid.isEmpty()) { foundSomething = true; - dependency.getVendorEvidence().addEvidence("pom", "groupid", groupid, Confidence.HIGHEST); - dependency.getProductEvidence().addEvidence("pom", "groupid", groupid, Confidence.LOW); - addMatchingValues(classes, groupid, dependency.getVendorEvidence()); - addMatchingValues(classes, groupid, dependency.getProductEvidence()); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "groupid", groupid, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.PRODUCT, "pom", "groupid", groupid, Confidence.LOW); + addMatchingVendorValues(classes, groupid, dependency); + addMatchingProductValues(classes, groupid, dependency); if (parentGroupId != null && !parentGroupId.isEmpty() && !parentGroupId.equals(groupid)) { - dependency.getVendorEvidence().addEvidence("pom", "parent-groupid", parentGroupId, Confidence.MEDIUM); - dependency.getProductEvidence().addEvidence("pom", "parent-groupid", parentGroupId, Confidence.LOW); - addMatchingValues(classes, parentGroupId, dependency.getVendorEvidence()); - addMatchingValues(classes, parentGroupId, dependency.getProductEvidence()); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "parent-groupid", parentGroupId, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.PRODUCT, "pom", "parent-groupid", parentGroupId, Confidence.LOW); + addMatchingVendorValues(classes, parentGroupId, dependency); + addMatchingProductValues(classes, parentGroupId, dependency); } } else { addAsIdentifier = false; @@ -537,15 +543,15 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { if (artifactid != null && !artifactid.isEmpty()) { foundSomething = true; - dependency.getProductEvidence().addEvidence("pom", "artifactid", artifactid, Confidence.HIGHEST); - dependency.getVendorEvidence().addEvidence("pom", "artifactid", artifactid, Confidence.LOW); - addMatchingValues(classes, artifactid, dependency.getVendorEvidence()); - addMatchingValues(classes, artifactid, dependency.getProductEvidence()); + dependency.addEvidence(EvidenceType.PRODUCT, "pom", "artifactid", artifactid, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "artifactid", artifactid, Confidence.LOW); + addMatchingVendorValues(classes, artifactid, dependency); + addMatchingProductValues(classes, artifactid, dependency); if (parentArtifactId != null && !parentArtifactId.isEmpty() && !parentArtifactId.equals(artifactid)) { - dependency.getProductEvidence().addEvidence("pom", "parent-artifactid", parentArtifactId, Confidence.MEDIUM); - dependency.getVendorEvidence().addEvidence("pom", "parent-artifactid", parentArtifactId, Confidence.LOW); - addMatchingValues(classes, parentArtifactId, dependency.getVendorEvidence()); - addMatchingValues(classes, parentArtifactId, dependency.getProductEvidence()); + dependency.addEvidence(EvidenceType.PRODUCT, "pom", "parent-artifactid", parentArtifactId, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "parent-artifactid", parentArtifactId, Confidence.LOW); + addMatchingProductValues(classes, parentArtifactId, dependency); + addMatchingProductValues(classes, parentArtifactId, dependency); } } else { addAsIdentifier = false; @@ -553,9 +559,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { if (version != null && !version.isEmpty()) { foundSomething = true; - dependency.getVersionEvidence().addEvidence("pom", "version", version, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "pom", "version", version, Confidence.HIGHEST); if (parentVersion != null && !parentVersion.isEmpty() && !parentVersion.equals(version)) { - dependency.getVersionEvidence().addEvidence("pom", "parent-version", version, Confidence.LOW); + dependency.addEvidence(EvidenceType.VERSION, "pom", "parent-version", version, Confidence.LOW); } } else { addAsIdentifier = false; @@ -568,26 +574,26 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { // org name final String org = pom.getOrganization(); if (org != null && !org.isEmpty()) { - dependency.getVendorEvidence().addEvidence("pom", "organization name", org, Confidence.HIGH); - dependency.getProductEvidence().addEvidence("pom", "organization name", org, Confidence.LOW); - addMatchingValues(classes, org, dependency.getVendorEvidence()); - addMatchingValues(classes, org, dependency.getProductEvidence()); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "organization name", org, Confidence.HIGH); + dependency.addEvidence(EvidenceType.PRODUCT, "pom", "organization name", org, Confidence.LOW); + addMatchingVendorValues(classes, org, dependency); + addMatchingProductValues(classes, org, dependency); } // org name final String orgUrl = pom.getOrganizationUrl(); if (orgUrl != null && !orgUrl.isEmpty()) { - dependency.getVendorEvidence().addEvidence("pom", "organization url", orgUrl, Confidence.MEDIUM); - dependency.getProductEvidence().addEvidence("pom", "organization url", orgUrl, Confidence.LOW); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "organization url", orgUrl, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.PRODUCT, "pom", "organization url", orgUrl, Confidence.LOW); } //pom name final String pomName = pom.getName(); if (pomName != null && !pomName.isEmpty()) { foundSomething = true; - dependency.getProductEvidence().addEvidence("pom", "name", pomName, Confidence.HIGH); - dependency.getVendorEvidence().addEvidence("pom", "name", pomName, Confidence.HIGH); - addMatchingValues(classes, pomName, dependency.getVendorEvidence()); - addMatchingValues(classes, pomName, dependency.getProductEvidence()); + dependency.addEvidence(EvidenceType.PRODUCT, "pom", "name", pomName, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "name", pomName, Confidence.HIGH); + addMatchingVendorValues(classes, pomName, dependency); + addMatchingProductValues(classes, pomName, dependency); } //Description @@ -595,13 +601,13 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { if (description != null && !description.isEmpty() && !description.startsWith("POM was created by")) { foundSomething = true; final String trimmedDescription = addDescription(dependency, description, "pom", "description"); - addMatchingValues(classes, trimmedDescription, dependency.getVendorEvidence()); - addMatchingValues(classes, trimmedDescription, dependency.getProductEvidence()); + addMatchingVendorValues(classes, trimmedDescription, dependency); + addMatchingProductValues(classes, trimmedDescription, dependency); } final String projectURL = pom.getProjectURL(); if (projectURL != null && !projectURL.trim().isEmpty()) { - dependency.getVendorEvidence().addEvidence("pom", "url", projectURL, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, "pom", "url", projectURL, Confidence.HIGHEST); } extractLicense(pom, dependency); @@ -626,25 +632,24 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { analyzeFullyQualifiedClassNames(classNames, vendorIdentifiers, productIdentifiers); final int classCount = classNames.size(); - final EvidenceCollection vendor = dependency.getVendorEvidence(); - final EvidenceCollection product = dependency.getProductEvidence(); for (Map.Entry entry : vendorIdentifiers.entrySet()) { final float ratio = entry.getValue() / (float) classCount; if (ratio > 0.5) { - //TODO remove weighting - vendor.addWeighting(entry.getKey()); + //TODO remove weighting? + dependency.addVendorWeighting(entry.getKey()); if (addPackagesAsEvidence && entry.getKey().length() > 1) { - vendor.addEvidence("jar", "package name", entry.getKey(), Confidence.LOW); + dependency.addEvidence(EvidenceType.VENDOR, "jar", "package name", entry.getKey(), Confidence.LOW); } } } for (Map.Entry entry : productIdentifiers.entrySet()) { final float ratio = entry.getValue() / (float) classCount; if (ratio > 0.5) { - product.addWeighting(entry.getKey()); + //todo remove weighting + dependency.addProductWeighting(entry.getKey()); if (addPackagesAsEvidence && entry.getKey().length() > 1) { - product.addEvidence("jar", "package name", entry.getKey(), Confidence.LOW); + dependency.addEvidence(EvidenceType.PRODUCT, "jar", "package name", entry.getKey(), Confidence.LOW); } } } @@ -681,9 +686,6 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { } return false; } - final EvidenceCollection vendorEvidence = dependency.getVendorEvidence(); - final EvidenceCollection productEvidence = dependency.getProductEvidence(); - final EvidenceCollection versionEvidence = dependency.getVersionEvidence(); String source = "Manifest"; String specificationVersion = null; boolean hasImplementationVersion = false; @@ -695,39 +697,41 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { value = Jsoup.parse(value).text(); } if (IGNORE_VALUES.contains(value)) { + //noinspection UnnecessaryContinue continue; } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_TITLE.toString())) { foundSomething = true; - productEvidence.addEvidence(source, key, value, Confidence.HIGH); - addMatchingValues(classInformation, value, productEvidence); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, value, Confidence.HIGH); + addMatchingProductValues(classInformation, value, dependency); } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VERSION.toString())) { hasImplementationVersion = true; foundSomething = true; - versionEvidence.addEvidence(source, key, value, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VERSION, source, key, value, Confidence.HIGH); } else if ("specification-version".equalsIgnoreCase(key)) { specificationVersion = value; } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) { foundSomething = true; - vendorEvidence.addEvidence(source, key, value, Confidence.HIGH); - addMatchingValues(classInformation, value, vendorEvidence); + dependency.addEvidence(EvidenceType.VENDOR, source, key, value, Confidence.HIGH); + addMatchingVendorValues(classInformation, value, dependency); } else if (key.equalsIgnoreCase(IMPLEMENTATION_VENDOR_ID)) { foundSomething = true; - vendorEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, vendorEvidence); + dependency.addEvidence(EvidenceType.VENDOR, source, key, value, Confidence.MEDIUM); + addMatchingVendorValues(classInformation, value, dependency); } else if (key.equalsIgnoreCase(BUNDLE_DESCRIPTION)) { foundSomething = true; addDescription(dependency, value, "manifest", key); - addMatchingValues(classInformation, value, productEvidence); + addMatchingProductValues(classInformation, value, dependency); } else if (key.equalsIgnoreCase(BUNDLE_NAME)) { foundSomething = true; - productEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, productEvidence); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, value, Confidence.MEDIUM); + addMatchingProductValues(classInformation, value, dependency); // //the following caused false positives. // } else if (key.equalsIgnoreCase(BUNDLE_VENDOR)) { } else if (key.equalsIgnoreCase(BUNDLE_VERSION)) { foundSomething = true; - versionEvidence.addEvidence(source, key, value, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VERSION, source, key, value, Confidence.HIGH); } else if (key.equalsIgnoreCase(Attributes.Name.MAIN_CLASS.toString())) { + //noinspection UnnecessaryContinue continue; //skipping main class as if this has important information to add it will be added during class name analysis... } else { @@ -746,7 +750,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { foundSomething = true; if (key.contains("version")) { if (!key.contains("specification")) { - versionEvidence.addEvidence(source, key, value, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VERSION, source, key, value, Confidence.MEDIUM); } } else if ("build-id".equals(key)) { int pos = value.indexOf('('); @@ -757,37 +761,37 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { if (pos > 0) { value = value.substring(0, pos - 1); } - versionEvidence.addEvidence(source, key, value, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VERSION, source, key, value, Confidence.MEDIUM); } else if (key.contains("title")) { - productEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, productEvidence); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, value, Confidence.MEDIUM); + addMatchingProductValues(classInformation, value, dependency); } else if (key.contains("vendor")) { if (key.contains("specification")) { - vendorEvidence.addEvidence(source, key, value, Confidence.LOW); + dependency.addEvidence(EvidenceType.VENDOR, source, key, value, Confidence.LOW); } else { - vendorEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, vendorEvidence); + dependency.addEvidence(EvidenceType.VENDOR, source, key, value, Confidence.MEDIUM); + addMatchingVendorValues(classInformation, value, dependency); } } else if (key.contains("name")) { - productEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - vendorEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, vendorEvidence); - addMatchingValues(classInformation, value, productEvidence); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, value, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VENDOR, source, key, value, Confidence.MEDIUM); + addMatchingVendorValues(classInformation, value, dependency); + addMatchingProductValues(classInformation, value, dependency); } else if (key.contains("license")) { addLicense(dependency, value); } else if (key.contains("description")) { addDescription(dependency, value, "manifest", key); } else { - productEvidence.addEvidence(source, key, value, Confidence.LOW); - vendorEvidence.addEvidence(source, key, value, Confidence.LOW); - addMatchingValues(classInformation, value, vendorEvidence); - addMatchingValues(classInformation, value, productEvidence); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, value, Confidence.LOW); + dependency.addEvidence(EvidenceType.VENDOR, source, key, value, Confidence.LOW); + addMatchingVendorValues(classInformation, value, dependency); + addMatchingProductValues(classInformation, value, dependency); if (value.matches(".*\\d.*")) { final StringTokenizer tokenizer = new StringTokenizer(value, " "); while (tokenizer.hasMoreElements()) { final String s = tokenizer.nextToken(); if (s.matches("^[0-9.]+$")) { - versionEvidence.addEvidence(source, key, s, Confidence.LOW); + dependency.addEvidence(EvidenceType.VERSION, source, key, s, Confidence.LOW); } } } @@ -804,25 +808,25 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { final String value = atts.getValue(key); if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_TITLE.toString())) { foundSomething = true; - productEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, productEvidence); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, value, Confidence.MEDIUM); + addMatchingProductValues(classInformation, value, dependency); } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VERSION.toString())) { foundSomething = true; - versionEvidence.addEvidence(source, key, value, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VERSION, source, key, value, Confidence.MEDIUM); } else if (key.equalsIgnoreCase(Attributes.Name.IMPLEMENTATION_VENDOR.toString())) { foundSomething = true; - vendorEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, vendorEvidence); + dependency.addEvidence(EvidenceType.VENDOR, source, key, value, Confidence.MEDIUM); + addMatchingVendorValues(classInformation, value, dependency); } else if (key.equalsIgnoreCase(Attributes.Name.SPECIFICATION_TITLE.toString())) { foundSomething = true; - productEvidence.addEvidence(source, key, value, Confidence.MEDIUM); - addMatchingValues(classInformation, value, productEvidence); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, value, Confidence.MEDIUM); + addMatchingProductValues(classInformation, value, dependency); } } } if (specificationVersion != null && !hasImplementationVersion) { foundSomething = true; - versionEvidence.addEvidence(source, "specification-version", specificationVersion, Confidence.HIGH); + dependency.addEvidence(EvidenceType.VERSION, source, "specification-version", specificationVersion, Confidence.HIGH); } } return foundSomething; @@ -880,11 +884,11 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { if (pos > 0) { desc = desc.substring(0, pos) + "..."; } - dependency.getProductEvidence().addEvidence(source, key, desc, Confidence.LOW); - dependency.getVendorEvidence().addEvidence(source, key, desc, Confidence.LOW); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, desc, Confidence.LOW); + dependency.addEvidence(EvidenceType.VENDOR, source, key, desc, Confidence.LOW); } else { - dependency.getProductEvidence().addEvidence(source, key, desc, Confidence.MEDIUM); - dependency.getVendorEvidence().addEvidence(source, key, desc, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.PRODUCT, source, key, desc, Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VENDOR, source, key, desc, Confidence.MEDIUM); } return desc; } @@ -911,13 +915,14 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the JarAnalyzer. * + * @param engine a reference to the dependency-check engine * @throws InitializationException is thrown if there is an exception * creating a temporary directory */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { try { - final File baseDir = Settings.getTempDirectory(); + final File baseDir = getSettings().getTempDirectory(); tempFileLocation = File.createTempFile("check", "tmp", baseDir); if (!tempFileLocation.delete()) { final String msg = String.format("Unable to delete temporary file '%s'.", tempFileLocation.getAbsolutePath()); @@ -1053,9 +1058,9 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { * * @param classes a collection of class name information * @param value the value to check to see if it contains a package name - * @param evidence the evidence collection to add new entries too + * @param dep the dependency to add new entries too */ - private static void addMatchingValues(List classes, String value, EvidenceCollection evidence) { + private static void addMatchingVendorValues(List classes, String value, Dependency dep) { if (value == null || value.isEmpty() || classes == null || classes.isEmpty()) { return; } @@ -1065,7 +1070,32 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer { final Pattern p = Pattern.compile("\b" + key + "\b"); if (p.matcher(text).find()) { //if (text.contains(key)) { //note, package structure elements are already lowercase. - evidence.addEvidence("jar", "package name", key, Confidence.HIGHEST); + dep.addEvidence(EvidenceType.VENDOR, "jar", "package name", key, Confidence.HIGHEST); + } + } + } + } + /** + * Cycles through the collection of class name information to see if parts + * of the package names are contained in the provided value. If found, it + * will be added as the HIGHEST confidence evidence because we have more + * then one source corroborating the value. + * + * @param classes a collection of class name information + * @param value the value to check to see if it contains a package name + * @param dep the dependency to add new entries too + */ + private static void addMatchingProductValues(List classes, String value, Dependency dep) { + if (value == null || value.isEmpty() || classes == null || classes.isEmpty()) { + return; + } + final String text = value.toLowerCase(); + for (ClassNameInformation cni : classes) { + for (String key : cni.getPackageStructure()) { + final Pattern p = Pattern.compile("\b" + key + "\b"); + if (p.matcher(text).find()) { + //if (text.contains(key)) { //note, package structure elements are already lowercase. + dep.addEvidence(EvidenceType.PRODUCT, "jar", "package name", key, Confidence.HIGHEST); } } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java index b6eae2a1d..a2e6167b3 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NexusAnalyzer.java @@ -35,6 +35,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.DownloadFailedException; import org.owasp.dependencycheck.utils.Downloader; @@ -59,6 +61,7 @@ import org.owasp.dependencycheck.utils.Settings; * * @author colezlaw */ +@ThreadSafe public class NexusAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -95,7 +98,18 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { /** * Field indicating if the analyzer is enabled. */ - private final boolean enabled = checkEnabled(); + private boolean enabled = true; + + /** + * Initializes the analyzer with the configured settings. + * + * @param settings the configured settings to use + */ + @Override + public void initialize(Settings settings) { + super.initialize(settings); + enabled = checkEnabled(); + } /** * Determines if this analyzer is enabled @@ -110,8 +124,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { */ boolean retval = false; try { - if (!DEFAULT_URL.equals(Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL)) - && Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED)) { + if (!DEFAULT_URL.equals(getSettings().getString(Settings.KEYS.ANALYZER_NEXUS_URL)) + && getSettings().getBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED)) { LOGGER.info("Enabling Nexus analyzer"); retval = true; } else { @@ -137,25 +151,25 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the analyzer once before any analysis is performed. * + * @param engine a reference to the dependency-check engine * @throws InitializationException if there's an error during initialization */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { LOGGER.debug("Initializing Nexus Analyzer"); LOGGER.debug("Nexus Analyzer enabled: {}", isEnabled()); if (isEnabled()) { final boolean useProxy = useProxy(); - final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL); - LOGGER.debug("Nexus Analyzer URL: {}", searchUrl); + LOGGER.debug("Using proxy: {}", useProxy); try { - searcher = new NexusSearch(new URL(searchUrl), useProxy); + searcher = new NexusSearch(getSettings(), useProxy); if (!searcher.preflightRequest()) { setEnabled(false); throw new InitializationException("There was an issue getting Nexus status. Disabling analyzer."); } } catch (MalformedURLException mue) { setEnabled(false); - throw new InitializationException("Malformed URL to Nexus: " + searchUrl, mue); + throw new InitializationException("Malformed URL to Nexus", mue); } } } @@ -223,7 +237,7 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { dependency.addAsEvidence("nexus", ma, Confidence.HIGH); boolean pomAnalyzed = false; LOGGER.debug("POM URL {}", ma.getPomUrl()); - for (Evidence e : dependency.getVendorEvidence()) { + for (Evidence e : dependency.getEvidence(EvidenceType.VENDOR)) { if ("pom".equals(e.getSource())) { pomAnalyzed = true; break; @@ -232,7 +246,7 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { if (!pomAnalyzed && ma.getPomUrl() != null) { File pomFile = null; try { - final File baseDir = Settings.getTempDirectory(); + final File baseDir = getSettings().getTempDirectory(); pomFile = File.createTempFile("pom", ".xml", baseDir); if (!pomFile.delete()) { LOGGER.warn("Unable to fetch pom.xml for {} from Nexus repository; " @@ -240,7 +254,8 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.debug("Unable to delete temp file"); } LOGGER.debug("Downloading {}", ma.getPomUrl()); - Downloader.fetchFile(new URL(ma.getPomUrl()), pomFile); + final Downloader downloader = new Downloader(getSettings()); + downloader.fetchFile(new URL(ma.getPomUrl()), pomFile); PomUtils.analyzePOM(dependency, pomFile); } catch (DownloadFailedException ex) { LOGGER.warn("Unable to download pom.xml for {} from Nexus repository; " @@ -266,14 +281,14 @@ public class NexusAnalyzer extends AbstractFileTypeAnalyzer { } /** - * Determine if a proxy should be used. + * Determine if a proxy should be used for the Nexus Analyzer. * * @return {@code true} if a proxy should be used */ - public static boolean useProxy() { + public boolean useProxy() { try { - return Settings.getString(Settings.KEYS.PROXY_SERVER) != null - && Settings.getBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY); + return getSettings().getString(Settings.KEYS.PROXY_SERVER) != null + && getSettings().getBoolean(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY); } catch (InvalidSettingException ise) { LOGGER.warn("Failed to parse proxy settings.", ise); return false; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java index ffd94fa7c..7145ef550 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzer.java @@ -22,7 +22,6 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; @@ -32,6 +31,7 @@ import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; import javax.json.Json; import javax.json.JsonException; import javax.json.JsonObject; @@ -39,6 +39,7 @@ import javax.json.JsonReader; import javax.json.JsonString; import javax.json.JsonValue; import org.owasp.dependencycheck.exception.InitializationException; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Used to analyze Node Package Manager (npm) package.json files, and collect @@ -47,6 +48,7 @@ import org.owasp.dependencycheck.exception.InitializationException; * @author Dale Visser */ @Experimental +@ThreadSafe public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -85,7 +87,7 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer { } @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { // NO-OP } @@ -123,26 +125,25 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer { @Override protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { final File file = dependency.getActualFile(); - if (!file.isFile() || file.length()==0) { + if (!file.isFile() || file.length() == 0) { return; } try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) { final JsonObject json = jsonReader.readObject(); - final EvidenceCollection productEvidence = dependency.getProductEvidence(); - final EvidenceCollection vendorEvidence = dependency.getVendorEvidence(); if (json.containsKey("name")) { final Object value = json.get("name"); if (value instanceof JsonString) { final String valueString = ((JsonString) value).getString(); - productEvidence.addEvidence(PACKAGE_JSON, "name", valueString, Confidence.HIGHEST); - vendorEvidence.addEvidence(PACKAGE_JSON, "name_project", String.format("%s_project", valueString), Confidence.LOW); + dependency.addEvidence(EvidenceType.PRODUCT, PACKAGE_JSON, "name", valueString, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, PACKAGE_JSON, "name_project", + String.format("%s_project", valueString), Confidence.LOW); } else { LOGGER.warn("JSON value not string as expected: {}", value); } } - addToEvidence(json, productEvidence, "description"); - addToEvidence(json, vendorEvidence, "author"); - addToEvidence(json, dependency.getVersionEvidence(), "version"); + addToEvidence(dependency, EvidenceType.PRODUCT, json, "description"); + addToEvidence(dependency, EvidenceType.VENDOR, json, "author"); + addToEvidence(dependency, EvidenceType.VERSION, json, "version"); dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName())); } catch (JsonException e) { LOGGER.warn("Failed to parse package.json file.", e); @@ -155,22 +156,24 @@ public class NodePackageAnalyzer extends AbstractFileTypeAnalyzer { * Adds information to an evidence collection from the node json * configuration. * + * @param dep the dependency to add the evidence + * @param t the type of evidence to add * @param json information from node.js - * @param collection a set of evidence about a dependency * @param key the key to obtain the data from the json information */ - private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) { + private void addToEvidence(Dependency dep, EvidenceType t, JsonObject json, String key) { if (json.containsKey(key)) { final JsonValue value = json.get(key); if (value instanceof JsonString) { - collection.addEvidence(PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST); + dep.addEvidence(t, PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST); + } else if (value instanceof JsonObject) { final JsonObject jsonObject = (JsonObject) value; for (final Map.Entry entry : jsonObject.entrySet()) { final String property = entry.getKey(); final JsonValue subValue = entry.getValue(); if (subValue instanceof JsonString) { - collection.addEvidence(PACKAGE_JSON, + dep.addEvidence(t, PACKAGE_JSON, String.format("%s.%s", key, property), ((JsonString) subValue).getString(), Confidence.HIGHEST); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java index 5fe536afc..da33c61a6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NspAnalyzer.java @@ -25,7 +25,6 @@ import org.owasp.dependencycheck.data.nsp.NspSearch; import org.owasp.dependencycheck.data.nsp.SanitizePackage; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.VulnerableSoftware; @@ -37,11 +36,11 @@ import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.MalformedURLException; -import java.net.URL; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonException; @@ -50,6 +49,7 @@ import javax.json.JsonObjectBuilder; import javax.json.JsonReader; import javax.json.JsonString; import javax.json.JsonValue; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.URLConnectionFailureException; @@ -59,6 +59,7 @@ import org.owasp.dependencycheck.utils.URLConnectionFailureException; * * @author Steve Springett */ +@ThreadSafe public class NspAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -100,17 +101,17 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the analyzer once before any analysis is performed. * + * @param engine a reference to the dependency-check engine * @throws InitializationException if there's an error during initialization */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { LOGGER.debug("Initializing {}", getName()); - final String searchUrl = Settings.getString(Settings.KEYS.ANALYZER_NSP_URL, DEFAULT_URL); try { - searcher = new NspSearch(new URL(searchUrl)); + searcher = new NspSearch(getSettings()); } catch (MalformedURLException ex) { setEnabled(false); - throw new InitializationException("The configured URL to Node Security Platform is malformed: " + searchUrl, ex); + throw new InitializationException("The configured URL to Node Security Platform is malformed", ex); } } @@ -148,7 +149,7 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { @Override protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { final File file = dependency.getActualFile(); - if (!file.isFile() || file.length()==0) { + if (!file.isFile() || file.length() == 0) { return; } try (JsonReader jsonReader = Json.createReader(FileUtils.openInputStream(file))) { @@ -197,20 +198,19 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { vuln.setVulnerableSoftware(new HashSet<>(Arrays.asList(vs))); // Add the vulnerability to package.json - dependency.getVulnerabilities().add(vuln); + dependency.addVulnerability(vuln); } /* * Adds evidence about the node package itself, not any of the modules. */ - final EvidenceCollection productEvidence = dependency.getProductEvidence(); - final EvidenceCollection vendorEvidence = dependency.getVendorEvidence(); if (packageJson.containsKey("name")) { final Object value = packageJson.get("name"); if (value instanceof JsonString) { final String valueString = ((JsonString) value).getString(); - productEvidence.addEvidence(PACKAGE_JSON, "name", valueString, Confidence.HIGHEST); - vendorEvidence.addEvidence(PACKAGE_JSON, "name_project", String.format("%s_project", valueString), Confidence.LOW); + dependency.addEvidence(EvidenceType.PRODUCT, PACKAGE_JSON, "name", valueString, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, PACKAGE_JSON, "name_project", + String.format("%s_project", valueString), Confidence.LOW); } else { LOGGER.warn("JSON value not string as expected: {}", value); } @@ -259,9 +259,9 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { /* * Adds general evidence to about the package. */ - addToEvidence(packageJson, productEvidence, "description"); - addToEvidence(packageJson, vendorEvidence, "author"); - addToEvidence(packageJson, dependency.getVersionEvidence(), "version"); + addToEvidence(dependency, EvidenceType.PRODUCT, packageJson, "description"); + addToEvidence(dependency, EvidenceType.VENDOR, packageJson, "author"); + addToEvidence(dependency, EvidenceType.VERSION, packageJson, "version"); dependency.setDisplayFileName(String.format("%s/%s", file.getParentFile().getName(), file.getName())); } catch (URLConnectionFailureException e) { this.setEnabled(false); @@ -276,19 +276,19 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { } /** - * Processes a part of package.json (as defined by JsonArray) and update - * the specified dependency with relevant info. + * Processes a part of package.json (as defined by JsonArray) and update the + * specified dependency with relevant info. * * @param dependency the Dependency to update * @param jsonArray the jsonArray to parse * @param depType the dependency type */ private void processPackage(Dependency dependency, JsonArray jsonArray, String depType) { - JsonObjectBuilder builder = Json.createObjectBuilder(); + final JsonObjectBuilder builder = Json.createObjectBuilder(); for (JsonString str : jsonArray.getValuesAs(JsonString.class)) { builder.add(str.toString(), ""); } - JsonObject jsonObject = builder.build(); + final JsonObject jsonObject = builder.build(); processPackage(dependency, jsonObject, depType); } @@ -324,9 +324,12 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { * dependency will not actually exist but needs to be unique (due to the use of Set in Dependency). * The use of related dependencies is a way to specify the actual software BOM in package.json. */ + //TODO is this actually correct? or should these be transitive dependencies? final Dependency nodeModule = new Dependency(new File(dependency.getActualFile() + "#" + entry.getKey()), true); nodeModule.setDisplayFileName(entry.getKey()); - nodeModule.setIdentifiers(new HashSet<>(Arrays.asList(moduleName, moduleVersion, moduleDepType))); + nodeModule.addIdentifier(moduleName); + nodeModule.addIdentifier(moduleVersion); + nodeModule.addIdentifier(moduleDepType); dependency.addRelatedDependency(nodeModule); } } @@ -336,22 +339,23 @@ public class NspAnalyzer extends AbstractFileTypeAnalyzer { * Adds information to an evidence collection from the node json * configuration. * + * @param dep the dependency to which the evidence will be added + * @param type the type of evidence to be added * @param json information from node.js - * @param collection a set of evidence about a dependency * @param key the key to obtain the data from the json information */ - private void addToEvidence(JsonObject json, EvidenceCollection collection, String key) { + private void addToEvidence(Dependency dep, EvidenceType type, JsonObject json, String key) { if (json.containsKey(key)) { final JsonValue value = json.get(key); if (value instanceof JsonString) { - collection.addEvidence(PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST); + dep.addEvidence(type, PACKAGE_JSON, key, ((JsonString) value).getString(), Confidence.HIGHEST); } else if (value instanceof JsonObject) { final JsonObject jsonObject = (JsonObject) value; for (final Map.Entry entry : jsonObject.entrySet()) { final String property = entry.getKey(); final JsonValue subValue = entry.getValue(); if (subValue instanceof JsonString) { - collection.addEvidence(PACKAGE_JSON, + dep.addEvidence(type, PACKAGE_JSON, String.format("%s.%s", key, property), ((JsonString) subValue).getString(), Confidence.HIGHEST); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzer.java index 1aefe1129..ea4d5a397 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzer.java @@ -33,6 +33,8 @@ import org.slf4j.LoggerFactory; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; /** @@ -40,6 +42,7 @@ import org.owasp.dependencycheck.exception.InitializationException; * * @author colezlaw */ +@ThreadSafe public class NuspecAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -69,10 +72,11 @@ public class NuspecAnalyzer extends AbstractFileTypeAnalyzer { /** * Initializes the analyzer once before any analysis is performed. * + * @param engine a reference to the dependency-check engine * @throws InitializationException if there's an error during initialization */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { //nothing to initialize } @@ -137,13 +141,13 @@ public class NuspecAnalyzer extends AbstractFileTypeAnalyzer { } if (np.getOwners() != null) { - dependency.getVendorEvidence().addEvidence("nuspec", "owners", np.getOwners(), Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, "nuspec", "owners", np.getOwners(), Confidence.HIGHEST); } - dependency.getVendorEvidence().addEvidence("nuspec", "authors", np.getAuthors(), Confidence.HIGH); - dependency.getVersionEvidence().addEvidence("nuspec", "version", np.getVersion(), Confidence.HIGHEST); - dependency.getProductEvidence().addEvidence("nuspec", "id", np.getId(), Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, "nuspec", "authors", np.getAuthors(), Confidence.HIGH); + dependency.addEvidence(EvidenceType.VERSION, "nuspec", "version", np.getVersion(), Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.PRODUCT, "nuspec", "id", np.getId(), Confidence.HIGHEST); if (np.getTitle() != null) { - dependency.getProductEvidence().addEvidence("nuspec", "title", np.getTitle(), Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.PRODUCT, "nuspec", "title", np.getTitle(), Confidence.MEDIUM); } } catch (Throwable e) { throw new AnalysisException(e); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NvdCveAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NvdCveAnalyzer.java index c3cdedf2f..6a6e82060 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NvdCveAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/NvdCveAnalyzer.java @@ -17,9 +17,8 @@ */ package org.owasp.dependencycheck.analyzer; -import java.io.IOException; -import java.sql.SQLException; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.data.nvdcve.CveDB; @@ -27,9 +26,7 @@ import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; -import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.Settings; -import org.slf4j.LoggerFactory; /** * NvdCveAnalyzer is a utility class that takes a project dependency and @@ -38,49 +35,13 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class NvdCveAnalyzer extends AbstractAnalyzer { /** * The Logger for use throughout the class */ - private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(NvdCveAnalyzer.class); - - /** - * The CVE Index. - */ - private CveDB cveDB; - - /** - * Opens the data source. - * - * @throws SQLException thrown when there is a SQL Exception - * @throws IOException thrown when there is an IO Exception - * @throws DatabaseException thrown when there is a database exceptions - * @throws ClassNotFoundException thrown if the h2 database driver cannot be - * loaded - */ - public void open() throws SQLException, IOException, DatabaseException, ClassNotFoundException { - cveDB = CveDB.getInstance(); - } - - /** - * Closes the data source. - */ - @Override - public void closeAnalyzer() { - cveDB.close(); - cveDB = null; - } - - /** - * Returns the status of the data source - is the database open. - * - * @return true or false. - */ - public boolean isOpen() { - return cveDB != null; - } - + //private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(NvdCveAnalyzer.class); /** * Analyzes a dependency and attempts to determine if there are any CPE * identifiers for this dependency. @@ -92,12 +53,13 @@ public class NvdCveAnalyzer extends AbstractAnalyzer { */ @Override protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + final CveDB cveDB = engine.getDatabase(); for (Identifier id : dependency.getIdentifiers()) { if ("cpe".equals(id.getType())) { try { final String value = id.getValue(); final List vulns = cveDB.getVulnerabilities(value); - dependency.getVulnerabilities().addAll(vulns); + dependency.addVulnerabilities(vulns); } catch (DatabaseException ex) { throw new AnalysisException(ex); } @@ -108,7 +70,7 @@ public class NvdCveAnalyzer extends AbstractAnalyzer { try { final String value = id.getValue(); final List vulns = cveDB.getVulnerabilities(value); - dependency.getSuppressedVulnerabilities().addAll(vulns); + dependency.addSuppressedVulnerabilities(vulns); } catch (DatabaseException ex) { throw new AnalysisException(ex); } @@ -146,29 +108,4 @@ public class NvdCveAnalyzer extends AbstractAnalyzer { protected String getAnalyzerEnabledSettingKey() { return Settings.KEYS.ANALYZER_NVD_CVE_ENABLED; } - - /** - * Opens the database used to gather NVD CVE data. - * - * @throws InitializationException is thrown if there is an issue opening - * the index. - */ - @Override - public void initializeAnalyzer() throws InitializationException { - try { - this.open(); - } catch (SQLException ex) { - LOGGER.debug("SQL Exception initializing NvdCveAnalyzer", ex); - throw new InitializationException(ex); - } catch (IOException ex) { - LOGGER.debug("IO Exception initializing NvdCveAnalyzer", ex); - throw new InitializationException(ex); - } catch (DatabaseException ex) { - LOGGER.debug("Database Exception initializing NvdCveAnalyzer", ex); - throw new InitializationException(ex); - } catch (ClassNotFoundException ex) { - LOGGER.debug("Exception initializing NvdCveAnalyzer", ex); - throw new InitializationException(ex); - } - } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java index 308f81f3d..aba425d2e 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzer.java @@ -31,6 +31,8 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; /** @@ -38,6 +40,7 @@ import org.owasp.dependencycheck.exception.InitializationException; * * @author Dale Visser */ +@ThreadSafe public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -143,13 +146,24 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { return OPENSSLV_FILTER; } + /** + * Returns the setting for the analyzer enabled setting key. + * + * @return the setting for the analyzer enabled setting key + */ + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_OPENSSL_ENABLED; + } + /** * No-op initializer implementation. * + * @param engine a reference to the dependency-check engine * @throws InitializationException never thrown */ @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { // Nothing to do here. } @@ -171,17 +185,17 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { if (!contents.isEmpty()) { final Matcher matcher = VERSION_PATTERN.matcher(contents); if (matcher.find()) { - dependency.getVersionEvidence().addEvidence(OPENSSLV_H, "Version Constant", + dependency.addEvidence(EvidenceType.VERSION, OPENSSLV_H, "Version Constant", getOpenSSLVersion(Long.parseLong(matcher.group(1), HEXADECIMAL)), Confidence.HIGH); found = true; } } if (found) { dependency.setDisplayFileName(parentName + File.separatorChar + OPENSSLV_H); - dependency.getVendorEvidence().addEvidence(OPENSSLV_H, "Vendor", "OpenSSL", Confidence.HIGHEST); - dependency.getProductEvidence().addEvidence(OPENSSLV_H, "Product", "OpenSSL", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, OPENSSLV_H, "Vendor", "OpenSSL", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.PRODUCT, OPENSSLV_H, "Product", "OpenSSL", Confidence.HIGHEST); } else { - engine.getDependencies().remove(dependency); + engine.removeDependency(dependency); } } @@ -201,14 +215,4 @@ public class OpenSSLAnalyzer extends AbstractFileTypeAnalyzer { "Problem occurred while reading dependency file.", e); } } - - /** - * Returns the setting for the analyzer enabled setting key. - * - * @return the setting for the analyzer enabled setting key - */ - @Override - protected String getAnalyzerEnabledSettingKey() { - return Settings.KEYS.ANALYZER_OPENSSL_ENABLED; - } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java index 551279eb7..697122a27 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzer.java @@ -32,7 +32,6 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +45,8 @@ import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.UrlStringUtils; import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Used to analyze a Wheel or egg distribution files, or their contents in @@ -55,30 +56,26 @@ import java.util.concurrent.atomic.AtomicInteger; * @author Dale Visser */ @Experimental +@ThreadSafe public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer { /** * Name of egg metadata files to analyze. */ private static final String PKG_INFO = "PKG-INFO"; - /** * Name of wheel metadata files to analyze. */ private static final String METADATA = "METADATA"; - /** * The logger. */ - private static final Logger LOGGER = LoggerFactory - .getLogger(PythonDistributionAnalyzer.class); - + private static final Logger LOGGER = LoggerFactory.getLogger(PythonDistributionAnalyzer.class); /** * The count of directories created during analysis. This is used for * creating temporary directories. */ private static final AtomicInteger DIR_COUNT = new AtomicInteger(0); - /** * The name of the analyzer. */ @@ -87,52 +84,39 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer { * The phase that this analyzer is intended to run in. */ private static final AnalysisPhase ANALYSIS_PHASE = AnalysisPhase.INFORMATION_COLLECTION; - /** * The set of file extensions supported by this analyzer. */ private static final String[] EXTENSIONS = {"whl", "egg", "zip"}; - /** * Used to match on egg archive candidate extensions. */ private static final FileFilter EGG_OR_ZIP = FileFilterBuilder.newInstance().addExtensions("egg", "zip").build(); - /** * Used to detect files with a .whl extension. */ private static final FileFilter WHL_FILTER = FileFilterBuilder.newInstance().addExtensions("whl").build(); - /** * The parent directory for the individual directories per archive. */ private File tempFileLocation; - /** * Filter that detects *.dist-info files (but doesn't verify they are * directories. */ - private static final FilenameFilter DIST_INFO_FILTER = new SuffixFileFilter( - ".dist-info"); - + private static final FilenameFilter DIST_INFO_FILTER = new SuffixFileFilter(".dist-info"); /** * Filter that detects files named "METADATA". */ - private static final FilenameFilter EGG_INFO_FILTER = new NameFileFilter( - "EGG-INFO"); - + private static final FilenameFilter EGG_INFO_FILTER = new NameFileFilter("EGG-INFO"); /** * Filter that detects files named "METADATA". */ - private static final NameFileFilter METADATA_FILTER = new NameFileFilter( - METADATA); - + private static final NameFileFilter METADATA_FILTER = new NameFileFilter(METADATA); /** * Filter that detects files named "PKG-INFO". */ - private static final NameFileFilter PKG_INFO_FILTER = new NameFileFilter( - PKG_INFO); - + private static final NameFileFilter PKG_INFO_FILTER = new NameFileFilter(PKG_INFO); /** * The file filter used to determine which files this analyzer supports. */ @@ -241,13 +225,14 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer { /** * Makes sure a usable temporary directory is available. * + * @param engine a reference to the dependency-check engine * @throws InitializationException an AnalyzeException is thrown when the * temp directory cannot be created */ @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { try { - final File baseDir = Settings.getTempDirectory(); + final File baseDir = getSettings().getTempDirectory(); tempFileLocation = File.createTempFile("check", "tmp", baseDir); if (!tempFileLocation.delete()) { setEnabled(false); @@ -294,41 +279,36 @@ public class PythonDistributionAnalyzer extends AbstractFileTypeAnalyzer { */ private static void collectWheelMetadata(Dependency dependency, File file) { final InternetHeaders headers = getManifestProperties(file); - addPropertyToEvidence(headers, dependency.getVersionEvidence(), - "Version", Confidence.HIGHEST); - addPropertyToEvidence(headers, dependency.getProductEvidence(), "Name", - Confidence.HIGHEST); + addPropertyToEvidence(dependency, EvidenceType.VERSION, Confidence.HIGHEST, headers, "Version"); + addPropertyToEvidence(dependency, EvidenceType.PRODUCT, Confidence.HIGHEST, headers, "Name"); final String url = headers.getHeader("Home-page", null); - final EvidenceCollection vendorEvidence = dependency - .getVendorEvidence(); if (StringUtils.isNotBlank(url)) { if (UrlStringUtils.isUrl(url)) { - vendorEvidence.addEvidence(METADATA, "vendor", url, - Confidence.MEDIUM); + dependency.addEvidence(EvidenceType.VENDOR, METADATA, "vendor", url, Confidence.MEDIUM); } } - addPropertyToEvidence(headers, vendorEvidence, "Author", Confidence.LOW); + addPropertyToEvidence(dependency, EvidenceType.VENDOR, Confidence.LOW, headers, "Author"); final String summary = headers.getHeader("Summary", null); if (StringUtils.isNotBlank(summary)) { - JarAnalyzer - .addDescription(dependency, summary, METADATA, "summary"); + JarAnalyzer.addDescription(dependency, summary, METADATA, "summary"); } } /** * Adds a value to the evidence collection. * + * @param dependency the dependency being analyzed + * @param type the type of evidence to add + * @param confidence the confidence in the evidence being added * @param headers the properties collection - * @param evidence the evidence collection to add the value * @param property the property name - * @param confidence the confidence of the evidence */ - private static void addPropertyToEvidence(InternetHeaders headers, - EvidenceCollection evidence, String property, Confidence confidence) { + private static void addPropertyToEvidence(Dependency dependency, EvidenceType type, Confidence confidence, + InternetHeaders headers, String property) { final String value = headers.getHeader(property, null); LOGGER.debug("Property: {}, Value: {}", property, value); if (StringUtils.isNotBlank(value)) { - evidence.addEvidence(METADATA, property, value, confidence); + dependency.addEvidence(type, METADATA, property, value, confidence); } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzer.java index 12e58d3f4..abc2a315c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzer.java @@ -24,7 +24,6 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.UrlStringUtils; @@ -35,6 +34,8 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; /** @@ -44,13 +45,13 @@ import org.owasp.dependencycheck.exception.InitializationException; * @author Dale Visser */ @Experimental +@ThreadSafe public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { /** * Used when compiling file scanning regex patterns. */ - private static final int REGEX_OPTIONS = Pattern.DOTALL - | Pattern.CASE_INSENSITIVE; + private static final int REGEX_OPTIONS = Pattern.DOTALL | Pattern.CASE_INSENSITIVE; /** * Filename extensions for files to be analyzed. @@ -58,16 +59,14 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { private static final String EXTENSIONS = "py"; /** - * Pattern for matching the module docstring in a source file. + * Pattern for matching the module doc string in a source file. */ - private static final Pattern MODULE_DOCSTRING = Pattern.compile( - "^(['\\\"]{3})(.*?)\\1", REGEX_OPTIONS); + private static final Pattern MODULE_DOCSTRING = Pattern.compile("^(['\\\"]{3})(.*?)\\1", REGEX_OPTIONS); /** * Matches assignments to version variables in Python source code. */ - private static final Pattern VERSION_PATTERN = Pattern.compile( - "\\b(__)?version(__)? *= *(['\"]+)(\\d+\\.\\d+.*?)\\3", + private static final Pattern VERSION_PATTERN = Pattern.compile("\\b(__)?version(__)? *= *(['\"]+)(\\d+\\.\\d+.*?)\\3", REGEX_OPTIONS); /** @@ -130,6 +129,16 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { return AnalysisPhase.INFORMATION_COLLECTION; } + /** + * Returns the key name for the analyzers enabled setting. + * + * @return the key name for the analyzers enabled setting + */ + @Override + protected String getAnalyzerEnabledSettingKey() { + return Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED; + } + /** * Returns the FileFilter * @@ -143,10 +152,11 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { /** * No-op initializer implementation. * + * @param engine a reference to the dependency-check engine * @throws InitializationException never thrown */ @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { // Nothing to do here. } @@ -181,8 +191,7 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { //"The __init__.py files are required to make Python treat the directories as containing packages" //see section "6.4 Packages" from https://docs.python.org/2/tutorial/modules.html; dependency.setDisplayFileName(parentName + "/__init__.py"); - dependency.getProductEvidence().addEvidence(file.getName(), - "PackageName", parentName, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.PRODUCT, file.getName(), "PackageName", parentName, Confidence.HIGHEST); final File[] fileList = parent.listFiles(PY_FILTER); if (fileList != null) { @@ -191,7 +200,7 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { } } } else { - engine.getDependencies().remove(dependency); + engine.removeDependency(dependency); } } @@ -211,32 +220,28 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { try { contents = FileUtils.readFileToString(file, Charset.defaultCharset()).trim(); } catch (IOException e) { - throw new AnalysisException( - "Problem occurred while reading dependency file.", e); + throw new AnalysisException("Problem occurred while reading dependency file.", e); } boolean found = false; if (!contents.isEmpty()) { final String source = file.getName(); - found = gatherEvidence(VERSION_PATTERN, contents, source, - dependency.getVersionEvidence(), "SourceVersion", - Confidence.MEDIUM); + found = gatherEvidence(dependency, EvidenceType.VERSION, VERSION_PATTERN, contents, + source, "SourceVersion", Confidence.MEDIUM); found |= addSummaryInfo(dependency, SUMMARY_PATTERN, 4, contents, source, "summary"); if (INIT_PY_FILTER.accept(file)) { found |= addSummaryInfo(dependency, MODULE_DOCSTRING, 2, contents, source, "docstring"); } - found |= gatherEvidence(TITLE_PATTERN, contents, source, - dependency.getProductEvidence(), "SourceTitle", - Confidence.LOW); - final EvidenceCollection vendorEvidence = dependency - .getVendorEvidence(); - found |= gatherEvidence(AUTHOR_PATTERN, contents, source, - vendorEvidence, "SourceAuthor", Confidence.MEDIUM); - found |= gatherHomePageEvidence(URI_PATTERN, vendorEvidence, + found |= gatherEvidence(dependency, EvidenceType.PRODUCT, TITLE_PATTERN, contents, + source, "SourceTitle", Confidence.LOW); + + found |= gatherEvidence(dependency, EvidenceType.VENDOR, AUTHOR_PATTERN, contents, + source, "SourceAuthor", Confidence.MEDIUM); + found |= gatherHomePageEvidence(dependency, EvidenceType.VENDOR, URI_PATTERN, source, "URL", contents); - found |= gatherHomePageEvidence(HOMEPAGE_PATTERN, - vendorEvidence, source, "HomePage", contents); + found |= gatherHomePageEvidence(dependency, EvidenceType.VENDOR, HOMEPAGE_PATTERN, + source, "HomePage", contents); } return found; } @@ -266,23 +271,23 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { /** * Collects evidence from the home page URL. * + * @param dependency the dependency that is being analyzed + * @param type the type of evidence * @param pattern the pattern to match - * @param evidence the evidence collection to add the evidence to * @param source the source of the evidence * @param name the name of the evidence * @param contents the home page URL * @return true if evidence was collected; otherwise false */ - private boolean gatherHomePageEvidence(Pattern pattern, - EvidenceCollection evidence, String source, String name, - String contents) { + private boolean gatherHomePageEvidence(Dependency dependency, EvidenceType type, Pattern pattern, + String source, String name, String contents) { final Matcher matcher = pattern.matcher(contents); boolean found = false; if (matcher.find()) { final String url = matcher.group(4); if (UrlStringUtils.isUrl(url)) { found = true; - evidence.addEvidence(source, name, url, Confidence.MEDIUM); + dependency.addEvidence(type, source, name, url, Confidence.MEDIUM); } } return found; @@ -292,27 +297,22 @@ public class PythonPackageAnalyzer extends AbstractFileTypeAnalyzer { * Gather evidence from a Python source file using the given string * assignment regex pattern. * + * @param dependency the dependency that is being analyzed + * @param type the type of evidence * @param pattern to scan contents with * @param contents of Python source file * @param source for storing evidence - * @param evidence to store evidence in * @param name of evidence * @param confidence in evidence * @return whether evidence was found */ - private boolean gatherEvidence(Pattern pattern, String contents, - String source, EvidenceCollection evidence, String name, - Confidence confidence) { + private boolean gatherEvidence(Dependency dependency, EvidenceType type, Pattern pattern, String contents, + String source, String name, Confidence confidence) { final Matcher matcher = pattern.matcher(contents); final boolean found = matcher.find(); if (found) { - evidence.addEvidence(source, name, matcher.group(4), confidence); + dependency.addEvidence(type, source, name, matcher.group(4), confidence); } return found; } - - @Override - protected String getAnalyzerEnabledSettingKey() { - return Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED; - } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java index 45cfd7955..2739642d4 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzer.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.io.FileUtils; import org.owasp.dependencycheck.Engine; @@ -36,6 +37,7 @@ import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.dependency.Reference; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.exception.InitializationException; @@ -50,6 +52,7 @@ import org.slf4j.LoggerFactory; * * @author Dale Visser */ +@ThreadSafe public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -90,7 +93,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { /** * The DAL. */ - private CveDB cvedb; + private CveDB cvedb = null; /** * @return a filter that accepts files named Gemfile.lock @@ -113,7 +116,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { throw new AnalysisException(String.format("%s should have been a directory.", folder.getAbsolutePath())); } final List args = new ArrayList<>(); - final String bundleAuditPath = Settings.getString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH); + final String bundleAuditPath = getSettings().getString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH); File bundleAudit = null; if (bundleAuditPath != null) { bundleAudit = new File(bundleAuditPath); @@ -140,22 +143,18 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { * Initialize the analyzer. In this case, extract GrokAssembly.exe to a * temporary location. * + * @param engine a reference to the dependency-check engine * @throws InitializationException if anything goes wrong */ @Override - public void initializeFileTypeAnalyzer() throws InitializationException { - try { - cvedb = CveDB.getInstance(); - } catch (DatabaseException ex) { - LOGGER.warn("Exception opening the database"); - LOGGER.debug("error", ex); - setEnabled(false); - throw new InitializationException("Error connecting to the database", ex); - } + public void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { // Now, need to see if bundle-audit actually runs from this location. + if (engine != null) { + this.cvedb = engine.getDatabase(); + } Process process = null; try { - process = launchBundleAudit(Settings.getTempDirectory()); + process = launchBundleAudit(getSettings().getTempDirectory()); } catch (AnalysisException ae) { setEnabled(false); @@ -208,17 +207,6 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } } - /** - * Closes the data source. - */ - @Override - public void closeAnalyzer() { - if (cvedb != null) { - cvedb.close(); - cvedb = null; - } - } - /** * Returns the name of the analyzer. * @@ -377,7 +365,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { vulnerability.setName(advisory); } if (null != dependency) { - dependency.getVulnerabilities().add(vulnerability); // needed to wait for vulnerability name to avoid NPE + dependency.addVulnerability(vulnerability); } LOGGER.debug("bundle-audit ({}): {}", parentName, nextLine); } @@ -413,13 +401,21 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { final String criticality = nextLine.substring(CRITICALITY.length()).trim(); float score = -1.0f; Vulnerability v = null; - try { - v = cvedb.getVulnerability(vulnerability.getName()); - } catch (DatabaseException ex) { - LOGGER.debug("Unable to look up vulnerability {}", vulnerability.getName()); + if (cvedb != null) { + try { + v = cvedb.getVulnerability(vulnerability.getName()); + } catch (DatabaseException ex) { + LOGGER.debug("Unable to look up vulnerability {}", vulnerability.getName()); + } } if (v != null) { score = v.getCvssScore(); + vulnerability.setCvssAccessComplexity(v.getCvssAccessComplexity()); + vulnerability.setCvssAccessVector(v.getCvssAccessVector()); + vulnerability.setCvssAuthentication(v.getCvssAuthentication()); + vulnerability.setCvssAvailabilityImpact(v.getCvssAvailabilityImpact()); + vulnerability.setCvssConfidentialityImpact(v.getCvssConfidentialityImpact()); + vulnerability.setCvssIntegrityImpact(v.getCvssIntegrityImpact()); } else if ("High".equalsIgnoreCase(criticality)) { score = 8.5f; } else if ("Medium".equalsIgnoreCase(criticality)) { @@ -445,7 +441,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { Vulnerability vulnerability = null; if (null != dependency) { final String version = nextLine.substring(VERSION.length()); - dependency.getVersionEvidence().addEvidence( + dependency.addEvidence(EvidenceType.VERSION, "bundler-audit", "Version", version, @@ -477,7 +473,7 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { * @throws IOException thrown if a temporary gem file could not be written */ private Dependency createDependencyForGem(Engine engine, String parentName, String fileName, String filePath, String gem) throws IOException { - final File gemFile = new File(Settings.getTempDirectory(), gem + "_Gemfile.lock"); + final File gemFile = new File(getSettings().getTempDirectory(), gem + "_Gemfile.lock"); if (!gemFile.createNewFile()) { throw new IOException("Unable to create temporary gem file"); } @@ -485,11 +481,11 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { FileUtils.write(gemFile, displayFileName, Charset.defaultCharset()); // unique contents to avoid dependency bundling final Dependency dependency = new Dependency(gemFile); - dependency.getProductEvidence().addEvidence("bundler-audit", "Name", gem, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.PRODUCT, "bundler-audit", "Name", gem, Confidence.HIGHEST); dependency.setDisplayFileName(displayFileName); dependency.setFileName(fileName); dependency.setFilePath(filePath); - engine.getDependencies().add(dependency); + engine.addDependency(dependency); return dependency; } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java index 6502d02ab..42e350cb2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzer.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.analyzer; import java.io.File; import java.io.FilenameFilter; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; @@ -47,6 +48,7 @@ import org.owasp.dependencycheck.dependency.Dependency; * @author Bianca Jiang (https://twitter.com/biancajiang) */ @Experimental +@ThreadSafe public class RubyBundlerAnalyzer extends RubyGemspecAnalyzer { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java index b600236d2..36a76574a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzer.java @@ -25,13 +25,14 @@ import java.nio.charset.Charset; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.io.FileUtils; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; @@ -46,6 +47,7 @@ import org.slf4j.LoggerFactory; * @author Dale Visser */ @Experimental +@ThreadSafe public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -89,7 +91,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { } @Override - protected void initializeFileTypeAnalyzer() throws InitializationException { + protected void prepareFileTypeAnalyzer(Engine engine) throws InitializationException { // NO-OP } @@ -130,8 +132,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { private static final Pattern GEMSPEC_BLOCK_INIT = Pattern.compile("Gem::Specification\\.new\\s+?do\\s+?\\|(.+?)\\|"); @Override - protected void analyzeDependency(Dependency dependency, Engine engine) - throws AnalysisException { + protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { String contents; try { contents = FileUtils.readFileToString(dependency.getActualFile(), Charset.defaultCharset()); @@ -144,23 +145,21 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { contents = contents.substring(matcher.end()); final String blockVariable = matcher.group(1); - final EvidenceCollection vendor = dependency.getVendorEvidence(); - final EvidenceCollection product = dependency.getProductEvidence(); - final String name = addStringEvidence(product, contents, blockVariable, "name", "name", Confidence.HIGHEST); + final String name = addStringEvidence(dependency, EvidenceType.PRODUCT, contents, blockVariable, "name", "name", Confidence.HIGHEST); if (!name.isEmpty()) { - vendor.addEvidence(GEMSPEC, "name_project", name + "_project", Confidence.LOW); + dependency.addEvidence(EvidenceType.VENDOR, GEMSPEC, "name_project", name + "_project", Confidence.LOW); } - addStringEvidence(product, contents, blockVariable, "summary", "summary", Confidence.LOW); + addStringEvidence(dependency, EvidenceType.PRODUCT, contents, blockVariable, "summary", "summary", Confidence.LOW); - addStringEvidence(vendor, contents, blockVariable, "author", "authors?", Confidence.HIGHEST); - addStringEvidence(vendor, contents, blockVariable, "email", "emails?", Confidence.MEDIUM); - addStringEvidence(vendor, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); - addStringEvidence(vendor, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST); + addStringEvidence(dependency, EvidenceType.VENDOR, contents, blockVariable, "author", "authors?", Confidence.HIGHEST); + addStringEvidence(dependency, EvidenceType.VENDOR, contents, blockVariable, "email", "emails?", Confidence.MEDIUM); + addStringEvidence(dependency, EvidenceType.VENDOR, contents, blockVariable, "homepage", "homepage", Confidence.HIGHEST); + addStringEvidence(dependency, EvidenceType.VENDOR, contents, blockVariable, "license", "licen[cs]es?", Confidence.HIGHEST); - final String value = addStringEvidence(dependency.getVersionEvidence(), contents, + final String value = addStringEvidence(dependency, EvidenceType.VERSION, contents, blockVariable, "version", "version", Confidence.HIGHEST); if (value.length() < 1) { - addEvidenceFromVersionFile(dependency.getActualFile(), dependency.getVersionEvidence()); + addEvidenceFromVersionFile(dependency, EvidenceType.VERSION, dependency.getActualFile()); } } @@ -170,7 +169,8 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { /** * Adds the specified evidence to the given evidence collection. * - * @param evidences the collection to add the evidence to + * @param dependency the dependency being analyzed + * @param type the type of evidence to add * @param contents the evidence contents * @param blockVariable the variable * @param field the field @@ -178,7 +178,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { * @param confidence the confidence of the evidence * @return the evidence string value added */ - private String addStringEvidence(EvidenceCollection evidences, String contents, + private String addStringEvidence(Dependency dependency, EvidenceType type, String contents, String blockVariable, String field, String fieldPattern, Confidence confidence) { String value = ""; @@ -196,7 +196,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { } } if (value.length() > 0) { - evidences.addEvidence(GEMSPEC, field, value, confidence); + dependency.addEvidence(type, GEMSPEC, field, value, confidence); } return value; @@ -205,10 +205,11 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { /** * Adds evidence from the version file. * + * @param dependency the dependency being analyzed + * @param type the type of evidence to add * @param dependencyFile the dependency being analyzed - * @param versionEvidences the version evidence */ - private void addEvidenceFromVersionFile(File dependencyFile, EvidenceCollection versionEvidences) { + private void addEvidenceFromVersionFile(Dependency dependency, EvidenceType type, File dependencyFile) { final File parentDir = dependencyFile.getParentFile(); if (parentDir != null) { final File[] matchingFiles = parentDir.listFiles(new FilenameFilter() { @@ -225,7 +226,7 @@ public class RubyGemspecAnalyzer extends AbstractFileTypeAnalyzer { final List lines = FileUtils.readLines(f, Charset.defaultCharset()); if (lines.size() == 1) { //TODO other checking? final String value = lines.get(0).trim(); - versionEvidences.addEvidence(GEMSPEC, "version", value, Confidence.HIGH); + dependency.addEvidence(type, GEMSPEC, "version", value, Confidence.HIGH); } } catch (IOException e) { LOGGER.debug("Error reading gemspec", e); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java index 5823d8aaf..a85b1c444 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/SwiftPackageManagerAnalyzer.java @@ -23,13 +23,14 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.io.FileUtils; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.FileFilterBuilder; import org.owasp.dependencycheck.utils.Settings; @@ -41,6 +42,7 @@ import org.owasp.dependencycheck.utils.Settings; * @author Bianca Jiang (https://twitter.com/biancajiang) */ @Experimental +@ThreadSafe public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { /** @@ -80,7 +82,7 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { } @Override - protected void initializeFileTypeAnalyzer() { + protected void prepareFileTypeAnalyzer(Engine engine) { // NO-OP } @@ -133,14 +135,11 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { return; } - final EvidenceCollection product = dependency.getProductEvidence(); - final EvidenceCollection vendor = dependency.getVendorEvidence(); - //SPM is currently under development for SWIFT 3. Its current metadata includes package name and dependencies. //Future interesting metadata: version, license, homepage, author, summary, etc. - final String name = addStringEvidence(product, packageDescription, "name", "name", Confidence.HIGHEST); + final String name = addStringEvidence(dependency, EvidenceType.PRODUCT, packageDescription, "name", "name", Confidence.HIGHEST); if (name != null && !name.isEmpty()) { - vendor.addEvidence(SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VENDOR, SPM_FILE_NAME, "name_project", name, Confidence.HIGHEST); } } setPackagePath(dependency); @@ -150,14 +149,15 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { * Extracts evidence from the package description and adds it to the given * evidence collection. * - * @param evidences the evidence collection to update + * @param dependency the dependency being analyzed + * @param type the type of evidence to add * @param packageDescription the text to extract evidence from * @param field the name of the field being searched for * @param fieldPattern the field pattern within the contents to search for * @param confidence the confidence level of the evidence if found * @return the string that was added as evidence */ - private String addStringEvidence(EvidenceCollection evidences, + private String addStringEvidence(Dependency dependency, EvidenceType type, String packageDescription, String field, String fieldPattern, Confidence confidence) { String value = ""; @@ -170,7 +170,7 @@ public class SwiftPackageManagerAnalyzer extends AbstractFileTypeAnalyzer { if (value != null) { value = value.trim(); if (value.length() > 0) { - evidences.addEvidence(SPM_FILE_NAME, field, value, confidence); + dependency.addEvidence(type, SPM_FILE_NAME, field, value, confidence); } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzer.java index 3c1c1526b..8fec4a3ea 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzer.java @@ -17,13 +17,15 @@ */ package org.owasp.dependencycheck.analyzer; -import java.util.Iterator; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Evidence; -import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.DependencyVersion; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; @@ -37,8 +39,14 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class VersionFilterAnalyzer extends AbstractAnalyzer { + /** + * The Logger for use throughout the class + */ + private static final Logger LOGGER = LoggerFactory.getLogger(VersionFilterAnalyzer.class); + // /** * Evidence source. @@ -111,11 +119,6 @@ public class VersionFilterAnalyzer extends AbstractAnalyzer { } // - /** - * The Logger for use throughout the class - */ - private static final Logger LOGGER = LoggerFactory.getLogger(VersionFilterAnalyzer.class); - /** * The HintAnalyzer uses knowledge about a dependency to add additional * information to help in identification of identifiers or vulnerabilities. @@ -126,18 +129,18 @@ public class VersionFilterAnalyzer extends AbstractAnalyzer { * the dependency. */ @Override - protected synchronized void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { + protected void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { String fileVersion = null; String pomVersion = null; String manifestVersion = null; - for (Evidence e : dependency.getVersionEvidence()) { + for (Evidence e : dependency.getEvidence(EvidenceType.VERSION)) { if (FILE.equals(e.getSource()) && VERSION.equals(e.getName())) { - fileVersion = e.getValue(Boolean.FALSE); + fileVersion = e.getValue(); } else if ((NEXUS.equals(e.getSource()) || CENTRAL.equals(e.getSource()) || POM.equals(e.getSource())) && VERSION.equals(e.getName())) { - pomVersion = e.getValue(Boolean.FALSE); + pomVersion = e.getValue(); } else if (MANIFEST.equals(e.getSource()) && IMPLEMENTATION_VERSION.equals(e.getName())) { - manifestVersion = e.getValue(Boolean.FALSE); + manifestVersion = e.getValue(); } } //ensure we have at least two not null @@ -150,17 +153,18 @@ public class VersionFilterAnalyzer extends AbstractAnalyzer { final boolean pomMatch = Objects.equals(dvPom, dvFile) || Objects.equals(dvPom, dvManifest); if (fileMatch || manifestMatch || pomMatch) { LOGGER.debug("filtering evidence from {}", dependency.getFileName()); - final EvidenceCollection versionEvidence = dependency.getVersionEvidence(); - final Iterator itr = versionEvidence.iterator(); - while (itr.hasNext()) { - final Evidence e = itr.next(); + final Set remove = new HashSet<>(); + for (Evidence e : dependency.getEvidence(EvidenceType.VERSION)) { if (!(pomMatch && VERSION.equals(e.getName()) && (NEXUS.equals(e.getSource()) || CENTRAL.equals(e.getSource()) || POM.equals(e.getSource()))) && !(fileMatch && VERSION.equals(e.getName()) && FILE.equals(e.getSource())) && !(manifestMatch && MANIFEST.equals(e.getSource()) && IMPLEMENTATION_VERSION.equals(e.getName()))) { - itr.remove(); + remove.add(e); } } + for (Evidence e : remove) { + dependency.removeEvidence(EvidenceType.VERSION, e); + } } } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java index 37216e894..9a85815d4 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzer.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.analyzer; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.utils.Settings; /** @@ -26,6 +27,7 @@ import org.owasp.dependencycheck.utils.Settings; * * @author Jeremy Long */ +@ThreadSafe public class VulnerabilitySuppressionAnalyzer extends AbstractSuppressionAnalyzer { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/AnalysisException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/AnalysisException.java index 2b641e29c..606f341a7 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/AnalysisException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/AnalysisException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.analyzer.exception; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception thrown when the analysis of a dependency fails. * * @author Jeremy Long */ +@ThreadSafe public class AnalysisException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/ArchiveExtractionException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/ArchiveExtractionException.java index 60c98f5c4..fdea55c10 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/ArchiveExtractionException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/analyzer/exception/ArchiveExtractionException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.analyzer.exception; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception thrown when files in an archive cannot be extracted. * * @author Jeremy Long */ +@ThreadSafe public class ArchiveExtractionException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/central/CentralSearch.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/central/CentralSearch.java index 314e321fb..8466741ef 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/central/CentralSearch.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/central/CentralSearch.java @@ -20,9 +20,12 @@ package org.owasp.dependencycheck.data.central; import java.io.FileNotFoundException; import java.io.IOException; import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; @@ -44,12 +47,13 @@ import org.xml.sax.SAXException; * * @author colezlaw */ +@ThreadSafe public class CentralSearch { /** * The URL for the Central service */ - private final URL rootURL; + private final String rootURL; /** * Whether to use the Proxy when making requests @@ -60,16 +64,28 @@ public class CentralSearch { * Used for logging. */ private static final Logger LOGGER = LoggerFactory.getLogger(CentralSearch.class); + /** + * The configured settings. + */ + private final Settings settings; /** * Creates a NexusSearch for the given repository URL. * - * @param rootURL the URL of the repository on which searches should - * execute. Only parameters are added to this (so it should end in /select) + * @param settings the configured settings + * @throws MalformedURLException thrown if the configured URL is + * invalid */ - public CentralSearch(URL rootURL) { - this.rootURL = rootURL; - if (null != Settings.getString(Settings.KEYS.PROXY_SERVER)) { + public CentralSearch(Settings settings) throws MalformedURLException { + this.settings = settings; + + final String searchUrl = settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL); + LOGGER.debug("Central Search URL: {}", searchUrl); + if (isInvalidURL(searchUrl)) { + throw new MalformedURLException(String.format("The configured central analyzer URL is invalid: %s", searchUrl)); + } + this.rootURL = searchUrl; + if (null != settings.getString(Settings.KEYS.PROXY_SERVER)) { useProxy = true; LOGGER.debug("Using proxy"); } else { @@ -93,7 +109,7 @@ public class CentralSearch { throw new IllegalArgumentException("Invalid SHA1 format"); } List result = null; - final URL url = new URL(rootURL + String.format("?q=1:\"%s\"&wt=xml", sha1)); + final URL url = new URL(String.format("%s?q=1:\"%s\"&wt=xml", rootURL, sha1)); LOGGER.debug("Searching Central url {}", url); @@ -101,7 +117,8 @@ public class CentralSearch { // 1) If the proxy is set, AND the setting is set to true, use the proxy // 2) Otherwise, don't use the proxy (either the proxy isn't configured, // or proxy is specifically set to false) - final HttpURLConnection conn = URLConnectionFactory.createHttpURLConnection(url, useProxy); + final URLConnectionFactory factory = new URLConnectionFactory(settings); + final HttpURLConnection conn = factory.createHttpURLConnection(url, useProxy); conn.setDoOutput(true); @@ -167,4 +184,21 @@ public class CentralSearch { } return result; } + + /** + * Tests to determine if the gien URL is invalid. + * + * @param url the url to evaluate + * @return true if the url is malformed; otherwise false + */ + private boolean isInvalidURL(String url) { + try { + final URL u = new URL(url); + u.toURI(); + } catch (MalformedURLException | URISyntaxException e) { + LOGGER.trace("URL is invalid: {}", url); + return true; + } + return false; + } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerDependency.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerDependency.java index 2ab329abe..c2d8f2fed 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerDependency.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerDependency.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.data.composer; +import javax.annotation.concurrent.ThreadSafe; + /** * Represents a dependency (GAV, right now) from a Composer dependency. * * @author colezlaw */ +@ThreadSafe public final class ComposerDependency { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerException.java index 16be04bc5..af215bff5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerException.java @@ -17,11 +17,15 @@ */ package org.owasp.dependencycheck.data.composer; +import javax.annotation.concurrent.ThreadSafe; + /** - * Represents an exception when handling a composer.json or composer.lock file. Generally used to wrap a downstream exception. + * Represents an exception when handling a composer.json or composer.lock file. + * Generally used to wrap a downstream exception. * * @author colezlaw */ +@ThreadSafe public class ComposerException extends RuntimeException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java index 0803276d1..929d8a4db 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/composer/ComposerLockParser.java @@ -29,12 +29,14 @@ import javax.json.stream.JsonParsingException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; /** * Parses a Composer.lock file from an input stream. In a separate class so it can hopefully be injected. * * @author colezlaw */ +@NotThreadSafe public class ComposerLockParser { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java index d40b0dd53..221a9a4b6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/CpeMemoryIndex.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.concurrent.ThreadSafe; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.core.KeywordAnalyzer; import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper; @@ -47,21 +49,29 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** + *

* An in memory Lucene index that contains the vendor/product combinations from - * the CPE (application) identifiers within the NVD CVE data. + * the CPE (application) identifiers within the NVD CVE data.

+ * + * This is the last remaining singleton in dependency-check-core; The use of + * this singleton - while it may not technically be thread-safe (one database + * used to build this index may not have the same entries as another) the risk + * of this is currently believed to be small. As this memory index consumes a + * large amount of memory we will remain using the singleton pattern for now. * * @author Jeremy Long */ +@ThreadSafe public final class CpeMemoryIndex implements AutoCloseable { + /** + * Singleton instance. + */ + private static final CpeMemoryIndex INSTANCE = new CpeMemoryIndex(); /** * The logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(CpeMemoryIndex.class); - /** - * singleton instance. - */ - private static final CpeMemoryIndex INSTANCE = new CpeMemoryIndex(); /** * The in memory Lucene index. */ @@ -82,19 +92,11 @@ public final class CpeMemoryIndex implements AutoCloseable { * The Lucene QueryParser used for Searching. */ private QueryParser queryParser; - /** - * The search field analyzer for the product field. - */ - private SearchFieldAnalyzer productFieldAnalyzer; - /** - * The search field analyzer for the vendor field. - */ - private SearchFieldAnalyzer vendorFieldAnalyzer; /** * Track the number of current users of the Lucene index; used to track it * it is okay to actually close the index. */ - private int usageCount = 0; + private final AtomicInteger usageCount = new AtomicInteger(0); /** * private constructor for singleton. @@ -118,8 +120,7 @@ public final class CpeMemoryIndex implements AutoCloseable { * @throws IndexException thrown if there is an error creating the index */ public synchronized void open(CveDB cve) throws IndexException { - if (INSTANCE.usageCount <= 0) { - INSTANCE.usageCount = 0; + if (INSTANCE.usageCount.addAndGet(1) == 1) { index = new RAMDirectory(); buildIndex(cve); try { @@ -131,7 +132,6 @@ public final class CpeMemoryIndex implements AutoCloseable { searchingAnalyzer = createSearchingAnalyzer(); queryParser = new QueryParser(LuceneUtils.CURRENT_VERSION, Fields.DOCUMENT_KEY, searchingAnalyzer); } - INSTANCE.usageCount += 1; } /** @@ -140,7 +140,7 @@ public final class CpeMemoryIndex implements AutoCloseable { * @return whether or not the index is open */ public synchronized boolean isOpen() { - return INSTANCE.usageCount > 0; + return INSTANCE.usageCount.get() > 0; } /** @@ -151,8 +151,8 @@ public final class CpeMemoryIndex implements AutoCloseable { private Analyzer createSearchingAnalyzer() { final Map fieldAnalyzers = new HashMap<>(); fieldAnalyzers.put(Fields.DOCUMENT_KEY, new KeywordAnalyzer()); - productFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION); - vendorFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION); + final SearchFieldAnalyzer productFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION); + final SearchFieldAnalyzer vendorFieldAnalyzer = new SearchFieldAnalyzer(LuceneUtils.CURRENT_VERSION); fieldAnalyzers.put(Fields.PRODUCT, productFieldAnalyzer); fieldAnalyzers.put(Fields.VENDOR, vendorFieldAnalyzer); @@ -164,8 +164,9 @@ public final class CpeMemoryIndex implements AutoCloseable { */ @Override public synchronized void close() { - INSTANCE.usageCount -= 1; - if (INSTANCE.usageCount <= 0) { + final int count = INSTANCE.usageCount.get() - 1; + if (count <= 0) { + INSTANCE.usageCount.set(0); if (searchingAnalyzer != null) { searchingAnalyzer.close(); searchingAnalyzer = null; @@ -218,8 +219,6 @@ public final class CpeMemoryIndex implements AutoCloseable { } catch (DatabaseException ex) { LOGGER.debug("", ex); throw new IndexException("Error reading CPE data", ex); - } catch (CorruptIndexException ex) { - throw new IndexException("Unable to close an in-memory index", ex); } catch (IOException ex) { throw new IndexException("Unable to close an in-memory index", ex); } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/Fields.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/Fields.java index 195348249..96666dee5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/Fields.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/Fields.java @@ -17,11 +17,15 @@ */ package org.owasp.dependencycheck.data.cpe; +import javax.annotation.concurrent.ThreadSafe; + /** - * Fields is a collection of field names used within the Lucene index for CPE entries. + * Fields is a collection of field names used within the Lucene index for CPE + * entries. * * @author Jeremy Long */ +@ThreadSafe public final class Fields { /** @@ -38,7 +42,8 @@ public final class Fields { public static final String PRODUCT = "product"; /** - * Private constructor as this is more of an enumeration rather then a full class. + * Private constructor as this is more of an enumeration rather then a full + * class. */ private Fields() { } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexEntry.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexEntry.java index 54efbafd4..44ad9be1b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexEntry.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexEntry.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.data.cpe; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.StringUtils; /** @@ -27,6 +28,7 @@ import org.apache.commons.lang3.StringUtils; * * @author Jeremy Long */ +@ThreadSafe public class IndexEntry implements Serializable { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexException.java index deb0f69e9..f6b41cd4f 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cpe/IndexException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.data.cpe; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception thrown when the there is an issue using the in-memory CPE Index. * * @author Jeremy Long */ +@ThreadSafe public class IndexException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java index d07232282..6b8735050 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweDB.java @@ -26,11 +26,13 @@ import java.io.InputStream; import java.io.ObjectInputStream; import java.util.HashMap; import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; /** * * @author Jeremy Long */ +@ThreadSafe public final class CweDB { /** @@ -59,9 +61,7 @@ public final class CweDB { final String filePath = "data/cwe.hashmap.serialized"; try (InputStream input = FileUtils.getResourceAsStream(filePath); ObjectInputStream oin = new ObjectInputStream(input)) { - - final Map ret = (HashMap) oin.readObject(); - return ret; + return (HashMap) oin.readObject(); } catch (ClassNotFoundException ex) { LOGGER.warn("Unable to load CWE data. This should not be an issue."); LOGGER.debug("", ex); @@ -79,7 +79,7 @@ public final class CweDB { * @param cweId the CWE ID * @return the full name of the CWE */ - public static String getCweName(String cweId) { + public static synchronized String getCweName(String cweId) { if (cweId != null) { return CWE.get(cweId); } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweHandler.java index 894aed93a..398670f4d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/cwe/CweHandler.java @@ -18,6 +18,7 @@ package org.owasp.dependencycheck.data.cwe; import java.util.HashMap; +import javax.annotation.concurrent.NotThreadSafe; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -27,6 +28,7 @@ import org.xml.sax.helpers.DefaultHandler; * * @author Jeremy Long */ +@NotThreadSafe public class CweHandler extends DefaultHandler { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AbstractTokenizingFilter.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AbstractTokenizingFilter.java index 25e943378..b8063bac9 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AbstractTokenizingFilter.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AbstractTokenizingFilter.java @@ -18,15 +18,18 @@ package org.owasp.dependencycheck.data.lucene; import java.util.LinkedList; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; /** - * An abstract tokenizing filter that can be used as the base for a tokenizing filter. + * An abstract tokenizing filter that can be used as the base for a tokenizing + * filter. * * @author Jeremy Long */ +@NotThreadSafe public abstract class AbstractTokenizingFilter extends TokenFilter { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java index 11f0ef562..2cf69f403 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/AlphaNumericTokenizer.java @@ -18,14 +18,17 @@ package org.owasp.dependencycheck.data.lucene; import java.io.Reader; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.lucene.analysis.util.CharTokenizer; import org.apache.lucene.util.Version; /** - * Tokenizes the input breaking it into tokens when non-alpha/numeric characters are found. + * Tokenizes the input breaking it into tokens when non-alpha/numeric characters + * are found. * * @author Jeremy Long */ +@NotThreadSafe public class AlphaNumericTokenizer extends CharTokenizer { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/DependencySimilarity.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/DependencySimilarity.java index 0201239aa..7518b0eb9 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/DependencySimilarity.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/DependencySimilarity.java @@ -17,12 +17,14 @@ */ package org.owasp.dependencycheck.data.lucene; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.lucene.search.similarities.DefaultSimilarity; /** * * @author Jeremy Long */ +@NotThreadSafe public class DependencySimilarity extends DefaultSimilarity { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java index 2fac02e15..614efe6b4 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/LuceneUtils.java @@ -18,19 +18,22 @@ package org.owasp.dependencycheck.data.lucene; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import javax.annotation.concurrent.ThreadSafe; import org.apache.lucene.util.Version; /** *

- * Lucene utils is a set of utilize written to make constructing Lucene queries simpler.

+ * Lucene utils is a set of utilize written to make constructing Lucene queries + * simpler.

* * @author Jeremy Long */ +@ThreadSafe public final class LuceneUtils { /** - * The current version of Lucene being used. Declaring this one place so an upgrade doesn't require hunting through the code - * base. + * The current version of Lucene being used. Declaring this one place so an + * upgrade doesn't require hunting through the code base. */ public static final Version CURRENT_VERSION = Version.LUCENE_47; @@ -41,7 +44,8 @@ public final class LuceneUtils { } /** - * Appends the text to the supplied StringBuilder escaping Lucene control characters in the process. + * Appends the text to the supplied StringBuilder escaping Lucene control + * characters in the process. * * @param buf a StringBuilder to append the escaped text to * @param text the data to be escaped @@ -88,7 +92,8 @@ public final class LuceneUtils { } /** - * Escapes the text passed in so that it is treated as data instead of control characters. + * Escapes the text passed in so that it is treated as data instead of + * control characters. * * @param text data to be escaped * @return the escaped text. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/SearchFieldAnalyzer.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/SearchFieldAnalyzer.java index 8b75f4c10..dddd9f535 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/SearchFieldAnalyzer.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/SearchFieldAnalyzer.java @@ -18,8 +18,6 @@ package org.owasp.dependencycheck.data.lucene; import java.io.Reader; -import java.util.Arrays; -import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; @@ -44,8 +42,8 @@ public class SearchFieldAnalyzer extends Analyzer { /** * The list of additional stop words to use. */ - private static final List ADDITIONAL_STOP_WORDS = Arrays.asList("software", "framework", "inc", - "com", "org", "net", "www", "consulting", "ltd", "foundation", "project"); + private static final String[] ADDITIONAL_STOP_WORDS = {"software", "framework", "inc", + "com", "org", "net", "www", "consulting", "ltd", "foundation", "project"}; /** * The set of stop words to use in the analyzer. */ @@ -57,8 +55,8 @@ public class SearchFieldAnalyzer extends Analyzer { * @return the set of stop words being used */ public static CharArraySet getStopWords() { - CharArraySet words = new CharArraySet(LuceneUtils.CURRENT_VERSION, StopAnalyzer.ENGLISH_STOP_WORDS_SET, true); - words.addAll(ADDITIONAL_STOP_WORDS); + final CharArraySet words = StopFilter.makeStopSet(LuceneUtils.CURRENT_VERSION, ADDITIONAL_STOP_WORDS, true); + words.addAll(StopAnalyzer.ENGLISH_STOP_WORDS_SET); return words; } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/TokenPairConcatenatingFilter.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/TokenPairConcatenatingFilter.java index 0d966b528..7e712ab91 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/TokenPairConcatenatingFilter.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/TokenPairConcatenatingFilter.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.data.lucene; import java.io.IOException; import java.util.LinkedList; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; @@ -33,6 +34,7 @@ import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; * * @author Jeremy Long */ +@NotThreadSafe public final class TokenPairConcatenatingFilter extends TokenFilter { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilter.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilter.java index db157f784..57bcfe729 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilter.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/lucene/UrlTokenizingFilter.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.util.LinkedList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.owasp.dependencycheck.utils.UrlStringUtils; @@ -28,13 +29,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - *

- * Takes a TokenStream and splits or adds tokens to correctly index version numbers.

- *

- * Example: "3.0.0.RELEASE" -> "3 3.0 3.0.0 RELEASE 3.0.0.RELEASE".

+ * + * Takes a TokenStream, looks for URLs, and breaks them into separate tokens. * * @author Jeremy Long */ +@NotThreadSafe public final class UrlTokenizingFilter extends AbstractTokenizingFilter { /** @@ -52,8 +52,9 @@ public final class UrlTokenizingFilter extends AbstractTokenizingFilter { } /** - * Increments the underlying TokenStream and sets CharTermAttributes to construct an expanded set of tokens by concatenating - * tokens with the previous token. + * Increments the underlying TokenStream and sets CharTermAttributes to + * construct an expanded set of tokens by concatenating tokens with the + * previous token. * * @return whether or not we have hit the end of the TokenStream * @throws IOException is thrown when an IOException occurs diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/MavenArtifact.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/MavenArtifact.java index e69ee8d76..d9fa706b2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/MavenArtifact.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/MavenArtifact.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.data.nexus; +import javax.annotation.concurrent.ThreadSafe; + /** * Simple bean representing a Maven Artifact. * * @author colezlaw */ +@ThreadSafe public class MavenArtifact { /** @@ -45,7 +48,8 @@ public class MavenArtifact { private String version; /** - * The artifact url. This may change depending on which Nexus server the search took place. + * The artifact url. This may change depending on which Nexus server the + * search took place. */ private String artifactUrl; /** @@ -80,7 +84,8 @@ public class MavenArtifact { * @param version the version * @param jarAvailable if the jar file is available from central * @param pomAvailable if the pom file is available from central - * @param secureDownload if the jar and pom files should be downloaded using HTTPS. + * @param secureDownload if the jar and pom files should be downloaded using + * HTTPS. */ public MavenArtifact(String groupId, String artifactId, String version, boolean jarAvailable, boolean pomAvailable, boolean secureDownload) { this.groupId = groupId; @@ -220,5 +225,3 @@ public class MavenArtifact { } } - -// vim: cc=120:sw=4:ts=4:sts=4 diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/NexusSearch.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/NexusSearch.java index 09567475f..0f4eac34c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/NexusSearch.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nexus/NexusSearch.java @@ -20,12 +20,15 @@ package org.owasp.dependencycheck.data.nexus; import java.io.FileNotFoundException; import java.io.IOException; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; +import javax.annotation.concurrent.ThreadSafe; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.URLConnectionFactory; import org.owasp.dependencycheck.utils.XmlUtils; @@ -39,6 +42,7 @@ import org.xml.sax.SAXException; * * @author colezlaw */ +@ThreadSafe public class NexusSearch { /** @@ -50,6 +54,10 @@ public class NexusSearch { * Whether to use the Proxy when making requests. */ private final boolean useProxy; + /** + * The configured settings. + */ + private final Settings settings; /** * Used for logging. */ @@ -58,15 +66,19 @@ public class NexusSearch { /** * Creates a NexusSearch for the given repository URL. * - * @param rootURL the root URL of the repository on which searches should - * execute. full URL's are calculated relative to this URL, so it should end - * with a / + * @param settings the configured settings * @param useProxy flag indicating if the proxy settings should be used + * @throws java.net.MalformedURLException thrown if the configured URL is + * invalid */ - public NexusSearch(URL rootURL, boolean useProxy) { - this.rootURL = rootURL; + public NexusSearch(Settings settings, boolean useProxy) throws MalformedURLException { + this.settings = settings; this.useProxy = useProxy; - LOGGER.debug("Using proxy: {}", useProxy); + + final String searchUrl = settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL); + LOGGER.debug("Nexus Search URL: {}", searchUrl); + this.rootURL = new URL(searchUrl); + } /** @@ -94,7 +106,8 @@ public class NexusSearch { // 2) Otherwise, don't use the proxy (either the proxy isn't configured, // or proxy is specifically set to false HttpURLConnection conn; - conn = URLConnectionFactory.createHttpURLConnection(url, useProxy); + final URLConnectionFactory factory = new URLConnectionFactory(settings); + conn = factory.createHttpURLConnection(url, useProxy); conn.setDoOutput(true); // JSON would be more elegant, but there's not currently a dependency @@ -159,7 +172,8 @@ public class NexusSearch { HttpURLConnection conn; try { final URL url = new URL(rootURL, "status"); - conn = URLConnectionFactory.createHttpURLConnection(url, useProxy); + final URLConnectionFactory factory = new URLConnectionFactory(settings); + conn = factory.createHttpURLConnection(url, useProxy); conn.addRequestProperty("Accept", "application/xml"); conn.connect(); if (conn.getResponseCode() != 200) { @@ -176,9 +190,6 @@ public class NexusSearch { } catch (IOException | ParserConfigurationException | SAXException e) { return false; } - return true; } } - -// vim: cc=120:sw=4:ts=4:sts=4 diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/Advisory.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/Advisory.java index 8736db13c..f6ec6a978 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/Advisory.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/Advisory.java @@ -18,6 +18,7 @@ package org.owasp.dependencycheck.data.nsp; import java.util.Arrays; +import javax.annotation.concurrent.ThreadSafe; /** * The response from NSP check API will respond with 0 or more advisories. This @@ -25,6 +26,7 @@ import java.util.Arrays; * * @author Steve Springett */ +@ThreadSafe public class Advisory { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/NspSearch.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/NspSearch.java index e3ed19024..327952a25 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/NspSearch.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/NspSearch.java @@ -23,10 +23,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.utils.Settings; import org.owasp.dependencycheck.utils.URLConnectionFactory; @@ -36,6 +38,8 @@ import javax.json.Json; import javax.json.JsonArray; import javax.json.JsonObject; import javax.json.JsonReader; +import javax.json.JsonValue.ValueType; +import static org.owasp.dependencycheck.analyzer.NspAnalyzer.DEFAULT_URL; import org.owasp.dependencycheck.utils.URLConnectionFailureException; /** @@ -43,6 +47,7 @@ import org.owasp.dependencycheck.utils.URLConnectionFailureException; * * @author Steve Springett */ +@ThreadSafe public class NspSearch { /** @@ -54,7 +59,10 @@ public class NspSearch { * Whether to use the Proxy when making requests. */ private final boolean useProxy; - + /** + * The configured settings. + */ + private final Settings settings; /** * Used for logging. */ @@ -63,11 +71,16 @@ public class NspSearch { /** * Creates a NspSearch for the given repository URL. * - * @param nspCheckUrl the URL to the public NSP check API + * @param settings the configured settings + * @throws java.net.MalformedURLException thrown if the configured URL is + * invalid */ - public NspSearch(URL nspCheckUrl) { - this.nspCheckUrl = nspCheckUrl; - if (null != Settings.getString(Settings.KEYS.PROXY_SERVER)) { + public NspSearch(Settings settings) throws MalformedURLException { + final String searchUrl = settings.getString(Settings.KEYS.ANALYZER_NSP_URL, DEFAULT_URL); + LOGGER.debug("NSP Search URL: {}", searchUrl); + this.nspCheckUrl = new URL(searchUrl); + this.settings = settings; + if (null != settings.getString(Settings.KEYS.PROXY_SERVER)) { useProxy = true; LOGGER.debug("Using proxy"); } else { @@ -90,8 +103,8 @@ public class NspSearch { try { final List result = new ArrayList<>(); final byte[] packageDatabytes = packageJson.toString().getBytes(StandardCharsets.UTF_8); - - final HttpURLConnection conn = URLConnectionFactory.createHttpURLConnection(nspCheckUrl, useProxy); + final URLConnectionFactory factory = new URLConnectionFactory(settings); + final HttpURLConnection conn = factory.createHttpURLConnection(nspCheckUrl, useProxy); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestMethod("POST"); @@ -121,7 +134,13 @@ public class NspSearch { advisory.setOverview(object.getString("overview")); advisory.setRecommendation(object.getString("recommendation", null)); advisory.setCvssVector(object.getString("cvss_vector", null)); - advisory.setCvssScore(Float.parseFloat(object.getJsonNumber("cvss_score").toString())); + + if (object.get("cvss_score").getValueType() != ValueType.NULL) { + advisory.setCvssScore(Float.parseFloat(object.getJsonNumber("cvss_score").toString())); + } else { + advisory.setCvssScore(-1); + } + advisory.setModule(object.getString("module", null)); advisory.setVersion(object.getString("version", null)); advisory.setVulnerableVersions(object.getString("vulnerable_versions", null)); @@ -141,6 +160,7 @@ public class NspSearch { } } break; + case 400: LOGGER.debug("Invalid payload submitted to Node Security Platform. Received response code: {} {}", conn.getResponseCode(), conn.getResponseMessage()); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/SanitizePackage.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/SanitizePackage.java index 26ac4dd16..4bee31ceb 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/SanitizePackage.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nsp/SanitizePackage.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; /** * Class used to create a Sanitized version of package.json suitable for @@ -32,6 +33,7 @@ import java.util.Map; * * @author Steve Springett */ +@ThreadSafe public final class SanitizePackage { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NugetPackage.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NugetPackage.java index 3897b3416..50c329654 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NugetPackage.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NugetPackage.java @@ -17,12 +17,16 @@ */ package org.owasp.dependencycheck.data.nuget; +import javax.annotation.concurrent.ThreadSafe; + /** * Represents the contents of a Nuspec manifest. * * @author colezlaw */ +@ThreadSafe public class NugetPackage { + /** * The id. */ @@ -55,6 +59,7 @@ public class NugetPackage { /** * Sets the id. + * * @param id the id */ public void setId(String id) { @@ -63,6 +68,7 @@ public class NugetPackage { /** * Gets the id. + * * @return the id */ public String getId() { @@ -71,6 +77,7 @@ public class NugetPackage { /** * Sets the version. + * * @param version the version */ public void setVersion(String version) { @@ -79,6 +86,7 @@ public class NugetPackage { /** * Gets the version. + * * @return the version */ public String getVersion() { @@ -87,6 +95,7 @@ public class NugetPackage { /** * Sets the title. + * * @param title the title */ public void setTitle(String title) { @@ -95,6 +104,7 @@ public class NugetPackage { /** * Gets the title. + * * @return the title */ public String getTitle() { @@ -103,6 +113,7 @@ public class NugetPackage { /** * Sets the authors. + * * @param authors the authors */ public void setAuthors(String authors) { @@ -111,6 +122,7 @@ public class NugetPackage { /** * Gets the authors. + * * @return the authors */ public String getAuthors() { @@ -119,6 +131,7 @@ public class NugetPackage { /** * Sets the owners. + * * @param owners the owners */ public void setOwners(String owners) { @@ -127,6 +140,7 @@ public class NugetPackage { /** * Gets the owners. + * * @return the owners */ public String getOwners() { @@ -135,6 +149,7 @@ public class NugetPackage { /** * Sets the licenseUrl. + * * @param licenseUrl the licenseUrl */ public void setLicenseUrl(String licenseUrl) { @@ -143,6 +158,7 @@ public class NugetPackage { /** * Gets the licenseUrl. + * * @return the licenseUrl */ public String getLicenseUrl() { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParseException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParseException.java index 5ab31ddb5..8156e35d6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParseException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParseException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.data.nuget; +import javax.annotation.concurrent.ThreadSafe; + /** * Exception during the parsing of a Nuspec file. * * @author colezlaw */ +@ThreadSafe public class NuspecParseException extends Exception { /** @@ -32,19 +35,20 @@ public class NuspecParseException extends Exception { /** * Constructs a new exception with null as its detail message. * - * The cause is not initialized, and may subsequently be initialized by a call to - * {@link java.lang.Throwable#initCause(java.lang.Throwable)}. + * The cause is not initialized, and may subsequently be initialized by a + * call to {@link java.lang.Throwable#initCause(java.lang.Throwable)}. */ public NuspecParseException() { super(); } /** - * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently - * be initialized by a call to {@link java.lang.Throwable#initCause(java.lang.Throwable)}. + * Constructs a new exception with the specified detail message. The cause + * is not initialized, and may subsequently be initialized by a call to + * {@link java.lang.Throwable#initCause(java.lang.Throwable)}. * - * @param message the detail message. The detail message is saved for later retrieval by the - * {@link java.lang.Throwable#getMessage()} method. + * @param message the detail message. The detail message is saved for later + * retrieval by the {@link java.lang.Throwable#getMessage()} method. */ public NuspecParseException(String message) { super(message); @@ -53,13 +57,16 @@ public class NuspecParseException extends Exception { /** * Constructs a new exception with the specified detail message and cause. * - * Note that the detail message associated with cause is not + * Note that the detail message associated with cause is + * not * automatically incorporated in this exception's detail message. * - * @param message the detail message (which is saved for later retrieval by the - * {@link java.lang.Throwable#getMessage()} method. - * @param cause the cause (which is saved for later retrieval by the {@link java.lang.Throwable#getCause()} method). - * (A null value is permitted, and indicates that the cause is nonexistent or unknown). + * @param message the detail message (which is saved for later retrieval by + * the {@link java.lang.Throwable#getMessage()} method. + * @param cause the cause (which is saved for later retrieval by the + * {@link java.lang.Throwable#getCause()} method). (A null + * value is permitted, and indicates that the cause is nonexistent or + * unknown). */ public NuspecParseException(String message, Throwable cause) { super(message, cause); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParser.java index 04a6f8362..13bacd45a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParser.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/NuspecParser.java @@ -26,6 +26,7 @@ import java.io.InputStream; * */ public interface NuspecParser { + /** * Parse an input stream and return the resulting {@link NugetPackage}. * diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/XPathNuspecParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/XPathNuspecParser.java index fa91cb73f..1a7e2ab18 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/XPathNuspecParser.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nuget/XPathNuspecParser.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.data.nuget; import java.io.IOException; import java.io.InputStream; +import javax.annotation.concurrent.ThreadSafe; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; @@ -35,6 +36,7 @@ import org.xml.sax.SAXException; * * @author colezlaw */ +@ThreadSafe public class XPathNuspecParser implements NuspecParser { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactory.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactory.java index e7a815c4c..f21972f82 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactory.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactory.java @@ -27,6 +27,7 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.io.IOUtils; import org.owasp.dependencycheck.utils.DBUtils; import org.owasp.dependencycheck.utils.DependencyVersion; @@ -44,16 +45,13 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public final class ConnectionFactory { /** * The Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionFactory.class); - /** - * The version of the current DB Schema. - */ - public static final String DB_SCHEMA_VERSION = Settings.getString(Settings.KEYS.DB_VERSION); /** * Resource location for SQL file used to create the database schema. */ @@ -69,29 +67,36 @@ public final class ConnectionFactory { /** * The database driver used to connect to the database. */ - private static Driver driver = null; + private Driver driver = null; /** * The database connection string. */ - private static String connectionString = null; + private String connectionString = null; /** * The username to connect to the database. */ - private static String userName = null; + private String userName = null; /** * The password for the database. */ - private static String password = null; + private String password = null; /** * Counter to ensure that calls to ensureSchemaVersion does not end up in an * endless loop. */ - private static int callDepth = 0; + private int callDepth = 0; + /** + * The configured settings. + */ + private final Settings settings; /** * Private constructor for this factory class; no instance is ever needed. + * + * @param settings the configured settings */ - private ConnectionFactory() { + public ConnectionFactory(Settings settings) { + this.settings = settings; } /** @@ -101,7 +106,7 @@ public final class ConnectionFactory { * @throws DatabaseException thrown if we are unable to connect to the * database */ - public static void initialize() throws DatabaseException { + public synchronized void initialize() throws DatabaseException { //this only needs to be called once. if (connectionString != null) { return; @@ -109,27 +114,23 @@ public final class ConnectionFactory { Connection conn = null; try { //load the driver if necessary - final String driverName = Settings.getString(Settings.KEYS.DB_DRIVER_NAME, ""); - if (!driverName.isEmpty()) { //likely need to load the correct driver - LOGGER.debug("Loading driver: {}", driverName); - final String driverPath = Settings.getString(Settings.KEYS.DB_DRIVER_PATH, ""); + final String driverName = settings.getString(Settings.KEYS.DB_DRIVER_NAME, ""); + final String driverPath = settings.getString(Settings.KEYS.DB_DRIVER_PATH, ""); + if (!driverPath.isEmpty()) { + LOGGER.debug("Loading driver '{}' from '{}'", driverName, driverPath); try { - if (!driverPath.isEmpty()) { - LOGGER.debug("Loading driver from: {}", driverPath); - driver = DriverLoader.load(driverName, driverPath); - } else { - driver = DriverLoader.load(driverName); - } + LOGGER.debug("Loading driver from: {}", driverPath); + driver = DriverLoader.load(driverName, driverPath); } catch (DriverLoadException ex) { LOGGER.debug("Unable to load database driver", ex); throw new DatabaseException("Unable to load database driver", ex); } } - userName = Settings.getString(Settings.KEYS.DB_USER, "dcuser"); + userName = settings.getString(Settings.KEYS.DB_USER, "dcuser"); //yes, yes - hard-coded password - only if there isn't one in the properties file. - password = Settings.getString(Settings.KEYS.DB_PASSWORD, "DC-Pass1337!"); + password = settings.getString(Settings.KEYS.DB_PASSWORD, "DC-Pass1337!"); try { - connectionString = Settings.getConnectionString( + connectionString = settings.getConnectionString( Settings.KEYS.DB_CONNECTION_STRING, Settings.KEYS.DB_FILE_NAME); } catch (IOException ex) { @@ -158,7 +159,7 @@ public final class ConnectionFactory { connectionString = connectionString.replace("AUTO_SERVER=TRUE;", ""); try { conn = DriverManager.getConnection(connectionString, userName, password); - Settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString); + settings.setString(Settings.KEYS.DB_CONNECTION_STRING, connectionString); LOGGER.debug("Unable to start the database in server mode; reverting to single user mode"); } catch (SQLException sqlex) { LOGGER.debug("Unable to connect to the database", ex); @@ -169,7 +170,6 @@ public final class ConnectionFactory { throw new DatabaseException("Unable to connect to the database", ex); } } - if (shouldCreateSchema) { try { createTables(conn); @@ -201,16 +201,9 @@ public final class ConnectionFactory { * finalize method being called as during shutdown the class loader used to * load the driver may be unloaded prior to the driver being de-registered. */ - public static void cleanup() { + public synchronized void cleanup() { if (driver != null) { - try { - DriverManager.deregisterDriver(driver); - } catch (SQLException ex) { - LOGGER.debug("An error occurred unloading the database driver", ex); - } catch (Throwable unexpected) { - LOGGER.debug( - "An unexpected throwable occurred unloading the database driver", unexpected); - } + DriverLoader.cleanup(driver); driver = null; } connectionString = null; @@ -226,7 +219,7 @@ public final class ConnectionFactory { * @throws DatabaseException thrown if there is an exception loading the * database connection */ - public static Connection getConnection() throws DatabaseException { + public synchronized Connection getConnection() throws DatabaseException { initialize(); Connection conn = null; try { @@ -246,22 +239,57 @@ public final class ConnectionFactory { * @throws IOException thrown if the data directory does not exist and * cannot be created */ - public static boolean h2DataFileExists() throws IOException { - final File dir = Settings.getDataDirectory(); - final String fileName = Settings.getString(Settings.KEYS.DB_FILE_NAME); - final File file = new File(dir, fileName); + public boolean h2DataFileExists() throws IOException { + return h2DataFileExists(settings); + } + + /** + * Determines if the H2 database file exists. If it does not exist then the + * data structure will need to be created. + * + * @param configuration the configured settings + * @return true if the H2 database file does not exist; otherwise false + * @throws IOException thrown if the data directory does not exist and + * cannot be created + */ + public static boolean h2DataFileExists(Settings configuration) throws IOException { + final File file = getH2DataFile(configuration); return file.exists(); } + /** + * Returns a reference to the H2 database file. + * + * @param configuration the configured settings + * @return the path to the H2 database file + * @throws IOException thrown if there is an error + */ + public static File getH2DataFile(Settings configuration) throws IOException { + final File dir = configuration.getDataDirectory(); + final String fileName = configuration.getString(Settings.KEYS.DB_FILE_NAME); + final File file = new File(dir, fileName); + return file; + } + /** * Determines if the connection string is for an H2 database. * * @return true if the connection string is for an H2 database */ - public static boolean isH2Connection() { + public boolean isH2Connection() { + return isH2Connection(settings); + } + + /** + * Determines if the connection string is for an H2 database. + * + * @param configuration the configured settings + * @return true if the connection string is for an H2 database + */ + public static boolean isH2Connection(Settings configuration) { String connStr; try { - connStr = Settings.getConnectionString( + connStr = configuration.getConnectionString( Settings.KEYS.DB_CONNECTION_STRING, Settings.KEYS.DB_FILE_NAME); } catch (IOException ex) { @@ -278,7 +306,7 @@ public final class ConnectionFactory { * @param conn the database connection * @throws DatabaseException thrown if there is a Database Exception */ - private static void createTables(Connection conn) throws DatabaseException { + private void createTables(Connection conn) throws DatabaseException { LOGGER.debug("Creating database structure"); InputStream is = null; try { @@ -315,7 +343,7 @@ public final class ConnectionFactory { * @throws DatabaseException thrown if there is an exception upgrading the * database schema */ - private static void updateSchema(Connection conn, DependencyVersion appExpectedVersion, DependencyVersion currentDbVersion) + private void updateSchema(Connection conn, DependencyVersion appExpectedVersion, DependencyVersion currentDbVersion) throws DatabaseException { final String databaseProductName; @@ -363,7 +391,7 @@ public final class ConnectionFactory { final int c1 = Integer.parseInt(currentDbVersion.getVersionParts().get(1)); if (e0 == c0 && e1 < c1) { LOGGER.warn("A new version of dependency-check is available; consider upgrading"); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); + settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); } else if (e0 == c0 && e1 == c1) { //do nothing - not sure how we got here, but just in case... } else { @@ -382,7 +410,7 @@ public final class ConnectionFactory { * @throws DatabaseException thrown if the schema version is not compatible * with this version of dependency-check */ - private static void ensureSchemaVersion(Connection conn) throws DatabaseException { + private void ensureSchemaVersion(Connection conn) throws DatabaseException { ResultSet rs = null; PreparedStatement ps = null; try { @@ -390,7 +418,8 @@ public final class ConnectionFactory { ps = conn.prepareStatement("SELECT value FROM properties WHERE id = 'version'"); rs = ps.executeQuery(); if (rs.next()) { - final DependencyVersion appDbVersion = DependencyVersionUtil.parseVersion(DB_SCHEMA_VERSION); + final String dbSchemaVersion = settings.getString(Settings.KEYS.DB_VERSION); + final DependencyVersion appDbVersion = DependencyVersionUtil.parseVersion(dbSchemaVersion); if (appDbVersion == null) { throw new DatabaseException("Invalid application database schema"); } @@ -399,7 +428,7 @@ public final class ConnectionFactory { throw new DatabaseException("Invalid database schema"); } if (appDbVersion.compareTo(db) > 0) { - LOGGER.debug("Current Schema: {}", DB_SCHEMA_VERSION); + LOGGER.debug("Current Schema: {}", dbSchemaVersion); LOGGER.debug("DB Schema: {}", rs.getString(1)); updateSchema(conn, appDbVersion, db); if (++callDepth < 10) { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CorruptDatabaseException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CorruptDatabaseException.java index 7dbdd6adb..a504485ef 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CorruptDatabaseException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CorruptDatabaseException.java @@ -17,11 +17,15 @@ */ package org.owasp.dependencycheck.data.nvdcve; +import javax.annotation.concurrent.ThreadSafe; + /** - * An exception used to indicate the db4o database is corrupt. This could be due to invalid data or a complete failure of the db. + * An exception used to indicate the db4o database is corrupt. This could be due + * to invalid data or a complete failure of the db. * * @author Jeremy Long */ +@ThreadSafe public class CorruptDatabaseException extends DatabaseException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java index c4b08cde3..f67ffb49d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/CveDB.java @@ -50,9 +50,11 @@ import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +//CSOFF: AvoidStarImport +import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*; +//CSON: AvoidStarImport import static org.apache.commons.collections.map.AbstractReferenceMap.HARD; import static org.apache.commons.collections.map.AbstractReferenceMap.SOFT; -import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*; /** * The database holding information about the NVD CVE data. This class is safe @@ -64,19 +66,14 @@ import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb @ThreadSafe public final class CveDB implements AutoCloseable { - /** - * Singleton instance of the CveDB. - */ - private static CveDB instance = null; - /** - * Track the number of current users of the CveDB; so that if someone is - * using database another user cannot close the connection on them. - */ - private int usageCount = 0; /** * The logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(CveDB.class); + /** + * The database connection factory. + */ + private final ConnectionFactory connectionFactory; /** * Database connection */ @@ -100,6 +97,10 @@ public final class CveDB implements AutoCloseable { */ @SuppressWarnings("unchecked") private final Map> vulnerabilitiesForCpeCache = Collections.synchronizedMap(new ReferenceMap(HARD, SOFT)); + /** + * The configured settings + */ + private final Settings settings; /** * The enum value names must match the keys of the statements in the @@ -196,31 +197,18 @@ public final class CveDB implements AutoCloseable { UPDATE_VULNERABILITY } - /** - * Gets the CveDB singleton object. - * - * @return the CveDB singleton - * @throws DatabaseException thrown if there is a database error - */ - public static synchronized CveDB getInstance() throws DatabaseException { - if (instance == null) { - instance = new CveDB(); - } - if (!instance.isOpen()) { - instance.open(); - } - instance.usageCount += 1; - return instance; - } - /** * Creates a new CveDB object and opens the database connection. Note, the * connection must be closed by the caller by calling the close method. * + * @param settings the configured settings * @throws DatabaseException thrown if there is an exception opening the * database. */ - private CveDB() throws DatabaseException { + public CveDB(Settings settings) throws DatabaseException { + this.settings = settings; + connectionFactory = new ConnectionFactory(settings); + open(); } /** @@ -229,7 +217,7 @@ public final class CveDB implements AutoCloseable { * @param conn the database connection * @return the product name of the database if successful, {@code null} else */ - private static String determineDatabaseProductName(Connection conn) { + private String determineDatabaseProductName(Connection conn) { try { final String databaseProductName = conn.getMetaData().getDatabaseProductName().toLowerCase(); LOGGER.debug("Database product: {}", databaseProductName); @@ -240,16 +228,6 @@ public final class CveDB implements AutoCloseable { } } - /** - * Method added for testing, returns the current usage count of the CveDB - * singleton. - * - * @return the current usage of the CveDB singleton - */ - protected synchronized int getUsageCount() { - return usageCount; - } - /** * Opens the database connection. If the database does not exist, it will * create a new one. @@ -259,14 +237,14 @@ public final class CveDB implements AutoCloseable { */ private synchronized void open() throws DatabaseException { try { - if (!instance.isOpen()) { - instance.connection = ConnectionFactory.getConnection(); - final String databaseProductName = determineDatabaseProductName(instance.connection); - instance.statementBundle = databaseProductName != null + if (!isOpen()) { + connection = connectionFactory.getConnection(); + final String databaseProductName = determineDatabaseProductName(this.connection); + statementBundle = databaseProductName != null ? ResourceBundle.getBundle("data/dbStatements", new Locale(databaseProductName)) : ResourceBundle.getBundle("data/dbStatements"); - instance.prepareStatements(); - instance.databaseProperties = new DatabaseProperties(instance); + prepareStatements(); + databaseProperties = new DatabaseProperties(this); } } catch (DatabaseException e) { releaseResources(); @@ -280,23 +258,20 @@ public final class CveDB implements AutoCloseable { */ @Override public synchronized void close() { - if (instance != null) { - instance.usageCount -= 1; - if (instance.usageCount <= 0 && instance.isOpen()) { - instance.usageCount = 0; - clearCache(); - instance.closeStatements(); - try { - instance.connection.close(); - } catch (SQLException ex) { - LOGGER.error("There was an error attempting to close the CveDB, see the log for more details."); - LOGGER.debug("", ex); - } catch (Throwable ex) { - LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details."); - LOGGER.debug("", ex); - } - releaseResources(); + if (isOpen()) { + clearCache(); + closeStatements(); + try { + connection.close(); + } catch (SQLException ex) { + LOGGER.error("There was an error attempting to close the CveDB, see the log for more details."); + LOGGER.debug("", ex); + } catch (Throwable ex) { + LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details."); + LOGGER.debug("", ex); } + releaseResources(); + connectionFactory.cleanup(); } } @@ -304,10 +279,10 @@ public final class CveDB implements AutoCloseable { * Releases the resources used by CveDB. */ private synchronized void releaseResources() { - instance.statementBundle = null; - instance.preparedStatements.clear(); - instance.databaseProperties = null; - instance.connection = null; + statementBundle = null; + preparedStatements.clear(); + databaseProperties = null; + connection = null; } /** @@ -379,6 +354,7 @@ public final class CveDB implements AutoCloseable { * * @throws SQLException thrown if a SQL Exception occurs */ + @SuppressWarnings("EmptyMethod") public synchronized void commit() throws SQLException { //temporary remove this as autocommit is on. //if (isOpen()) { @@ -539,7 +515,7 @@ public final class CveDB implements AutoCloseable { * * It should be also called when DB is closed. */ - private void clearCache() { + private synchronized void clearCache() { vulnerabilitiesForCpeCache.clear(); } @@ -836,15 +812,15 @@ public final class CveDB implements AutoCloseable { } catch (Exception ex) { String dd; try { - dd = Settings.getDataDirectory().getAbsolutePath(); + dd = settings.getDataDirectory().getAbsolutePath(); } catch (IOException ex1) { - dd = Settings.getString(Settings.KEYS.DATA_DIRECTORY); + dd = settings.getString(Settings.KEYS.DATA_DIRECTORY); } LOGGER.error("Unable to access the local database.\n\nEnsure that '{}' is a writable directory. " + "If the problem persist try deleting the files in '{}' and running {} again. If the problem continues, please " + "create a log file (see documentation at http://jeremylong.github.io/DependencyCheck/) and open a ticket at " + "https://github.com/jeremylong/DependencyCheck/issues and include the log file.\n\n", - dd, dd, Settings.getString(Settings.KEYS.APPLICATION_NAME)); + dd, dd, settings.getString(Settings.KEYS.APPLICATION_NAME)); LOGGER.debug("", ex); } finally { DBUtils.closeResultSet(rs); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseException.java index 474664302..50af8d496 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.data.nvdcve; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception thrown if an operation against the database fails. * * @author Jeremy Long */ +@ThreadSafe public class DatabaseException extends RuntimeException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java index ea690ba75..d8f97865b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DatabaseProperties.java @@ -97,7 +97,7 @@ public class DatabaseProperties { * * @return whether or not any properties are set */ - public boolean isEmpty() { + public synchronized boolean isEmpty() { return properties == null || properties.isEmpty(); } @@ -107,7 +107,7 @@ public class DatabaseProperties { * @param updatedValue the updated NVD CVE entry * @throws UpdateException is thrown if there is an update exception */ - public void save(NvdCveInfo updatedValue) throws UpdateException { + public synchronized void save(NvdCveInfo updatedValue) throws UpdateException { if (updatedValue == null) { return; } @@ -121,7 +121,7 @@ public class DatabaseProperties { * @param value the property value * @throws UpdateException is thrown if there is an update exception */ - public void save(String key, String value) throws UpdateException { + public synchronized void save(String key, String value) throws UpdateException { properties.put(key, value); cveDB.saveProperty(key, value); } @@ -133,7 +133,7 @@ public class DatabaseProperties { * @param key the property key * @return the value of the property */ - public String getProperty(String key) { + public synchronized String getProperty(String key) { return properties.getProperty(key); } @@ -145,7 +145,7 @@ public class DatabaseProperties { * @param defaultValue the default value * @return the value of the property */ - public String getProperty(String key, String defaultValue) { + public synchronized String getProperty(String key, String defaultValue) { return properties.getProperty(key, defaultValue); } @@ -154,7 +154,7 @@ public class DatabaseProperties { * * @return the collection of Database Properties */ - public Properties getProperties() { + public synchronized Properties getProperties() { return properties; } @@ -165,7 +165,7 @@ public class DatabaseProperties { * * @return a map of the database meta data */ - public Map getMetaData() { + public synchronized Map getMetaData() { final Map map = new TreeMap<>(); for (Entry entry : properties.entrySet()) { final String key = (String) entry.getKey(); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoadException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoadException.java index cf8c0e1d0..37a7df201 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoadException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoadException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.data.nvdcve; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception thrown the database driver is unable to be loaded. * * @author Jeremy Long */ +@ThreadSafe public class DriverLoadException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoader.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoader.java index 1c61fcde9..643974b4d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoader.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverLoader.java @@ -31,12 +31,14 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; /** * DriverLoader is a utility class that is used to load database drivers. * * @author Jeremy Long */ +@ThreadSafe public final class DriverLoader { /** @@ -44,6 +46,21 @@ public final class DriverLoader { */ private static final Logger LOGGER = LoggerFactory.getLogger(DriverLoader.class); + /** + * De-registers the driver. + * + * @param driver the driver to de-register + */ + public static void cleanup(Driver driver) { + try { + DriverManager.deregisterDriver(driver); + } catch (SQLException ex) { + LOGGER.debug("An error occurred unloading the database driver", ex); + } catch (Throwable unexpected) { + LOGGER.debug("An unexpected throwable occurred unloading the database driver", unexpected); + } + } + /** * Private constructor for a utility class. */ @@ -51,25 +68,30 @@ public final class DriverLoader { } /** - * Loads the specified class using the system class loader and registers the driver with the driver manager. + * Loads the specified class using the system class loader and registers the + * driver with the driver manager. * * @param className the fully qualified name of the desired class * @return the loaded Driver * @throws DriverLoadException thrown if the driver cannot be loaded */ public static Driver load(String className) throws DriverLoadException { - final ClassLoader loader = DriverLoader.class.getClassLoader(); //ClassLoader.getSystemClassLoader(); + final ClassLoader loader = DriverLoader.class.getClassLoader(); return load(className, loader); } /** - * Loads the specified class by registering the supplied paths to the class loader and then registers the driver with the - * driver manager. The pathToDriver argument is added to the class loader so that an external driver can be loaded. Note, the - * pathToDriver can contain a semi-colon separated list of paths so any dependencies can be added as needed. If a path in the - * pathToDriver argument is a directory all files in the directory are added to the class path. + * Loads the specified class by registering the supplied paths to the class + * loader and then registers the driver with the driver manager. The + * pathToDriver argument is added to the class loader so that an external + * driver can be loaded. Note, the pathToDriver can contain a semi-colon + * separated list of paths so any dependencies can be added as needed. If a + * path in the pathToDriver argument is a directory all files in the + * directory are added to the class path. * * @param className the fully qualified name of the desired class - * @param pathToDriver the path to the JAR file containing the driver; note, this can be a semi-colon separated list of paths + * @param pathToDriver the path to the JAR file containing the driver; note, + * this can be a semi-colon separated list of paths * @return the loaded Driver * @throws DriverLoadException thrown if the driver cannot be loaded */ @@ -113,7 +135,8 @@ public final class DriverLoader { } /** - * Loads the specified class using the supplied class loader and registers the driver with the driver manager. + * Loads the specified class using the supplied class loader and registers + * the driver with the driver manager. * * @param className the fully qualified name of the desired class * @param loader the class loader to use when loading the driver @@ -125,6 +148,8 @@ public final class DriverLoader { final Class c = Class.forName(className, true, loader); //final Class c = loader.loadClass(className); final Driver driver = (Driver) c.newInstance(); + + //TODO add usage count so we don't de-register a driver that is in use. final Driver shim = new DriverShim(driver); //using the DriverShim to get around the fact that the DriverManager won't register a driver not in the base class path DriverManager.registerDriver(shim); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverShim.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverShim.java index 9d5f04ff0..f98fec30c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverShim.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/nvdcve/DriverShim.java @@ -28,16 +28,19 @@ import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.Properties; +import javax.annotation.concurrent.ThreadSafe; /** *

- * Driver shim to get around the class loader issue with the DriverManager. The following code is a nearly identical - * copy (with more comments and a few more methods implemented) of the DriverShim from:

+ * Driver shim to get around the class loader issue with the DriverManager. The + * following code is a nearly identical copy (with more comments and a few more + * methods implemented) of the DriverShim from:

*
http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
* * @author Jeremy Long * @see java.sql.Driver */ +@ThreadSafe class DriverShim implements Driver { /** @@ -59,12 +62,13 @@ class DriverShim implements Driver { } /** - * Wraps the underlying driver's call to acceptsURL. Returns whether or not the driver can open a connection to the - * given URL. + * Wraps the underlying driver's call to acceptsURL. Returns whether or not + * the driver can open a connection to the given URL. * * @param url the URL of the database * @return true if the wrapped driver can connect to the specified URL - * @throws SQLException thrown if there is an error connecting to the database + * @throws SQLException thrown if there is an error connecting to the + * database * @see java.sql.Driver#acceptsURL(java.lang.String) */ @Override @@ -78,7 +82,8 @@ class DriverShim implements Driver { * @param url the URL of the database * @param info a collection of string/value pairs * @return a Connection object - * @throws SQLException thrown if there is an error connecting to the database + * @throws SQLException thrown if there is an error connecting to the + * database * @see java.sql.Driver#connect(java.lang.String, java.util.Properties) */ @Override @@ -112,7 +117,8 @@ class DriverShim implements Driver { * Wraps the call to the underlying driver's getParentLogger method. * * @return the parent's Logger - * @throws SQLFeatureNotSupportedException thrown if the feature is not supported + * @throws SQLFeatureNotSupportedException thrown if the feature is not + * supported * @see java.sql.Driver#getParentLogger() */ public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException { @@ -140,7 +146,8 @@ class DriverShim implements Driver { * @param info a collection of string/value pairs * @return an array of DriverPropertyInfo objects * @throws SQLException thrown if there is an error accessing the database - * @see java.sql.Driver#getPropertyInfo(java.lang.String, java.util.Properties) + * @see java.sql.Driver#getPropertyInfo(java.lang.String, + * java.util.Properties) */ @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/CachedWebDataSource.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/CachedWebDataSource.java index 4445cbce8..c66ac6af8 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/CachedWebDataSource.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/CachedWebDataSource.java @@ -17,21 +17,25 @@ */ package org.owasp.dependencycheck.data.update; +import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.update.exception.UpdateException; /** - * Defines a data source who's data is retrieved from the Internet. This data can be downloaded and the local cache - * updated. + * Defines a data source who's data is retrieved from the Internet. This data + * can be downloaded and the local cache updated. * * @author Jeremy Long */ public interface CachedWebDataSource { /** - * Determines if an update to the current data store is needed, if it is the new data is downloaded from the - * Internet and imported into the current cached data store. + * Determines if an update to the current data store is needed, if it is the + * new data is downloaded from the Internet and imported into the current + * cached data store. * - * @throws UpdateException is thrown if there is an exception downloading the data or updating the data store. + * @param engine a reference to the dependency-check engine + * @throws UpdateException is thrown if there is an exception downloading + * the data or updating the data store. */ - void update() throws UpdateException; + void update(Engine engine) throws UpdateException; } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/EngineVersionCheck.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/EngineVersionCheck.java index 2bd2eb5ef..ac9c33bf2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/EngineVersionCheck.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/EngineVersionCheck.java @@ -21,7 +21,9 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.io.IOUtils; +import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; @@ -43,6 +45,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class EngineVersionCheck implements CachedWebDataSource { /** @@ -62,6 +65,25 @@ public class EngineVersionCheck implements CachedWebDataSource { * against. */ private String updateToVersion; + /** + * The configured settings. + */ + private Settings settings; + + /** + * Constructs a new engine version check utility for testing. + * + * @param settings the configured settings + */ + protected EngineVersionCheck(Settings settings) { + this.settings = settings; + } + + /** + * Constructs a new engine version check utility. + */ + public EngineVersionCheck() { + } /** * Getter for updateToVersion - only used for testing. Represents the @@ -92,12 +114,14 @@ public class EngineVersionCheck implements CachedWebDataSource { * be updated */ @Override - public void update() throws UpdateException { - try (CveDB db = CveDB.getInstance()) { - final boolean autoupdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); - final boolean enabled = Settings.getBoolean(Settings.KEYS.UPDATE_VERSION_CHECK_ENABLED, true); - final String original = Settings.getString(Settings.KEYS.CVE_ORIGINAL_MODIFIED_20_URL); - final String current = Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL); + public void update(Engine engine) throws UpdateException { + this.settings = engine.getSettings(); + try { + final CveDB db = engine.getDatabase(); + final boolean autoupdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE, true); + final boolean enabled = settings.getBoolean(Settings.KEYS.UPDATE_VERSION_CHECK_ENABLED, true); + final String original = settings.getString(Settings.KEYS.CVE_ORIGINAL_MODIFIED_20_URL); + final String current = settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL); /* * Only update if auto-update is enabled, the engine check is * enabled, and the NVD CVE URLs have not been modified (i.e. the @@ -111,7 +135,7 @@ public class EngineVersionCheck implements CachedWebDataSource { final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0")); final long now = System.currentTimeMillis(); updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, ""); - final String currentVersion = Settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0"); + final String currentVersion = settings.getString(Settings.KEYS.APPLICATION_VERSION, "0.0.0"); LOGGER.debug("Last checked: {}", lastChecked); LOGGER.debug("Now: {}", now); LOGGER.debug("Current version: {}", currentVersion); @@ -184,9 +208,10 @@ public class EngineVersionCheck implements CachedWebDataSource { protected String getCurrentReleaseVersion() { HttpURLConnection conn = null; try { - final String str = Settings.getString(Settings.KEYS.ENGINE_VERSION_CHECK_URL, "http://jeremylong.github.io/DependencyCheck/current.txt"); + final String str = settings.getString(Settings.KEYS.ENGINE_VERSION_CHECK_URL, "http://jeremylong.github.io/DependencyCheck/current.txt"); final URL url = new URL(str); - conn = URLConnectionFactory.createHttpURLConnection(url); + final URLConnectionFactory factory = new URLConnectionFactory(settings); + conn = factory.createHttpURLConnection(url); conn.connect(); if (conn.getResponseCode() != 200) { return null; diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/NvdCveUpdater.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/NvdCveUpdater.java index c2b477b76..5872a0b54 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/NvdCveUpdater.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/NvdCveUpdater.java @@ -17,9 +17,6 @@ */ package org.owasp.dependencycheck.data.update; -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; import java.net.MalformedURLException; import java.util.Calendar; import java.util.HashMap; @@ -27,8 +24,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import java.net.URL; -import java.nio.channels.FileLock; -import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -36,7 +31,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.owasp.dependencycheck.data.nvdcve.ConnectionFactory; +import javax.annotation.concurrent.ThreadSafe; +import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; @@ -47,11 +43,9 @@ import org.owasp.dependencycheck.data.update.nvd.DownloadTask; import org.owasp.dependencycheck.data.update.nvd.NvdCveInfo; import org.owasp.dependencycheck.data.update.nvd.ProcessTask; import org.owasp.dependencycheck.data.update.nvd.UpdateableNvdCve; -import org.owasp.dependencycheck.exception.H2DBLockException; import org.owasp.dependencycheck.utils.DateUtil; import org.owasp.dependencycheck.utils.Downloader; import org.owasp.dependencycheck.utils.DownloadFailedException; -import org.owasp.dependencycheck.utils.H2DBLock; import org.owasp.dependencycheck.utils.InvalidSettingException; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; @@ -62,6 +56,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class NvdCveUpdater implements CachedWebDataSource { /** @@ -85,7 +80,10 @@ public class NvdCveUpdater implements CachedWebDataSource { * very CPU-intense, e.g. downloading files. */ private ExecutorService downloadExecutorService = null; - + /** + * The configured settings. + */ + private Settings settings; /** * Reference to the DAO. */ @@ -101,22 +99,21 @@ public class NvdCveUpdater implements CachedWebDataSource { * prevent more then one thread/JVM from updating the database at the same * time. This method may sleep upto 5 minutes. * + * @param engine a reference to the dependency-check engine * @throws UpdateException is thrown if there is an error updating the * database */ @Override - public synchronized void update() throws UpdateException { + public synchronized void update(Engine engine) throws UpdateException { + this.settings = engine.getSettings(); + this.cveDb = engine.getDatabase(); if (isUpdateConfiguredFalse()) { return; } - H2DBLock dbupdate = new H2DBLock(); try { - dbupdate.lock(); - initializeExecutorServices(); - cveDb = CveDB.getInstance(); dbProperties = cveDb.getDatabaseProperties(); - if (checkUpdate()) { + initializeExecutorServices(); final UpdateableNvdCve updateable = getUpdatesNeeded(); if (updateable.isUpdateNeeded()) { performUpdate(updateable); @@ -127,19 +124,13 @@ public class NvdCveUpdater implements CachedWebDataSource { throw new UpdateException("NVD CVE properties files contain an invalid URL, unable to update the data to use the most current data.", ex); } catch (DownloadFailedException ex) { LOGGER.warn("Unable to download the NVD CVE data; the results may not include the most recent CPE/CVEs from the NVD."); - if (Settings.getString(Settings.KEYS.PROXY_SERVER) == null) { + if (settings.getString(Settings.KEYS.PROXY_SERVER) == null) { LOGGER.info("If you are behind a proxy you may need to configure dependency-check to use the proxy."); } throw new UpdateException("Unable to download the NVD CVE data.", ex); } catch (DatabaseException ex) { throw new UpdateException("Database Exception, unable to update the data to use the most current data.", ex); - } catch (H2DBLockException ex) { - throw new UpdateException("Unable to obtain an exclusive lock on the H2 database to perform updates", ex); } finally { - if (cveDb != null) { - cveDb.close(); - } - dbupdate.release(); shutdownExecutorServices(); } } @@ -152,7 +143,7 @@ public class NvdCveUpdater implements CachedWebDataSource { */ private boolean isUpdateConfiguredFalse() { try { - if (!Settings.getBoolean(Settings.KEYS.UPDATE_NVDCVE_ENABLED, true)) { + if (!settings.getBoolean(Settings.KEYS.UPDATE_NVDCVE_ENABLED, true)) { return true; } } catch (InvalidSettingException ex) { @@ -160,7 +151,7 @@ public class NvdCveUpdater implements CachedWebDataSource { } boolean autoUpdate = true; try { - autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); + autoUpdate = settings.getBoolean(Settings.KEYS.AUTO_UPDATE); } catch (InvalidSettingException ex) { LOGGER.debug("Invalid setting for auto-update; using true."); } @@ -204,7 +195,7 @@ public class NvdCveUpdater implements CachedWebDataSource { private boolean checkUpdate() throws UpdateException { boolean proceed = true; // If the valid setting has not been specified, then we proceed to check... - final int validForHours = Settings.getInt(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, 0); + final int validForHours = settings.getInt(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, 0); if (dataExists() && 0 < validForHours) { // ms Valid = valid (hours) x 60 min/hour x 60 sec/min x 1000 ms/sec final long msValid = validForHours * 60L * 60L * 1000L; @@ -213,8 +204,7 @@ public class NvdCveUpdater implements CachedWebDataSource { proceed = (now - lastChecked) > msValid; if (!proceed) { LOGGER.info("Skipping NVD check since last check was within {} hours.", validForHours); - LOGGER.debug("Last NVD was at {}, and now {} is within {} ms.", - lastChecked, now, msValid); + LOGGER.debug("Last NVD was at {}, and now {} is within {} ms.", lastChecked, now, msValid); } } return proceed; @@ -226,11 +216,7 @@ public class NvdCveUpdater implements CachedWebDataSource { * @return true if the database contains data */ private boolean dataExists() { - try (CveDB cve = CveDB.getInstance()) { - return cve.dataExists(); - } catch (DatabaseException ex) { - return false; - } + return cveDb.dataExists(); } /** @@ -259,7 +245,7 @@ public class NvdCveUpdater implements CachedWebDataSource { final Set>> downloadFutures = new HashSet<>(maxUpdates); for (NvdCveInfo cve : updateable) { if (cve.getNeedsUpdate()) { - final DownloadTask call = new DownloadTask(cve, processingExecutorService, cveDb, Settings.getInstance()); + final DownloadTask call = new DownloadTask(cve, processingExecutorService, cveDb, settings); downloadFutures.add(downloadExecutorService.submit(call)); } } @@ -303,12 +289,12 @@ public class NvdCveUpdater implements CachedWebDataSource { } //always true because <=0 exits early above - //if (maxUpdates >= 1) { - //ensure the modified file date gets written (we may not have actually updated it) - dbProperties.save(updateable.get(MODIFIED)); - LOGGER.info("Begin database maintenance."); - cveDb.cleanupDatabase(); - LOGGER.info("End database maintenance."); + //if (maxUpdates >= 1) { + //ensure the modified file date gets written (we may not have actually updated it) + dbProperties.save(updateable.get(MODIFIED)); + LOGGER.info("Begin database maintenance."); + cveDb.cleanupDatabase(); + LOGGER.info("End database maintenance."); //} } @@ -345,7 +331,7 @@ public class NvdCveUpdater implements CachedWebDataSource { } if (dbProperties != null && !dbProperties.isEmpty()) { try { - final int startYear = Settings.getInt(Settings.KEYS.CVE_START_YEAR, 2002); + final int startYear = settings.getInt(Settings.KEYS.CVE_START_YEAR, 2002); final int endYear = Calendar.getInstance().get(Calendar.YEAR); boolean needsFullUpdate = false; for (int y = startYear; y <= endYear; y++) { @@ -357,7 +343,7 @@ public class NvdCveUpdater implements CachedWebDataSource { final long lastUpdated = Long.parseLong(dbProperties.getProperty(DatabaseProperties.LAST_UPDATED, "0")); final long now = System.currentTimeMillis(); - final int days = Settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7); + final int days = settings.getInt(Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS, 7); if (!needsFullUpdate && lastUpdated == updates.getTimeStamp(MODIFIED)) { updates.clear(); //we don't need to update anything. } else if (!needsFullUpdate && DateUtil.withinDateRange(lastUpdated, now, days)) { @@ -410,25 +396,24 @@ public class NvdCveUpdater implements CachedWebDataSource { private UpdateableNvdCve retrieveCurrentTimestampsFromWeb() throws MalformedURLException, DownloadFailedException, InvalidDataException, InvalidSettingException { - final int start = Settings.getInt(Settings.KEYS.CVE_START_YEAR); + final int start = settings.getInt(Settings.KEYS.CVE_START_YEAR); final int end = Calendar.getInstance().get(Calendar.YEAR); final Map lastModifiedDates = retrieveLastModifiedDates(start, end); final UpdateableNvdCve updates = new UpdateableNvdCve(); - final String baseUrl20 = Settings.getString(Settings.KEYS.CVE_SCHEMA_2_0); - final String baseUrl12 = Settings.getString(Settings.KEYS.CVE_SCHEMA_1_2); + final String baseUrl20 = settings.getString(Settings.KEYS.CVE_SCHEMA_2_0); + final String baseUrl12 = settings.getString(Settings.KEYS.CVE_SCHEMA_1_2); for (int i = start; i <= end; i++) { final String url = String.format(baseUrl20, i); updates.add(Integer.toString(i), url, String.format(baseUrl12, i), lastModifiedDates.get(url), true); } - final String url = Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL); - updates.add(MODIFIED, url, Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL), + final String url = settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL); + updates.add(MODIFIED, url, settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL), lastModifiedDates.get(url), false); - return updates; } @@ -448,16 +433,16 @@ public class NvdCveUpdater implements CachedWebDataSource { throws MalformedURLException, DownloadFailedException { final Set urls = new HashSet<>(); - final String baseUrl20 = Settings.getString(Settings.KEYS.CVE_SCHEMA_2_0); + final String baseUrl20 = settings.getString(Settings.KEYS.CVE_SCHEMA_2_0); for (int i = startYear; i <= endYear; i++) { final String url = String.format(baseUrl20, i); urls.add(url); } - urls.add(Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL)); + urls.add(settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL)); final Map> timestampFutures = new HashMap<>(); for (String url : urls) { - final TimestampRetriever timestampRetriever = new TimestampRetriever(url, Settings.getInstance()); + final TimestampRetriever timestampRetriever = new TimestampRetriever(url, settings); final Future future = downloadExecutorService.submit(timestampRetriever); timestampFutures.put(url, future); } @@ -480,6 +465,15 @@ public class NvdCveUpdater implements CachedWebDataSource { return lastModifiedDates; } + /** + * Sets the settings object; this is used during testing. + * + * @param settings the configured settings + */ + protected synchronized void setSettings(Settings settings) { + this.settings = settings; + } + /** * Retrieves the last modified timestamp from a NVD CVE meta data file. */ @@ -509,10 +503,10 @@ public class NvdCveUpdater implements CachedWebDataSource { public Long call() throws Exception { LOGGER.debug("Checking for updates from: {}", url); try { - Settings.setInstance(settings); - return Downloader.getLastModified(new URL(url)); + final Downloader downloader = new Downloader(settings); + return downloader.getLastModified(new URL(url)); } finally { - Settings.cleanup(false); + settings.cleanup(false); } } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/UpdateService.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/UpdateService.java index 8720b3539..c4cd75f68 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/UpdateService.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/UpdateService.java @@ -19,13 +19,15 @@ package org.owasp.dependencycheck.data.update; import java.util.Iterator; import java.util.ServiceLoader; +import javax.annotation.concurrent.NotThreadSafe; /** - * The CachedWebDataSource Service Loader. This class loads all services that implement - * org.owasp.dependencycheck.data.update.CachedWebDataSource. + * The CachedWebDataSource Service Loader. This class loads all services that + * implement {@link org.owasp.dependencycheck.data.update.CachedWebDataSource}. * * @author Jeremy Long */ +@NotThreadSafe public class UpdateService { /** @@ -36,14 +38,16 @@ public class UpdateService { /** * Creates a new instance of UpdateService. * - * @param classLoader the ClassLoader to use when dynamically loading Analyzer and Update services + * @param classLoader the ClassLoader to use when dynamically loading + * Analyzer and Update services */ public UpdateService(ClassLoader classLoader) { loader = ServiceLoader.load(CachedWebDataSource.class, classLoader); } /** - * Returns an Iterator for all instances of the CachedWebDataSource interface. + * Returns an Iterator for all instances of the CachedWebDataSource + * interface. * * @return an iterator of CachedWebDataSource. */ diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java index 123d326cc..4b1b5111d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/CPEHandler.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.data.update.cpe; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import org.owasp.dependencycheck.data.update.NvdCveUpdater; import org.owasp.dependencycheck.data.update.exception.InvalidDataException; import org.owasp.dependencycheck.utils.Settings; @@ -34,6 +35,7 @@ import org.xml.sax.helpers.DefaultHandler; * * @author Jeremy Long */ +@NotThreadSafe public class CPEHandler extends DefaultHandler { /** @@ -43,7 +45,7 @@ public class CPEHandler extends DefaultHandler { /** * The Starts with expression to filter CVE entries by CPE. */ - private static final String CPE_STARTS_WITH = Settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:/a:"); + private final String cpeStartsWith; /** * The text content of the node being processed. This can be used during the * end element event. @@ -62,6 +64,15 @@ public class CPEHandler extends DefaultHandler { */ private final List data = new ArrayList<>(); + /** + * Constructs a new CPE Handler object with the configured settings. + * + * @param settings the configured settings + */ + public CPEHandler(Settings settings) { + cpeStartsWith = settings.getString(Settings.KEYS.CVE_CPE_STARTS_WITH_FILTER, "cpe:/a:"); + } + /** * Returns the list of CPE values. * @@ -89,7 +100,7 @@ public class CPEHandler extends DefaultHandler { final String temp = attributes.getValue("deprecated"); final String value = attributes.getValue("name"); final boolean delete = "true".equalsIgnoreCase(temp); - if (!delete && value.startsWith(CPE_STARTS_WITH) && value.length() > 7) { + if (!delete && value.startsWith(cpeStartsWith) && value.length() > 7) { try { final Cpe cpe = new Cpe(value); data.add(cpe); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/Cpe.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/Cpe.java index 980552df1..8d037b9d6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/Cpe.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/cpe/Cpe.java @@ -20,14 +20,30 @@ package org.owasp.dependencycheck.data.update.cpe; import org.apache.commons.lang3.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.data.update.exception.InvalidDataException; /** * * @author Jeremy Long */ +@ThreadSafe public class Cpe { + /** + * The CPE identifier string (cpe:/a:vendor:product:version). + */ + private String value; + /** + * The vendor portion of the identifier. + */ + private String vendor; + + /** + * The product portion of the identifier. + */ + private String product; + /** * Constructs a new Cpe Object by parsing the vendor and product from the CPE identifier value. * @@ -47,11 +63,6 @@ public class Cpe { } } - /** - * The CPE identifier string (cpe:/a:vendor:product:version). - */ - private String value; - /** * Get the value of value. * @@ -69,10 +80,6 @@ public class Cpe { public void setValue(String value) { this.value = value; } - /** - * The vendor portion of the identifier. - */ - private String vendor; /** * Get the value of vendor. @@ -92,11 +99,6 @@ public class Cpe { this.vendor = vendor; } - /** - * The product portion of the identifier. - */ - private String product; - /** * Get the value of product. * diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/InvalidDataException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/InvalidDataException.java index e5398720f..90ac58186 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/InvalidDataException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/InvalidDataException.java @@ -17,11 +17,15 @@ */ package org.owasp.dependencycheck.data.update.exception; +import javax.annotation.concurrent.ThreadSafe; + /** - * An InvalidDataDataException is a generic exception used when trying to load the NVD CVE meta data. + * An InvalidDataDataException is a generic exception used when trying to load + * the NVD CVE meta data. * * @author Jeremy Long */ +@ThreadSafe public class InvalidDataException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/UpdateException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/UpdateException.java index a09c17040..2c4e53f18 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/UpdateException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/exception/UpdateException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.data.update.exception; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception used when an error occurs reading a setting. * * @author Jeremy Long */ +@ThreadSafe public class UpdateException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/DownloadTask.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/DownloadTask.java index e34d5ab12..743c8185b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/DownloadTask.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/DownloadTask.java @@ -25,6 +25,7 @@ import java.net.URL; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.utils.DownloadFailedException; @@ -39,6 +40,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class DownloadTask implements Callable> { /** @@ -91,8 +93,8 @@ public class DownloadTask implements Callable> { final File file2; try { - file1 = File.createTempFile("cve" + nvdCveInfo.getId() + '_', ".xml", Settings.getTempDirectory()); - file2 = File.createTempFile("cve_1_2_" + nvdCveInfo.getId() + '_', ".xml", Settings.getTempDirectory()); + file1 = File.createTempFile("cve" + nvdCveInfo.getId() + '_', ".xml", settings.getTempDirectory()); + file2 = File.createTempFile("cve_1_2_" + nvdCveInfo.getId() + '_', ".xml", settings.getTempDirectory()); } catch (IOException ex) { throw new UpdateException("Unable to create temporary files", ex); } @@ -128,15 +130,6 @@ public class DownloadTask implements Callable> { return first; } - /** - * Set the value of first. - * - * @param first new value of first - */ - public void setFirst(File first) { - this.first = first; - } - /** * Get the value of second. * @@ -146,29 +139,20 @@ public class DownloadTask implements Callable> { return second; } - /** - * Set the value of second. - * - * @param second new value of second - */ - public void setSecond(File second) { - this.second = second; - } - @Override public Future call() throws Exception { try { - Settings.setInstance(settings); final URL url1 = new URL(nvdCveInfo.getUrl()); final URL url2 = new URL(nvdCveInfo.getOldSchemaVersionUrl()); LOGGER.info("Download Started for NVD CVE - {}", nvdCveInfo.getId()); final long startDownload = System.currentTimeMillis(); try { - Downloader.fetchFile(url1, first); - Downloader.fetchFile(url2, second); + final Downloader downloader = new Downloader(settings); + downloader.fetchFile(url1, first); + downloader.fetchFile(url2, second); } catch (DownloadFailedException ex) { LOGGER.warn("Download Failed for NVD CVE - {}\nSome CVEs may not be reported.", nvdCveInfo.getId()); - if (Settings.getString(Settings.KEYS.PROXY_SERVER) == null) { + if (settings.getString(Settings.KEYS.PROXY_SERVER) == null) { LOGGER.info("If you are behind a proxy you may need to configure dependency-check to use the proxy."); } LOGGER.debug("", ex); @@ -193,7 +177,7 @@ public class DownloadTask implements Callable> { LOGGER.warn("An exception occurred downloading NVD CVE - {}\nSome CVEs may not be reported.", nvdCveInfo.getId()); LOGGER.debug("Download Task Failed", ex); } finally { - Settings.cleanup(false); + settings.cleanup(false); } return null; } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve12Handler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve12Handler.java index 7adfbb18c..b30c9bbe2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve12Handler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve12Handler.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.concurrent.NotThreadSafe; import org.owasp.dependencycheck.dependency.VulnerableSoftware; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -34,6 +35,7 @@ import org.xml.sax.helpers.DefaultHandler; * * @author Jeremy Long */ +@NotThreadSafe public class NvdCve12Handler extends DefaultHandler { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve20Handler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve20Handler.java index 2a5b8bf80..eaba27b78 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve20Handler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCve20Handler.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.data.update.nvd; import java.io.IOException; import java.util.List; import java.util.Map; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.lucene.index.CorruptIndexException; import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; @@ -32,14 +33,16 @@ import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.helpers.DefaultHandler; - +//CSOFF: AvoidStarImport import static org.owasp.dependencycheck.data.update.nvd.NvdCve20Handler.AttributeValues.*; +//CSON: AvoidStarImport /** * A SAX Handler that will parse the NVD CVE XML (schema version 2.0). * * @author Jeremy Long */ +@NotThreadSafe public class NvdCve20Handler extends DefaultHandler { /** @@ -184,9 +187,7 @@ public class NvdCve20Handler extends DefaultHandler { totalNumberOfApplicationEntries += 1; try { saveEntry(vulnerability); - } catch (DatabaseException | CorruptIndexException ex) { - throw new SAXException(ex); - } catch (IOException ex) { + } catch (DatabaseException | IOException ex) { throw new SAXException(ex); } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveInfo.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveInfo.java index a143000a8..bdeb8f1b8 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveInfo.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/NvdCveInfo.java @@ -17,17 +17,36 @@ */ package org.owasp.dependencycheck.data.update.nvd; +import javax.annotation.concurrent.ThreadSafe; + /** * A pojo that contains the Url and timestamp of the current NvdCve XML files. * * @author Jeremy Long */ +@ThreadSafe public class NvdCveInfo { /** * an id. */ private String id; + /** + * a url. + */ + private String url; + /** + * The 1.2 schema URL. + */ + private String oldSchemaVersionUrl; + /** + * a timestamp - epoch time. + */ + private long timestamp; + /** + * indicates whether or not this item should be updated. + */ + private boolean needsUpdate = true; /** * Get the value of id. @@ -46,10 +65,6 @@ public class NvdCveInfo { public void setId(String id) { this.id = id; } - /** - * a url. - */ - private String url; /** * Get the value of url. @@ -68,10 +83,6 @@ public class NvdCveInfo { public void setUrl(String url) { this.url = url; } - /** - * The 1.2 schema URL. - */ - private String oldSchemaVersionUrl; /** * Get the value of oldSchemaVersionUrl. @@ -90,10 +101,6 @@ public class NvdCveInfo { public void setOldSchemaVersionUrl(String oldSchemaVersionUrl) { this.oldSchemaVersionUrl = oldSchemaVersionUrl; } - /** - * a timestamp - epoch time. - */ - private long timestamp; /** * Get the value of timestamp - epoch time. @@ -112,10 +119,6 @@ public class NvdCveInfo { public void setTimestamp(long timestamp) { this.timestamp = timestamp; } - /** - * indicates whether or not this item should be updated. - */ - private boolean needsUpdate = true; /** * Get the value of needsUpdate. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java index d9c4f26f1..3f4e0cbbb 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/ProcessTask.java @@ -23,6 +23,7 @@ import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import javax.annotation.concurrent.ThreadSafe; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import org.owasp.dependencycheck.data.nvdcve.CveDB; @@ -42,6 +43,7 @@ import org.xml.sax.SAXException; * * @author Jeremy Long */ +@ThreadSafe public class ProcessTask implements Callable { /** @@ -114,12 +116,11 @@ public class ProcessTask implements Callable { @Override public ProcessTask call() throws Exception { try { - Settings.setInstance(settings); processFiles(); } catch (UpdateException ex) { this.exception = ex; } finally { - Settings.cleanup(false); + settings.cleanup(false); } return this; } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java index e086f2fae..34074a54f 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/data/update/nvd/UpdateableNvdCve.java @@ -21,13 +21,15 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; +import javax.annotation.concurrent.NotThreadSafe; /** - * Contains a collection of updateable NvdCveInfo objects. This is used to determine which files need to be downloaded and - * processed. + * Contains a collection of updateable NvdCveInfo objects. This is used to + * determine which files need to be downloaded and processed. * * @author Jeremy Long */ +@NotThreadSafe public class UpdateableNvdCve implements Iterable, Iterator { /** @@ -36,7 +38,13 @@ public class UpdateableNvdCve implements Iterable, Iterator collection = new TreeMap<>(); /** - * Returns the collection of NvdCveInfo objects. This method is mainly used for testing. + * An internal iterator used to implement iterable. + */ + private Iterator> iterableContent = null; + + /** + * Returns the collection of NvdCveInfo objects. This method is mainly used + * for testing. * * @return the collection of NvdCveInfo objects */ @@ -63,7 +71,8 @@ public class UpdateableNvdCve implements Iterable, Iterator, Iterator> iterableContent = null; /** *

@@ -118,7 +123,8 @@ public class UpdateableNvdCve implements Iterable, Iterator * This method is not thread safe.

* - * @return true or false depending on whether or not another item exists in the collection + * @return true or false depending on whether or not another item exists in + * the collection */ @Override public boolean hasNext() { diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java index 6beb4a9c1..83e114ed0 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Dependency.java @@ -22,11 +22,13 @@ import java.io.IOException; import java.io.Serializable; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -43,7 +45,8 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ -public class Dependency implements Serializable, Comparable { +@ThreadSafe +public class Dependency extends EvidenceCollection implements Serializable, Comparable { /** * The serial version UID for serialization. @@ -53,14 +56,6 @@ public class Dependency implements Serializable, Comparable { * The logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(Dependency.class); - /** - * Used as starting point for generating the value in {@link #hashCode()}. - */ - private static final int MAGIC_HASH_INIT_VALUE = 3; - /** - * Used as a multiplier for generating the value in {@link #hashCode()}. - */ - private static final int MAGIC_HASH_MULTIPLIER = 47; /** * The actual file path of the dependency on disk. */ @@ -88,19 +83,7 @@ public class Dependency implements Serializable, Comparable { /** * A list of Identifiers. */ - private Set identifiers; - /** - * A collection of vendor evidence. - */ - private final EvidenceCollection vendorEvidence; - /** - * A collection of product evidence. - */ - private final EvidenceCollection productEvidence; - /** - * A collection of version evidence. - */ - private final EvidenceCollection versionEvidence; + private final Set identifiers = new TreeSet<>(); /** * The file name to display in reports. */ @@ -108,11 +91,11 @@ public class Dependency implements Serializable, Comparable { /** * A set of identifiers that have been suppressed. */ - private Set suppressedIdentifiers; + private final Set suppressedIdentifiers = new TreeSet<>(); /** * A set of vulnerabilities that have been suppressed. */ - private SortedSet suppressedVulnerabilities; + private final SortedSet suppressedVulnerabilities = new TreeSet<>(new VulnerabilityComparator()); /** * The description of the JAR file. */ @@ -124,19 +107,19 @@ public class Dependency implements Serializable, Comparable { /** * A list of vulnerabilities for this dependency. */ - private SortedSet vulnerabilities; + private final SortedSet vulnerabilities = new TreeSet<>(new VulnerabilityComparator()); /** * A collection of related dependencies. */ - private Set relatedDependencies = new TreeSet<>(); + private final Set relatedDependencies = new TreeSet<>(); /** * A list of projects that reference this dependency. */ - private Set projectReferences = new HashSet<>(); + private final Set projectReferences = new HashSet<>(); /** * A list of available versions. */ - private List availableVersions = new ArrayList<>(); + private final List availableVersions = new ArrayList<>(); /** * Defines an actual or virtual dependency. @@ -165,13 +148,7 @@ public class Dependency implements Serializable, Comparable { * Constructs a new Dependency object. */ public Dependency() { - vendorEvidence = new EvidenceCollection(); - productEvidence = new EvidenceCollection(); - versionEvidence = new EvidenceCollection(); - identifiers = new TreeSet<>(); - vulnerabilities = new TreeSet<>(new VulnerabilityComparator()); - suppressedIdentifiers = new TreeSet<>(); - suppressedVulnerabilities = new TreeSet<>(new VulnerabilityComparator()); + //empty contructor } /** @@ -187,7 +164,8 @@ public class Dependency implements Serializable, Comparable { * Constructs a new Dependency object. * * @param file the File to create the dependency object from. - * @param isVirtual specifies if the dependency is virtual indicating the file doesn't actually exist. + * @param isVirtual specifies if the dependency is virtual indicating the + * file doesn't actually exist. */ public Dependency(File file, boolean isVirtual) { this(); @@ -208,18 +186,6 @@ public class Dependency implements Serializable, Comparable { return this.fileName; } - /** - * Returns the file name of the dependency with the backslash escaped for - * use in JavaScript. This is a complete hack as I could not get the replace - * to work in the template itself. - * - * @return the file name of the dependency with the backslash escaped for - * use in JavaScript - */ - public String getFileNameForJavaScript() { - return this.fileName.replace("\\", "\\\\"); - } - /** * Sets the file name of the dependency. * @@ -345,21 +311,22 @@ public class Dependency implements Serializable, Comparable { } /** - * Returns a List of Identifiers. + * Returns an unmodifiable List of Identifiers. * - * @return an ArrayList of Identifiers + * @return an unmodifiable List of Identifiers */ - public Set getIdentifiers() { - return this.identifiers; + public synchronized Set getIdentifiers() { + return Collections.unmodifiableSet(new HashSet<>(identifiers)); } /** - * Sets a List of Identifiers. + * Adds a set of Identifiers to the current list of identifiers. Only used + * for testing. * - * @param identifiers A list of Identifiers + * @param identifiers A set of Identifiers */ - public void setIdentifiers(Set identifiers) { - this.identifiers = identifiers; + protected synchronized void addIdentifiers(Set identifiers) { + this.identifiers.addAll(identifiers); } /** @@ -370,7 +337,7 @@ public class Dependency implements Serializable, Comparable { * @param value the value of the identifier * @param url the URL of the identifier */ - public void addIdentifier(String type, String value, String url) { + public synchronized void addIdentifier(String type, String value, String url) { final Identifier i = new Identifier(type, value, url); this.identifiers.add(i); } @@ -384,12 +351,21 @@ public class Dependency implements Serializable, Comparable { * @param url the URL of the identifier * @param confidence the confidence in the Identifier being accurate */ - public void addIdentifier(String type, String value, String url, Confidence confidence) { + public synchronized void addIdentifier(String type, String value, String url, Confidence confidence) { final Identifier i = new Identifier(type, value, url); i.setConfidence(confidence); this.identifiers.add(i); } + /** + * Removes an identifier from the list of identifiers. + * + * @param i the identifier to remove + */ + public synchronized void removeIdentifier(Identifier i) { + this.identifiers.remove(i); + } + /** * Adds the maven artifact as evidence. * @@ -399,25 +375,27 @@ public class Dependency implements Serializable, Comparable { */ public void addAsEvidence(String source, MavenArtifact mavenArtifact, Confidence confidence) { if (mavenArtifact.getGroupId() != null && !mavenArtifact.getGroupId().isEmpty()) { - this.getVendorEvidence().addEvidence(source, "groupid", mavenArtifact.getGroupId(), confidence); + this.addEvidence(EvidenceType.VENDOR, source, "groupid", mavenArtifact.getGroupId(), confidence); } if (mavenArtifact.getArtifactId() != null && !mavenArtifact.getArtifactId().isEmpty()) { - this.getProductEvidence().addEvidence(source, "artifactid", mavenArtifact.getArtifactId(), confidence); + this.addEvidence(EvidenceType.PRODUCT, source, "artifactid", mavenArtifact.getArtifactId(), confidence); } if (mavenArtifact.getVersion() != null && !mavenArtifact.getVersion().isEmpty()) { - this.getVersionEvidence().addEvidence(source, "version", mavenArtifact.getVersion(), confidence); + this.addEvidence(EvidenceType.VERSION, source, "version", mavenArtifact.getVersion(), confidence); } if (mavenArtifact.getArtifactUrl() != null && !mavenArtifact.getArtifactUrl().isEmpty()) { boolean found = false; - for (Identifier i : this.getIdentifiers()) { - if ("maven".equals(i.getType()) && i.getValue().equals(mavenArtifact.toString())) { - found = true; - i.setConfidence(Confidence.HIGHEST); - final String url = "http://search.maven.org/#search|ga|1|1%3A%22" + this.getSha1sum() + "%22"; - i.setUrl(url); - //i.setUrl(mavenArtifact.getArtifactUrl()); - LOGGER.debug("Already found identifier {}. Confidence set to highest", i.getValue()); - break; + synchronized (this) { + for (Identifier i : this.identifiers) { + if ("maven".equals(i.getType()) && i.getValue().equals(mavenArtifact.toString())) { + found = true; + i.setConfidence(Confidence.HIGHEST); + final String url = "http://search.maven.org/#search|ga|1|1%3A%22" + this.getSha1sum() + "%22"; + i.setUrl(url); + //i.setUrl(mavenArtifact.getArtifactUrl()); + LOGGER.debug("Already found identifier {}. Confidence set to highest", i.getValue()); + break; + } } } if (!found) { @@ -433,26 +411,17 @@ public class Dependency implements Serializable, Comparable { * * @param identifier the identifier to add */ - public void addIdentifier(Identifier identifier) { + public synchronized void addIdentifier(Identifier identifier) { this.identifiers.add(identifier); } /** - * Get the value of suppressedIdentifiers. + * Get the unmodifiable set of suppressedIdentifiers. * * @return the value of suppressedIdentifiers */ - public Set getSuppressedIdentifiers() { - return suppressedIdentifiers; - } - - /** - * Set the value of suppressedIdentifiers. - * - * @param suppressedIdentifiers new value of suppressedIdentifiers - */ - public void setSuppressedIdentifiers(Set suppressedIdentifiers) { - this.suppressedIdentifiers = suppressedIdentifiers; + public synchronized Set getSuppressedIdentifiers() { + return Collections.unmodifiableSet(new HashSet<>(suppressedIdentifiers)); } /** @@ -460,26 +429,17 @@ public class Dependency implements Serializable, Comparable { * * @param identifier an identifier that was suppressed. */ - public void addSuppressedIdentifier(Identifier identifier) { + public synchronized void addSuppressedIdentifier(Identifier identifier) { this.suppressedIdentifiers.add(identifier); } /** - * Get the value of suppressedVulnerabilities. + * Get an unmodifiable sorted set of suppressedVulnerabilities. * - * @return the value of suppressedVulnerabilities + * @return the unmodifiable sorted set of suppressedVulnerabilities */ - public SortedSet getSuppressedVulnerabilities() { - return suppressedVulnerabilities; - } - - /** - * Set the value of suppressedVulnerabilities. - * - * @param suppressedVulnerabilities new value of suppressedVulnerabilities - */ - public void setSuppressedVulnerabilities(SortedSet suppressedVulnerabilities) { - this.suppressedVulnerabilities = suppressedVulnerabilities; + public synchronized SortedSet getSuppressedVulnerabilities() { + return Collections.unmodifiableSortedSet(new TreeSet<>(suppressedVulnerabilities)); } /** @@ -487,64 +447,10 @@ public class Dependency implements Serializable, Comparable { * * @param vulnerability the vulnerability that was suppressed */ - public void addSuppressedVulnerability(Vulnerability vulnerability) { + public synchronized void addSuppressedVulnerability(Vulnerability vulnerability) { this.suppressedVulnerabilities.add(vulnerability); } - /** - * Returns the evidence used to identify this dependency. - * - * @return an EvidenceCollection. - */ - public EvidenceCollection getEvidence() { - return EvidenceCollection.merge(this.productEvidence, this.vendorEvidence, this.versionEvidence); - } - - /** - * Returns the evidence used to identify this dependency. - * - * @return an EvidenceCollection. - */ - public Set getEvidenceForDisplay() { - return EvidenceCollection.mergeForDisplay(this.productEvidence, this.vendorEvidence, this.versionEvidence); - } - - /** - * Returns the evidence used to identify this dependency. - * - * @return an EvidenceCollection. - */ - public EvidenceCollection getEvidenceUsed() { - return EvidenceCollection.mergeUsed(this.productEvidence, this.vendorEvidence, this.versionEvidence); - } - - /** - * Gets the Vendor Evidence. - * - * @return an EvidenceCollection. - */ - public EvidenceCollection getVendorEvidence() { - return this.vendorEvidence; - } - - /** - * Gets the Product Evidence. - * - * @return an EvidenceCollection. - */ - public EvidenceCollection getProductEvidence() { - return this.productEvidence; - } - - /** - * Gets the Version Evidence. - * - * @return an EvidenceCollection. - */ - public EvidenceCollection getVersionEvidence() { - return this.versionEvidence; - } - /** * Get the value of description. * @@ -582,21 +488,12 @@ public class Dependency implements Serializable, Comparable { } /** - * Get the list of vulnerabilities. + * Get the unmodifiable sorted set of vulnerabilities. * - * @return the list of vulnerabilities + * @return the unmodifiable sorted set of vulnerabilities */ - public SortedSet getVulnerabilities() { - return vulnerabilities; - } - - /** - * Set the value of vulnerabilities. - * - * @param vulnerabilities new value of vulnerabilities - */ - public void setVulnerabilities(SortedSet vulnerabilities) { - this.vulnerabilities = vulnerabilities; + public synchronized SortedSet getVulnerabilities() { + return Collections.unmodifiableSortedSet(new TreeSet<>(vulnerabilities)); } /** @@ -627,39 +524,48 @@ public class Dependency implements Serializable, Comparable { /** * Adds a vulnerability to the dependency. * - * @param vulnerability a vulnerability outlining a vulnerability. + * @param vulnerability a vulnerability */ - public void addVulnerability(Vulnerability vulnerability) { + public synchronized void addVulnerability(Vulnerability vulnerability) { this.vulnerabilities.add(vulnerability); } /** - * Get the value of {@link #relatedDependencies}. This field is used to - * collect other dependencies which really represent the same dependency, - * and may be presented as one item in reports. + * Adds a list of vulnerabilities to the dependency. * - * @return the value of relatedDependencies + * @param vulnerabilities a list of vulnerabilities */ - public Set getRelatedDependencies() { - return relatedDependencies; + public synchronized void addVulnerabilities(List vulnerabilities) { + this.vulnerabilities.addAll(vulnerabilities); } /** - * Get the value of projectReferences. + * Removes the given vulnerability from the list. * - * @return the value of projectReferences + * @param v the vulnerability to remove */ - public Set getProjectReferences() { - return projectReferences; + public synchronized void removeVulnerability(Vulnerability v) { + this.vulnerabilities.remove(v); } /** - * Set the value of projectReferences. + * Get the unmodifiable set of {@link #relatedDependencies}. This field is + * used to collect other dependencies which really represent the same + * dependency, and may be presented as one item in reports. * - * @param projectReferences new value of projectReferences + * @return the unmodifiable set of relatedDependencies */ - public void setProjectReferences(Set projectReferences) { - this.projectReferences = projectReferences; + public synchronized Set getRelatedDependencies() { + return Collections.unmodifiableSet(new HashSet<>(relatedDependencies)); + } + + /** + * Get the unmodifiable set of projectReferences. + * + * @return the unmodifiable set of projectReferences + */ + public synchronized Set getProjectReferences() { + return Collections.unmodifiableSet(new HashSet<>(projectReferences)); } /** @@ -667,7 +573,7 @@ public class Dependency implements Serializable, Comparable { * * @param projectReference a project reference */ - public void addProjectReference(String projectReference) { + public synchronized void addProjectReference(String projectReference) { this.projectReferences.add(projectReference); } @@ -676,19 +582,10 @@ public class Dependency implements Serializable, Comparable { * * @param projectReferences a set of project references */ - public void addAllProjectReferences(Set projectReferences) { + public synchronized void addAllProjectReferences(Set projectReferences) { this.projectReferences.addAll(projectReferences); } - /** - * Set the value of relatedDependencies. - * - * @param relatedDependencies new value of relatedDependencies - */ - public void setRelatedDependencies(Set relatedDependencies) { - this.relatedDependencies = relatedDependencies; - } - /** * Adds a related dependency. The internal collection is normally a * {@link java.util.TreeSet}, which relies on @@ -698,7 +595,7 @@ public class Dependency implements Serializable, Comparable { * * @param dependency a reference to the related dependency */ - public void addRelatedDependency(Dependency dependency) { + public synchronized void addRelatedDependency(Dependency dependency) { if (this == dependency) { LOGGER.warn("Attempted to add a circular reference - please post the log file to issue #172 here " + "https://github.com/jeremylong/DependencyCheck/issues/172"); @@ -711,22 +608,22 @@ public class Dependency implements Serializable, Comparable { } } + /** + * Removes a related dependency. + * + * @param dependency the dependency to remove + */ + public synchronized void removeRelatedDependencies(Dependency dependency) { + this.relatedDependencies.remove(dependency); + } + /** * Get the value of availableVersions. * * @return the value of availableVersions */ - public List getAvailableVersions() { - return availableVersions; - } - - /** - * Set the value of availableVersions. - * - * @param availableVersions new value of availableVersions - */ - public void setAvailableVersions(List availableVersions) { - this.availableVersions = availableVersions; + public synchronized List getAvailableVersions() { + return Collections.unmodifiableList(new ArrayList<>(availableVersions)); } /** @@ -734,7 +631,7 @@ public class Dependency implements Serializable, Comparable { * * @param version the version to add to the list */ - public void addAvailableVersion(String version) { + public synchronized void addAvailableVersion(String version) { this.availableVersions.add(version); } @@ -781,13 +678,9 @@ public class Dependency implements Serializable, Comparable { .append(this.md5sum, other.md5sum) .append(this.sha1sum, other.sha1sum) .append(this.identifiers, other.identifiers) - .append(this.vendorEvidence, other.vendorEvidence) - .append(this.productEvidence, other.productEvidence) - .append(this.versionEvidence, other.versionEvidence) .append(this.description, other.description) .append(this.license, other.license) .append(this.vulnerabilities, other.vulnerabilities) - //.append(this.relatedDependencies, other.relatedDependencies) .append(this.projectReferences, other.projectReferences) .append(this.availableVersions, other.availableVersions) .isEquals(); @@ -800,20 +693,17 @@ public class Dependency implements Serializable, Comparable { */ @Override public int hashCode() { - return new HashCodeBuilder(MAGIC_HASH_INIT_VALUE, MAGIC_HASH_MULTIPLIER) + return new HashCodeBuilder(3, 47) + .appendSuper(super.hashCode()) .append(actualFilePath) .append(filePath) .append(fileName) .append(md5sum) .append(sha1sum) .append(identifiers) - .append(vendorEvidence) - .append(productEvidence) - .append(versionEvidence) .append(description) .append(license) .append(vulnerabilities) - //.append(relatedDependencies) .append(projectReferences) .append(availableVersions) .toHashCode(); @@ -830,4 +720,13 @@ public class Dependency implements Serializable, Comparable { return "Dependency{ fileName='" + fileName + "', actualFilePath='" + actualFilePath + "', filePath='" + filePath + "', packagePath='" + packagePath + "'}"; } + + /** + * Add a list of suppressed vulnerabilities to the collection. + * + * @param vulns the list of suppressed vulnerabilities to add + */ + public synchronized void addSuppressedVulnerabilities(List vulns) { + this.suppressedVulnerabilities.addAll(vulns); + } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Evidence.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Evidence.java index 0c33e7c1d..aacda1e6a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Evidence.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Evidence.java @@ -22,12 +22,14 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.HashCodeBuilder; import java.io.Serializable; +import javax.annotation.concurrent.ThreadSafe; /** * Evidence is a piece of information about a Dependency. * * @author Jeremy Long */ +@ThreadSafe public class Evidence implements Serializable, Comparable { /** @@ -44,6 +46,26 @@ public class Evidence implements Serializable, Comparable { */ private static final int MAGIC_HASH_MULTIPLIER = 67; + /** + * The name of the evidence. + */ + private String name; + + /** + * The source of the evidence. + */ + private String source; + + /** + * The value of the evidence. + */ + private String value; + + /** + * The confidence level for the evidence. + */ + private Confidence confidence; + /** * Creates a new Evidence object. */ @@ -65,11 +87,6 @@ public class Evidence implements Serializable, Comparable { this.confidence = confidence; } - /** - * The name of the evidence. - */ - private String name; - /** * Get the value of name. * @@ -88,11 +105,6 @@ public class Evidence implements Serializable, Comparable { this.name = name; } - /** - * The source of the evidence. - */ - private String source; - /** * Get the value of source. * @@ -111,31 +123,12 @@ public class Evidence implements Serializable, Comparable { this.source = source; } - /** - * The value of the evidence. - */ - private String value; - /** * Get the value of value. * * @return the value of value */ public String getValue() { - used = true; - return value; - } - - /** - * Get the value of value. If setUsed is set to false this call to get will - * not mark the evidence as used. - * - * @param setUsed whether or not this call to getValue should cause the used - * flag to be updated - * @return the value of value - */ - public String getValue(Boolean setUsed) { - used = used || setUsed; return value; } @@ -148,34 +141,6 @@ public class Evidence implements Serializable, Comparable { this.value = value; } - /** - * A value indicating if the Evidence has been "used" (aka read). - */ - private boolean used; - - /** - * Get the value of used. - * - * @return the value of used - */ - public boolean isUsed() { - return used; - } - - /** - * Set the value of used. - * - * @param used new value of used - */ - public void setUsed(boolean used) { - this.used = used; - } - - /** - * The confidence level for the evidence. - */ - private Confidence confidence; - /** * Get the value of confidence. * diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java index 80283bd75..eacb5d40c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceCollection.java @@ -18,17 +18,13 @@ package org.owasp.dependencycheck.dependency; import java.io.Serializable; -import java.net.MalformedURLException; +import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; -import java.util.List; import java.util.Set; -import java.util.TreeSet; -import org.apache.commons.lang3.StringUtils; -import org.owasp.dependencycheck.utils.DependencyVersion; -import org.owasp.dependencycheck.utils.DependencyVersionUtil; +import javax.annotation.concurrent.ThreadSafe; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.owasp.dependencycheck.utils.Filter; -import org.owasp.dependencycheck.utils.UrlStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,7 +33,8 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ -public class EvidenceCollection implements Serializable, Iterable { +@ThreadSafe +class EvidenceCollection implements Serializable { /** * The serial version UID for serialization. @@ -48,13 +45,25 @@ public class EvidenceCollection implements Serializable, Iterable { */ private static final Logger LOGGER = LoggerFactory.getLogger(EvidenceCollection.class); /** - * A collection of evidence. + * A collection of vendor evidence. */ - private final Set list; + private final Set vendors = new HashSet<>(); /** - * A collection of strings used to adjust Lucene's term weighting. + * A collection of strings used to adjust Lucene's vendor term weighting. */ - private final Set weightedStrings; + private final Set vendorWeightings = new HashSet<>(); + /** + * A collection of product evidence. + */ + private final Set products = new HashSet<>(); + /** + * A collection of strings used to adjust Lucene's product term weighting. + */ + private final Set productWeightings = new HashSet<>(); + /** + * A collection of version evidence. + */ + private final Set versions = new HashSet<>(); /** * Used to iterate over highest confidence evidence contained in the @@ -95,74 +104,113 @@ public class EvidenceCollection implements Serializable, Iterable { return evidence.getConfidence() == Confidence.LOW; } }; - /** - * Used to iterate over evidence that has was used (aka read) from the - * collection. - */ - private static final Filter EVIDENCE_USED = new Filter() { - @Override - public boolean passes(Evidence evidence) { - return evidence.isUsed(); - } - }; /** - * Used to iterate over evidence of the specified confidence. + * Used to iterate over evidence of the specified type and confidence. * + * @param type the evidence type to iterate over * @param confidence the confidence level for the evidence to be iterated * over. * @return Iterable<Evidence> an iterable collection of evidence */ - public final Iterable iterator(Confidence confidence) { - if (null != confidence) { + public synchronized Iterable getIterator(EvidenceType type, Confidence confidence) { + if (null != confidence && null != type) { + Set list; + + switch (type) { + case VENDOR: + list = Collections.unmodifiableSet(new HashSet<>(vendors)); + break; + case PRODUCT: + list = Collections.unmodifiableSet(new HashSet<>(products)); + break; + case VERSION: + list = Collections.unmodifiableSet(new HashSet<>(versions)); + break; + default: + return null; + } + switch (confidence) { case HIGHEST: - return EvidenceCollection.HIGHEST_CONFIDENCE.filter(this.list); + return EvidenceCollection.HIGHEST_CONFIDENCE.filter(list); case HIGH: - return EvidenceCollection.HIGH_CONFIDENCE.filter(this.list); + return EvidenceCollection.HIGH_CONFIDENCE.filter(list); case MEDIUM: - return EvidenceCollection.MEDIUM_CONFIDENCE.filter(this.list); + return EvidenceCollection.MEDIUM_CONFIDENCE.filter(list); default: - return EvidenceCollection.LOW_CONFIDENCE.filter(this.list); + return EvidenceCollection.LOW_CONFIDENCE.filter(list); } } return null; } /** - * Creates a new EvidenceCollection. + * Adds evidence to the collection. + * + * @param type the type of evidence (vendor, product, version) + * @param e Evidence */ - public EvidenceCollection() { - list = new TreeSet<>(); - weightedStrings = new HashSet<>(); + public synchronized void addEvidence(EvidenceType type, Evidence e) { + if (null != type) { + switch (type) { + case VENDOR: + vendors.add(e); + break; + case PRODUCT: + products.add(e); + break; + case VERSION: + versions.add(e); + break; + default: + break; + } + } } /** - * Adds evidence to the collection. + * Removes evidence from the collection. * + * @param type the type of evidence (vendor, product, version) * @param e Evidence. */ - public void addEvidence(Evidence e) { - list.add(e); + public synchronized void removeEvidence(EvidenceType type, Evidence e) { + if (null != type) { + switch (type) { + case VENDOR: + vendors.remove(e); + break; + case PRODUCT: + products.remove(e); + break; + case VERSION: + versions.remove(e); + break; + default: + break; + } + } } /** * Creates an Evidence object from the parameters and adds the resulting - * object to the collection. + * object to the evidence collection. * + * @param type the type of evidence (vendor, product, version) * @param source the source of the Evidence. * @param name the name of the Evidence. * @param value the value of the Evidence. * @param confidence the confidence of the Evidence. */ - public void addEvidence(String source, String name, String value, Confidence confidence) { + public void addEvidence(EvidenceType type, String source, String name, String value, Confidence confidence) { final Evidence e = new Evidence(source, name, value, confidence); - addEvidence(e); + addEvidence(type, e); } /** - * Adds term to the weighting collection. The terms added here are used - * later to boost the score of other terms. This is a way of combining + * Adds term to the vendor weighting collection. The terms added here are + * used later to boost the score of other terms. This is a way of combining * evidence from multiple sources to boost the confidence of the given * evidence. * @@ -175,136 +223,122 @@ public class EvidenceCollection implements Serializable, Iterable { * * @param str to add to the weighting collection. */ - public void addWeighting(String str) { - weightedStrings.add(str); + public synchronized void addVendorWeighting(String str) { + vendorWeightings.add(str); } /** - * Returns a set of Weightings - a list of terms that are believed to be of - * higher confidence when also found in another location. + * Adds term to the product weighting collection. The terms added here are + * used later to boost the score of other terms. This is a way of combining + * evidence from multiple sources to boost the confidence of the given + * evidence. * - * @return Set<String> + * Example: The term 'Apache' is found in the manifest of a JAR and is added + * to the Collection. When we parse the package names within the JAR file we + * may add these package names to the "weighted" strings collection to boost + * the score in the Lucene query. That way when we construct the Lucene + * query we find the term Apache in the collection AND in the weighted + * strings; as such, we will boost the confidence of the term Apache. + * + * @param str to add to the weighting collection. */ - public Set getWeighting() { - return weightedStrings; + public synchronized void addProductWeighting(String str) { + productWeightings.add(str); } /** - * Returns the set of evidence. + * Returns an unmodifiable set of vendor Weightings - a list of terms that + * are believed to be of higher confidence when also found in another + * location. * - * @return the set of evidence. + * @return an unmodifiable set of vendor weighting strings */ - public Set getEvidence() { - return list; + public synchronized Set getVendorWeightings() { + return Collections.unmodifiableSet(new HashSet<>(vendorWeightings)); } /** - * Returns the set of evidence from a given source. + * Returns an unmodifiable set of product Weightings - a list of terms that + * are believed to be of higher confidence when also found in another + * location. * - * @param source the source of the evidence - * @return the set of evidence. + * @return an unmodifiable set of vendor weighting strings */ - public Set getEvidence(String source) { - if (source == null) { - return null; - } - final Set ret = new HashSet<>(); - for (Evidence e : list) { - if (source.equals(e.getSource())) { - ret.add(e); + public synchronized Set getProductWeightings() { + return Collections.unmodifiableSet(new HashSet<>(productWeightings)); + } + + /** + * Returns the unmodifiable set of evidence of the given type. + * + * @param type the type of evidence (vendor, product, version) + * @return the unmodifiable set of evidence + */ + public synchronized Set getEvidence(EvidenceType type) { + if (null != type) { + switch (type) { + case VENDOR: + return Collections.unmodifiableSet(new HashSet<>(vendors)); + case PRODUCT: + return Collections.unmodifiableSet(new HashSet<>(products)); + case VERSION: + return Collections.unmodifiableSet(new HashSet<>(versions)); + default: + break; } } - return ret; + return null; } /** - * Returns the set of evidence from a given source and name. + * Tests if the evidence collection contains the given evidence. * - * @param source the source of the evidence - * @param name the name of the evidence to return - * @return the set of evidence. + * @param type the type of evidence (vendor, product, version) + * @param e the evidence to search + * @return true if the evidence is found; otherwise false */ - public Set getEvidence(String source, String name) { - if (source == null || name == null) { - return null; - } - final Set ret = new HashSet<>(); - for (Evidence e : list) { - if (source.equals(e.getSource()) && name.equals(e.getName())) { - ret.add(e); - } - } - return ret; - } - - /** - * Implements the iterator interface for the Evidence Collection. - * - * @return an Iterator<Evidence> - */ - @Override - public Iterator iterator() { - return list.iterator(); - } - - /** - * Used to determine if a given string was used (aka read). - * - * @param text the string to search for. - * @return whether or not the string was used. - */ - public boolean containsUsedString(String text) { - if (text == null) { - return false; - } - final String textToTest = text.toLowerCase(); - - for (Evidence e : EvidenceCollection.EVIDENCE_USED.filter(this)) { - //TODO consider changing the regex to only compare alpha-numeric (i.e. strip everything else) - final String item = e.getValue(); - if (item != null) { - final String uc = urlCorrection(item.toLowerCase()); - if (uc != null) { - final String value = uc.replaceAll("[\\s_-]", ""); - if (value.contains(textToTest)) { - return true; - } - } + public synchronized boolean contains(EvidenceType type, Evidence e) { + if (null != type) { + switch (type) { + case VENDOR: + return vendors.contains(e); + case PRODUCT: + return products.contains(e); + case VERSION: + return versions.contains(e); + default: + break; } } return false; } /** - * Used to determine if a given version was used (aka read) from the - * EvidenceCollection. - * - * @param version the version to search for within the collected evidence. - * @return whether or not the string was used. - */ - public boolean containsUsedVersion(DependencyVersion version) { - if (version == null) { - return false; - } - - for (Evidence e : EvidenceCollection.EVIDENCE_USED.filter(this)) { - final DependencyVersion value = DependencyVersionUtil.parseVersion(e.getValue()); - if (value != null && value.matchesAtLeastThreeLevels(version)) { - return true; - } - } - return false; - } - - /** - * Returns whether or not the collection contains evidence of a specified - * Confidence. - * + * Returns whether or not the collection contains evidence of a + * specified type and confidence. + * @param type the type of evidence (vendor, product, version) * @param confidence A Confidence value. * @return boolean. */ - public boolean contains(Confidence confidence) { - for (Evidence e : list) { + public synchronized boolean contains(EvidenceType type, Confidence confidence) { + if (null == type) { + return false; + } + Set col; + switch (type) { + case VENDOR: + col = vendors; + break; + case PRODUCT: + col = products; + break; + case VERSION: + col = versions; + break; + default: + return false; + } + for (Evidence e : col) { if (e.getConfidence().equals(confidence)) { return true; } @@ -312,73 +346,27 @@ public class EvidenceCollection implements Serializable, Iterable { return false; } - /** - * Merges multiple EvidenceCollections together, only merging evidence that - * was used, into a new EvidenceCollection. - * - * @param ec One or more EvidenceCollections. - * @return a new EvidenceCollection containing the used evidence. - */ - public static EvidenceCollection mergeUsed(EvidenceCollection... ec) { - final EvidenceCollection ret = new EvidenceCollection(); - for (EvidenceCollection col : ec) { - for (Evidence e : col.list) { - if (e.isUsed()) { - ret.addEvidence(e); - } - } - } - return ret; - } - - /** - * Merges multiple EvidenceCollections together. - * - * @param ec One or more EvidenceCollections. - * @return a new EvidenceCollection. - */ - public static EvidenceCollection merge(EvidenceCollection... ec) { - final EvidenceCollection ret = new EvidenceCollection(); - for (EvidenceCollection col : ec) { - ret.list.addAll(col.list); - ret.weightedStrings.addAll(col.weightedStrings); - } - return ret; - } - - /** - * Merges multiple EvidenceCollections together; flattening all of the - * evidence items by removing the confidence. - * - * @param ec One or more EvidenceCollections - * @return new set of evidence resulting from merging the evidence in the - * collections - */ - public static Set mergeForDisplay(EvidenceCollection... ec) { - final Set ret = new TreeSet<>(); - for (EvidenceCollection col : ec) { - for (Evidence e : col) { - //if (e.isUsed()) { - final Evidence newEvidence = new Evidence(e.getSource(), e.getName(), e.getValue(), null); - newEvidence.setUsed(true); - ret.add(newEvidence); - //} - } - } - return ret; - } - /** * Returns a string of evidence 'values'. * * @return a string containing the evidence. */ @Override - public String toString() { + public synchronized String toString() { final StringBuilder sb = new StringBuilder(); - for (Evidence e : this.list) { - sb.append(e.getValue()).append(' '); + sb.append("{vendors: ["); + for (Evidence e : this.vendors) { + sb.append("'").append(e.getValue()).append("', "); } + sb.append("],/nproducts: ["); + for (Evidence e : this.products) { + sb.append("'").append(e.getValue()).append("', "); + } + sb.append("],/nversions: ["); + for (Evidence e : this.versions) { + sb.append("'").append(e.getValue()).append("', "); + } + sb.append("]"); return sb.toString(); } @@ -387,49 +375,34 @@ public class EvidenceCollection implements Serializable, Iterable { * * @return the number of elements in the collection. */ - public int size() { - return list.size(); + public synchronized int size() { + return vendors.size() + products.size() + versions.size(); } - /** - *

- * Takes a string that may contain a fully qualified domain and it will - * return the string having removed the query string, the protocol, the - * sub-domain of 'www', and the file extension of the path.

- *

- * This is useful for checking if the evidence contains a specific string. - * The presence of the protocol, file extension, etc. may produce false - * positives. - * - *

- * Example, given the following input:

- * 'Please visit https://www.owasp.com/path1/path2/file.php?id=439' - *

- * The function would return:

- * 'Please visit owasp path1 path2 file' - * - * @param value the value that may contain a url - * @return the modified string - */ - private String urlCorrection(String value) { - if (value == null || !UrlStringUtils.containsUrl(value)) { - return value; + @Override + public int hashCode() { + return new HashCodeBuilder(13, 43) + .append(vendors) + .append(vendorWeightings) + .append(products) + .append(productWeightings) + .append(versions) + .toHashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || getClass() != obj.getClass()) { + return false; } - final StringBuilder sb = new StringBuilder(value.length()); - final String[] parts = value.split("\\s"); - for (String part : parts) { - if (UrlStringUtils.isUrl(part)) { - try { - final List data = UrlStringUtils.extractImportantUrlData(part); - sb.append(' ').append(StringUtils.join(data, ' ')); - } catch (MalformedURLException ex) { - LOGGER.debug("error parsing {}", part, ex); - sb.append(' ').append(part); - } - } else { - sb.append(' ').append(part); - } - } - return sb.toString().trim(); + final EvidenceCollection other = (EvidenceCollection) obj; + return new EqualsBuilder() + .appendSuper(super.equals(obj)) + .append(this.vendors, other.vendors) + .append(this.vendorWeightings, other.vendorWeightings) + .append(this.products, other.products) + .append(this.productWeightings, other.productWeightings) + .append(this.versions, other.versions) + .isEquals(); } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceType.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceType.java new file mode 100644 index 000000000..efbe92e94 --- /dev/null +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/EvidenceType.java @@ -0,0 +1,39 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2017 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.dependency; + +/** + * The types of evidence. + * + * @author jeremy long + */ +public enum EvidenceType { + /** + * Vendor evidence. + */ + VENDOR, + /** + * Product evidence. + */ + PRODUCT, + /** + * Version evidence. + */ + VERSION + +} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Identifier.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Identifier.java index a9ff9ca41..35c50d168 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Identifier.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Identifier.java @@ -18,12 +18,14 @@ package org.owasp.dependencycheck.dependency; import java.io.Serializable; +import javax.annotation.concurrent.ThreadSafe; /** * In identifier such as a CPE or dependency coordinates (i.e. GAV). * * @author Jeremy Long */ +@ThreadSafe public class Identifier implements Serializable, Comparable { // diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Reference.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Reference.java index a3ebdc582..8c44bf566 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Reference.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Reference.java @@ -18,6 +18,7 @@ package org.owasp.dependencycheck.dependency; import java.io.Serializable; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.builder.CompareToBuilder; /** @@ -26,6 +27,7 @@ import org.apache.commons.lang3.builder.CompareToBuilder; * * @author Jeremy Long */ +@ThreadSafe public class Reference implements Serializable, Comparable { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java index 3574fba1a..dee7b5f80 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/Vulnerability.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.lang3.builder.CompareToBuilder; /** @@ -30,6 +31,7 @@ import org.apache.commons.lang3.builder.CompareToBuilder; * * @author Jeremy Long */ +@NotThreadSafe public class Vulnerability implements Serializable, Comparable { /** @@ -276,6 +278,8 @@ public class Vulnerability implements Serializable, Comparable { * @param vulnSoftware the vulnerable software */ public void updateVulnerableSoftware(VulnerableSoftware vulnSoftware) { + //this behavior is because the vuln software being updated may have + // a value that is not included in the hash/comparison if (vulnerableSoftware.contains(vulnSoftware)) { vulnerableSoftware.remove(vulnSoftware); } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerabilityComparator.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerabilityComparator.java index 3ce8e5a32..5432559a0 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerabilityComparator.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerabilityComparator.java @@ -19,12 +19,14 @@ package org.owasp.dependencycheck.dependency; import java.io.Serializable; import java.util.Comparator; +import javax.annotation.concurrent.ThreadSafe; /** * Comparator for Vulnerability objects. * * @author Jeremy Long */ +@ThreadSafe public class VulnerabilityComparator implements Comparator, Serializable { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java index 9c3570914..11eaefc6a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/dependency/VulnerableSoftware.java @@ -20,16 +20,19 @@ package org.owasp.dependencycheck.dependency; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.StringUtils; import org.owasp.dependencycheck.data.cpe.IndexEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * A record containing information about vulnerable software. This is referenced from a vulnerability. + * A record containing information about vulnerable software. This is referenced + * from a vulnerability. * * @author Jeremy Long */ +@ThreadSafe public class VulnerableSoftware extends IndexEntry implements Serializable, Comparable { /** @@ -41,6 +44,26 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp */ private static final long serialVersionUID = 307319490326651052L; + /** + * If present, indicates that previous version are vulnerable. + */ + private String previousVersion; + + /** + * The name of the cpe. + */ + private String name; + + /** + * The product version number. + */ + private String version; + + /** + * The product update version. + */ + private String update; + /** * Parse a CPE entry from the cpe string representation. * @@ -58,7 +81,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp /** *

- * Parses a name attribute value, from the cpe.xml, into its corresponding parts: vendor, product, version, update.

+ * Parses a name attribute value, from the cpe.xml, into its corresponding + * parts: vendor, product, version, update.

*

* Example:

*    cpe:/a:apache:struts:1.1:rc2 @@ -93,10 +117,6 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp } } } - /** - * If present, indicates that previous version are vulnerable. - */ - private String previousVersion; /** * Indicates if previous versions of this software are vulnerable. @@ -126,7 +146,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp } /** - * Standard equals implementation to compare this VulnerableSoftware to another object. + * Standard equals implementation to compare this VulnerableSoftware to + * another object. * * @param obj the object to compare * @return whether or not the objects are equal @@ -156,7 +177,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp } /** - * Standard toString() implementation display the name and whether or not previous versions are also affected. + * Standard toString() implementation display the name and whether or not + * previous versions are also affected. * * @return a string representation of the object */ @@ -224,10 +246,9 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp } /** - * Determines if the string passed in is a positive integer. - * To be counted as a positive integer, the string must only contain 0-9 - * and must not have any leading zeros (though "0" is a valid positive - * integer). + * Determines if the string passed in is a positive integer. To be counted + * as a positive integer, the string must only contain 0-9 and must not have + * any leading zeros (though "0" is a valid positive integer). * * @param str the string to test * @return true if the string only contains 0-9, otherwise false. @@ -251,10 +272,6 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp } return true; } - /** - * The name of the cpe. - */ - private String name; /** * Get the value of name. @@ -273,10 +290,6 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp public void setName(String name) { this.name = name; } - /** - * The product version number. - */ - private String version; /** * Get the value of version. @@ -295,10 +308,6 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp public void setVersion(String version) { this.version = version; } - /** - * The product update version. - */ - private String update; /** * Get the value of update. @@ -341,7 +350,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp } /** - * Replaces '+' with '%2B' and then URL Decodes the string attempting first UTF-8, then ASCII, then default. + * Replaces '+' with '%2B' and then URL Decodes the string attempting first + * UTF-8, then ASCII, then default. * * @param string the string to URL Decode * @return the URL Decoded string @@ -362,7 +372,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp } /** - * Call {@link java.net.URLDecoder#decode(String)} to URL decode using the default encoding. + * Call {@link java.net.URLDecoder#decode(String)} to URL decode using the + * default encoding. * * @param text www-form-encoded URL to decode * @return the newly decoded String diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/DependencyNotFoundException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/DependencyNotFoundException.java index 3406b9900..4e0f00ead 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/DependencyNotFoundException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/DependencyNotFoundException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.exception; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception used when a dependency could not be found. * * @author Jeremy Long */ +@ThreadSafe public class DependencyNotFoundException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java index e7bf7e9dd..2c662ac98 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ExceptionCollection.java @@ -21,12 +21,14 @@ import java.io.PrintStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; /** * A collection of several exceptions. * * @author Jeremy Long */ +@NotThreadSafe public class ExceptionCollection extends Exception { /** * The serial version uid. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/H2DBLockException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/H2DBLockException.java index 55e496592..5b4ae2dc9 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/H2DBLockException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/H2DBLockException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.exception; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception used when trying to obtain a lock on the H2 database. * * @author Jeremy Long */ +@ThreadSafe public class H2DBLockException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/InitializationException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/InitializationException.java index f818ebff6..0e36540f5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/InitializationException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/InitializationException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.exception; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception used when initializing analyzers. * * @author Jeremy Long */ +@ThreadSafe public class InitializationException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/NoDataException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/NoDataException.java index 2e6c7d94c..5e0e11a88 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/NoDataException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/NoDataException.java @@ -18,12 +18,14 @@ package org.owasp.dependencycheck.exception; import java.io.IOException; +import javax.annotation.concurrent.ThreadSafe; /** * An exception used when the data needed does not exist to perform analysis. * * @author Jeremy Long */ +@ThreadSafe public class NoDataException extends IOException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ReportException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ReportException.java index 7199b3520..7942ac008 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ReportException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ReportException.java @@ -17,11 +17,14 @@ */ package org.owasp.dependencycheck.exception; +import javax.annotation.concurrent.ThreadSafe; + /** * An exception used when generating reports. * * @author Jeremy Long */ +@ThreadSafe public class ReportException extends Exception { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ScanAgentException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ScanAgentException.java index 5f4c5dce9..2e742f31f 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ScanAgentException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/exception/ScanAgentException.java @@ -18,12 +18,15 @@ package org.owasp.dependencycheck.exception; import java.io.IOException; +import javax.annotation.concurrent.ThreadSafe; /** - * An exception used when using @{link DependencyCheckScanAgent} to conduct a scan and the scan fails. + * An exception used when using @{link DependencyCheckScanAgent} to conduct a + * scan and the scan fails. * * @author Steve Springett */ +@ThreadSafe public class ScanAgentException extends IOException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/EscapeTool.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/EscapeTool.java index 4eb456176..2f8819421 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/EscapeTool.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/EscapeTool.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.reporting; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Set; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.lang3.StringEscapeUtils; import org.owasp.dependencycheck.dependency.Identifier; import org.slf4j.Logger; @@ -32,6 +33,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public class EscapeTool { /** @@ -97,6 +99,20 @@ public class EscapeTool { return StringEscapeUtils.escapeJson(text); } + /** + * JavaScript encodes the provided text. + * + * @param text the text to encode + * @return the JavaScript encoded text + */ + public String javascript(String text) { + if (text == null || text.isEmpty()) { + return text; + } + //until lang3 has escapeJavaScript we use this... + return org.apache.commons.lang.StringEscapeUtils.escapeJavaScript(text); + } + /** * Formats text for CSV format. This includes trimming whitespace, replace * line breaks with spaces, and if necessary quotes the text and/or escapes diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java index df32a7402..ee99e6f12 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/ReportGenerator.java @@ -19,7 +19,6 @@ package org.owasp.dependencycheck.reporting; import java.util.List; -import com.google.gson.JsonSyntaxException; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; @@ -35,6 +34,8 @@ import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; +import javax.annotation.concurrent.NotThreadSafe; +import org.apache.commons.lang3.text.WordUtils; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.context.Context; @@ -45,6 +46,7 @@ import org.joda.time.format.DateTimeFormatter; import org.owasp.dependencycheck.analyzer.Analyzer; import org.owasp.dependencycheck.data.nvdcve.DatabaseProperties; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.ReportException; import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.Settings; @@ -59,6 +61,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@NotThreadSafe public class ReportGenerator { /** @@ -104,6 +107,10 @@ public class ReportGenerator { * The Velocity Engine Context. */ private final Context context; + /** + * The configured settings. + */ + private final Settings settings; /** * Constructs a new ReportGenerator. @@ -113,8 +120,11 @@ public class ReportGenerator { * @param analyzers the list of analyzers used * @param properties the database properties (containing timestamps of the * NVD CVE data) + * @param settings a reference to the database settings */ - public ReportGenerator(String applicationName, List dependencies, List analyzers, DatabaseProperties properties) { + public ReportGenerator(String applicationName, List dependencies, List analyzers, + DatabaseProperties properties, Settings settings) { + this.settings = settings; velocityEngine = createVelocityEngine(); velocityEngine.init(); context = createContext(applicationName, dependencies, analyzers, properties); @@ -131,11 +141,11 @@ public class ReportGenerator { * @param analyzers the list of analyzers used * @param properties the database properties (containing timestamps of the * NVD CVE data) + * @param settings a reference to the database settings */ public ReportGenerator(String applicationName, String groupID, String artifactID, String version, - List dependencies, List analyzers, DatabaseProperties properties) { - - this(applicationName, dependencies, analyzers, properties); + List dependencies, List analyzers, DatabaseProperties properties, Settings settings) { + this(applicationName, dependencies, analyzers, properties, settings); if (version != null) { context.put("applicationVersion", version); } @@ -187,7 +197,11 @@ public class ReportGenerator { ctxt.put("scanDate", scanDate); ctxt.put("scanDateXML", scanDateXML); ctxt.put("enc", new EscapeTool()); - ctxt.put("version", Settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown")); + ctxt.put("WordUtils", new WordUtils()); + ctxt.put("VENDOR", EvidenceType.VENDOR); + ctxt.put("PRODUCT", EvidenceType.PRODUCT); + ctxt.put("VERSION", EvidenceType.VERSION); + ctxt.put("version", settings.getString(Settings.KEYS.APPLICATION_VERSION, "Unknown")); return ctxt; } @@ -246,22 +260,6 @@ public class ReportGenerator { } } -// /** -// * Writes the dependency-check report(s). -// * -// * @param outputStream the OutputStream to send the generated report to -// * @param format the format the report should be written in -// * @throws ReportException thrown if the report format is ALL -// * @throws IOException is thrown when the template file does not exist -// * @throws Exception is thrown if there is an error writing out the reports -// */ -// public void write(OutputStream outputStream, Format format) throws ReportException, IOException, Exception { -// if (format == Format.ALL) { -// throw new ReportException("Unable to write ALL reports to a single output stream, please check the API"); -// } -// final String templateName = format.toString().toLowerCase() + "Report"; -// processTemplate(templateName, outputStream); -// } /** * Determines the report file name based on the give output location and * format. If the output location contains a full file name that has the diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/VelocityLoggerRedirect.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/VelocityLoggerRedirect.java index 73a05ee31..fc6bf446c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/VelocityLoggerRedirect.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/reporting/VelocityLoggerRedirect.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.reporting; +import javax.annotation.concurrent.ThreadSafe; import org.apache.velocity.runtime.RuntimeServices; import org.apache.velocity.runtime.log.LogChute; import org.slf4j.Logger; @@ -36,6 +37,7 @@ import org.slf4j.LoggerFactory; * * @author Steve Springett */ +@ThreadSafe public class VelocityLoggerRedirect implements LogChute { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java index 3dd1042fc..bdc23442b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DBUtils.java @@ -21,14 +21,17 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** + * Collection of utility methods for working with database objects. * * @author Jeremy Long */ +@ThreadSafe public final class DBUtils { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DateUtil.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DateUtil.java index 782b32379..415793681 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DateUtil.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DateUtil.java @@ -17,10 +17,13 @@ */ package org.owasp.dependencycheck.utils; +import javax.annotation.concurrent.ThreadSafe; + /** * * @author Jeremy Long */ +@ThreadSafe public final class DateUtil { /** @@ -30,9 +33,10 @@ public final class DateUtil { } /** - * Determines if the epoch date is within the range specified of the compareTo epoch time. This takes the - * (compareTo-date)/1000/60/60/24 to get the number of days. If the calculated days is less then the range the date - * is considered valid. + * Determines if the epoch date is within the range specified of the + * compareTo epoch time. This takes the (compareTo-date)/1000/60/60/24 to + * get the number of days. If the calculated days is less then the range the + * date is considered valid. * * @param date the date to be checked. * @param compareTo the date to compare to. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java index ff8bf15c8..1d5756468 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersion.java @@ -22,6 +22,7 @@ import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.lang3.StringUtils; /** @@ -38,8 +39,14 @@ import org.apache.commons.lang3.StringUtils; * * @author Jeremy Long */ +@NotThreadSafe public class DependencyVersion implements Iterable, Comparable { + /** + * A list of the version parts. + */ + private List versionParts; + /** * Constructor for a empty DependencyVersion. */ @@ -78,10 +85,6 @@ public class DependencyVersion implements Iterable, Comparable versionParts; /** * Get the value of versionParts. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java index b4ac9cbd6..4bf15f62b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/DependencyVersionUtil.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; /** *

@@ -29,6 +30,7 @@ import java.util.regex.Pattern; * * @author Jeremy Long */ +@ThreadSafe public final class DependencyVersionUtil { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java index 4eccc0129..08a0465bd 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/ExtractionUtil.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveInputStream; @@ -43,6 +44,7 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@ThreadSafe public final class ExtractionUtil { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/FileFilterBuilder.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/FileFilterBuilder.java index f320e0604..36c1ef4c0 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/FileFilterBuilder.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/FileFilterBuilder.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import javax.annotation.concurrent.NotThreadSafe; /** *

@@ -43,6 +44,7 @@ import java.util.Set; * @author Dale Visser * @see Builder pattern */ +@NotThreadSafe public class FileFilterBuilder { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Filter.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Filter.java index 554a70dc1..3a303d1c2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Filter.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Filter.java @@ -2,6 +2,7 @@ package org.owasp.dependencycheck.utils; import java.util.Iterator; import java.util.NoSuchElementException; +import javax.annotation.concurrent.NotThreadSafe; /** * This is an abstract filter that can be used to filter iterable list. @@ -14,6 +15,7 @@ import java.util.NoSuchElementException; * * @param the type to filter */ +@NotThreadSafe public abstract class Filter { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/H2DBLock.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/H2DBLock.java index 1d274bbd2..001d9141c 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/H2DBLock.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/H2DBLock.java @@ -21,8 +21,10 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileLock; +import java.security.SecureRandom; +import java.sql.Timestamp; import java.util.Date; -import org.owasp.dependencycheck.data.nvdcve.ConnectionFactory; +import javax.annotation.concurrent.NotThreadSafe; import org.owasp.dependencycheck.exception.H2DBLockException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,12 +33,21 @@ import org.slf4j.LoggerFactory; * * @author Jeremy Long */ +@NotThreadSafe public class H2DBLock { /** * The logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(H2DBLock.class); + /** + * How long to sleep waiting for the lock. + */ + public static final int SLEEP_DURATION = 10000; + /** + * Max attempts to obtain a lock. + */ + public static final int MAX_SLEEP_COUNT = 120; /** * The file lock. */ @@ -49,6 +60,27 @@ public class H2DBLock { * The lock file. */ private File lockFile = null; + /** + * The configured settings. + */ + private final Settings settings; + /** + * A random string used to validate the lock. + */ + private final String magic; + + /** + * Constructs a new H2DB Lock object with the configured settings. + * + * @param settings the configured settings + */ + public H2DBLock(Settings settings) { + this.settings = settings; + final byte[] random = new byte[16]; + final SecureRandom gen = new SecureRandom(); + gen.nextBytes(random); + magic = Checksum.getHex(random); + } /** * Determine if the lock is currently held. @@ -65,49 +97,68 @@ public class H2DBLock { * @throws H2DBLockException thrown if a lock could not be obtained */ public void lock() throws H2DBLockException { - if (ConnectionFactory.isH2Connection()) { - try { - final File dir = Settings.getDataDirectory(); - lockFile = new File(dir, "dc.update.lock"); - if (lockFile.isFile() && getFileAge(lockFile) > 5 && !lockFile.delete()) { + try { + final File dir = settings.getDataDirectory(); + lockFile = new File(dir, "dc.update.lock"); + if (!lockFile.getParentFile().isDirectory() && !lockFile.mkdir()) { + throw new H2DBLockException("Unable to create path to data directory."); + } + if (lockFile.isFile() && getFileAge(lockFile) > 30) { + LOGGER.debug("An old db update lock file was found: {}", lockFile.getAbsolutePath()); + if (!lockFile.delete()) { LOGGER.warn("An old db update lock file was found but the system was unable to delete " + "the file. Consider manually deleting {}", lockFile.getAbsolutePath()); } - int ctr = 0; - do { - try { - if (!lockFile.exists() && lockFile.createNewFile()) { - file = new RandomAccessFile(lockFile, "rw"); - lock = file.getChannel().lock(); - } - } catch (IOException ex) { - LOGGER.trace("Expected error as another thread has likely locked the file", ex); - } finally { - if (lock == null && file != null) { - try { - file.close(); - } catch (IOException ex) { - LOGGER.trace("Unable to close the ulFile", ex); - } - } - } - if (lock == null || !lock.isValid()) { - try { - LOGGER.debug("Sleeping thread {} for 5 seconds because we could not obtain the update lock.", - Thread.currentThread().getName()); - Thread.sleep(5000); - } catch (InterruptedException ex) { - LOGGER.trace("ignorable error, sleep was interrupted.", ex); - Thread.currentThread().interrupt(); - } - } - } while (++ctr < 60 && (lock == null || !lock.isValid())); - if (lock == null || !lock.isValid()) { - throw new H2DBLockException("Unable to obtain the update lock, skipping the database update. Skippinig the database update."); - } - } catch (IOException ex) { - throw new H2DBLockException(ex.getMessage(), ex); } + int ctr = 0; + do { + try { + if (!lockFile.exists() && lockFile.createNewFile()) { + file = new RandomAccessFile(lockFile, "rw"); + lock = file.getChannel().lock(); + file.writeBytes(magic); + file.getChannel().force(true); + Thread.sleep(20); + file.seek(0); + final String current = file.readLine(); + if (current != null && !current.equals(magic)) { + lock.close(); + lock = null; + LOGGER.debug("Another process obtained a lock first ({})", Thread.currentThread().getName()); + } else { + final Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + LOGGER.debug("Lock file created ({}) {} @ {}", Thread.currentThread().getName(), magic, timestamp.toString()); + } + } + } catch (IOException | InterruptedException ex) { + LOGGER.trace("Expected error as another thread has likely locked the file", ex); + } finally { + if (lock == null && file != null) { + try { + file.close(); + file = null; + } catch (IOException ex) { + LOGGER.trace("Unable to close the lock file", ex); + } + } + } + if (lock == null || !lock.isValid()) { + try { + final Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + LOGGER.debug("Sleeping thread {} ({}) for 10 seconds because an exclusive lock on the database could not be obtained ({})", + Thread.currentThread().getName(), magic, timestamp.toString()); + Thread.sleep(SLEEP_DURATION); + } catch (InterruptedException ex) { + LOGGER.debug("sleep was interrupted.", ex); + Thread.currentThread().interrupt(); + } + } + } while (++ctr < MAX_SLEEP_COUNT && (lock == null || !lock.isValid())); + if (lock == null || !lock.isValid()) { + throw new H2DBLockException("Unable to obtain the update lock, skipping the database update. Skippinig the database update."); + } + } catch (IOException ex) { + throw new H2DBLockException(ex.getMessage(), ex); } } @@ -120,7 +171,7 @@ public class H2DBLock { lock.release(); lock = null; } catch (IOException ex) { - LOGGER.trace("Ignorable exception", ex); + LOGGER.debug("Failed to release lock", ex); } } if (file != null) { @@ -128,14 +179,25 @@ public class H2DBLock { file.close(); file = null; } catch (IOException ex) { - LOGGER.trace("Ignorable exception", ex); + LOGGER.debug("Unable to delete lock file", ex); } } - if (lockFile != null && lockFile.isFile() && !lockFile.delete()) { - LOGGER.error("Lock file '{}' was unable to be deleted. Please manually delete this file.", lockFile.toString()); - lockFile.deleteOnExit(); + if (lockFile != null && lockFile.isFile()) { + try (RandomAccessFile f = new RandomAccessFile(lockFile, "rw")) { + final String m = f.readLine(); + //yes, we are explicitly calling close on an auto-closable object - this is so we can delete the file. + f.close(); + if (m != null && m.equals(magic) && !lockFile.delete()) { + LOGGER.error("Lock file '{}' was unable to be deleted. Please manually delete this file.", lockFile.toString()); + lockFile.deleteOnExit(); + } + } catch (IOException ex) { + LOGGER.debug("Error deleting lock file", ex); + } } lockFile = null; + final Timestamp timestamp = new Timestamp(System.currentTimeMillis()); + LOGGER.debug("Lock released ({}) {} @ {}", Thread.currentThread().getName(), magic, timestamp.toString()); } /** @@ -144,9 +206,11 @@ public class H2DBLock { * @param file the file to calculate the age * @return the age of the file */ - private long getFileAge(File file) { + private double getFileAge(File file) { final Date d = new Date(); final long modified = file.lastModified(); - return (d.getTime() - modified) / 1000 / 60; + final double time = (d.getTime() - modified) / 1000 / 60; + LOGGER.debug("Lock file age is {} minutes", time); + return time; } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Pair.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Pair.java index 49f42570e..90a2eb3dd 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Pair.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/Pair.java @@ -17,6 +17,8 @@ */ package org.owasp.dependencycheck.utils; +import javax.annotation.concurrent.ThreadSafe; + /** * A generic pair of elements. * @@ -25,6 +27,7 @@ package org.owasp.dependencycheck.utils; * * @author Jeremy Long */ +@ThreadSafe public class Pair { /** * The left element of the pair. diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/UrlStringUtils.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/UrlStringUtils.java index 6e2e63dbb..d96819a4d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/UrlStringUtils.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/utils/UrlStringUtils.java @@ -21,15 +21,15 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; /** * * @author Jeremy Long */ +@ThreadSafe public final class UrlStringUtils { /** @@ -44,8 +44,11 @@ public final class UrlStringUtils { * A listing of domain parts that should not be used as evidence. Yes, this * is an incomplete list. */ - private static final Set IGNORE_LIST = new HashSet<>( - Arrays.asList("www", "com", "org", "gov", "info", "name", "net", "pro", "tel", "mobi", "xxx")); + private static final String[] IGNORE_LIST = {"www", "com", "org", "gov", "info", "name", "net", "pro", "tel", "mobi", "xxx"}; + + static { + Arrays.sort(IGNORE_LIST); + } /** * Private constructor for a utility class. @@ -96,7 +99,7 @@ public final class UrlStringUtils { //add the domain except www and the tld. for (int i = 0; i < domain.length - 1; i++) { final String sub = domain[i]; - if (!IGNORE_LIST.contains(sub.toLowerCase())) { + if (Arrays.binarySearch(IGNORE_LIST, sub.toLowerCase()) < 0) { importantParts.add(sub); } } diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlEntity.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlEntity.java index 9e436d9bc..341ebd558 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlEntity.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlEntity.java @@ -3,6 +3,7 @@ package org.owasp.dependencycheck.xml; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; /** * This is a utility class to convert named XML Entities (such as ø) into @@ -12,6 +13,7 @@ import java.util.Map; * * @author https://stackoverflow.com/users/823393/oldcurmudgeon */ +@ThreadSafe public final class XmlEntity { /** @@ -20,7 +22,7 @@ public final class XmlEntity { private static final Map SPECIALS; // - /** + /* * Create a map HTML Named Entities to their numeric equivalent. Derived * from Wikipedia * http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlInputStream.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlInputStream.java index cec6f0407..c089cec1b 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlInputStream.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/XmlInputStream.java @@ -3,6 +3,7 @@ package org.owasp.dependencycheck.xml; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import javax.annotation.concurrent.NotThreadSafe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,6 +22,7 @@ import org.slf4j.LoggerFactory; * * @author https://stackoverflow.com/users/823393/oldcurmudgeon */ +@NotThreadSafe public class XmlInputStream extends FilterInputStream { /** @@ -34,11 +36,11 @@ public class XmlInputStream extends FilterInputStream { /** * Holder for everything we've read. */ - private StringBuilder red = new StringBuilder(); + private final StringBuilder red = new StringBuilder(); /** * Data that needs to be pushed back. */ - private StringBuilder pushBack = new StringBuilder(); + private final StringBuilder pushBack = new StringBuilder(); /** * How much we've given them. */ diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintErrorHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintErrorHandler.java index f73dc4382..15a041d7e 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintErrorHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintErrorHandler.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.xml.hints; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.utils.XmlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,7 @@ import org.xml.sax.SAXParseException; * * @author Jeremy Long */ +@ThreadSafe public class HintErrorHandler implements ErrorHandler { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java index 676067c5e..6fef7de80 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintHandler.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.xml.hints; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.xml.suppression.PropertyType; import org.xml.sax.Attributes; @@ -30,6 +31,7 @@ import org.xml.sax.helpers.DefaultHandler; * * @author Jeremy Long */ +@NotThreadSafe public class HintHandler extends DefaultHandler { // diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParseException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParseException.java index 4b5b19c6e..ec9267ed5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParseException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParseException.java @@ -18,12 +18,14 @@ package org.owasp.dependencycheck.xml.hints; import java.io.IOException; +import javax.annotation.concurrent.ThreadSafe; /** * An exception used when parsing a suppression rule file fails. * * @author Jeremy Long */ +@ThreadSafe public class HintParseException extends IOException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParser.java index 7f440049a..41f447ec4 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParser.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintParser.java @@ -24,6 +24,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; @@ -41,6 +43,7 @@ import org.xml.sax.XMLReader; * * @author Jeremy Long */ +@NotThreadSafe public class HintParser { /** @@ -73,25 +76,52 @@ public class HintParser { */ private static final String HINT_SCHEMA_OLD = "schema/dependency-hint.1.1.xsd"; + /** + * The hint rules. + */ + private List hintRules; + /** + * The vendor duplicating hint rules. + */ + private List vendorDuplicatingHintRules; + + /** + * Returns the hint rules. + * + * @return the hint rules + */ + @SuppressWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}) + public List getHintRules() { + return hintRules; + } + + /** + * Returns the vendor duplicating hint rules. + * + * @return the vendor duplicating hint rules + */ + public List getVendorDuplicatingHintRules() { + return vendorDuplicatingHintRules; + } + /** * Parses the given XML file and returns a list of the hints contained. * * @param file an XML file containing hints - * @return a list of hint rules * @throws HintParseException thrown if the XML file cannot be parsed */ - public Hints parseHints(File file) throws HintParseException { + public void parseHints(File file) throws HintParseException { //TODO there must be a better way to determine which schema to use for validation. try { try (FileInputStream fis = new FileInputStream(file)) { - return parseHints(fis); + parseHints(fis); } catch (IOException ex) { LOGGER.debug("", ex); throw new HintParseException(ex); } } catch (SAXException ex) { try (FileInputStream fis = new FileInputStream(file)) { - return parseHints(fis, HINT_SCHEMA_OLD); + parseHints(fis, HINT_SCHEMA_OLD); } catch (SAXException | IOException ex1) { throw new HintParseException(ex); } @@ -103,12 +133,11 @@ public class HintParser { * contained. * * @param inputStream an InputStream containing hint rules - * @return a list of hint rules * @throws HintParseException thrown if the XML cannot be parsed * @throws SAXException thrown if the XML cannot be parsed */ - public Hints parseHints(InputStream inputStream) throws HintParseException, SAXException { - return parseHints(inputStream, HINT_SCHEMA); + public void parseHints(InputStream inputStream) throws HintParseException, SAXException { + parseHints(inputStream, HINT_SCHEMA); } /** @@ -117,11 +146,10 @@ public class HintParser { * * @param inputStream an InputStream containing hint rules * @param schema the XSD to use to validate the XML against - * @return a list of hint rules * @throws HintParseException thrown if the XML cannot be parsed * @throws SAXException thrown if the XML cannot be parsed */ - private Hints parseHints(InputStream inputStream, String schema) throws HintParseException, SAXException { + private void parseHints(InputStream inputStream, String schema) throws HintParseException, SAXException { try (InputStream schemaStream = FileUtils.getResourceAsStream(schema)) { final HintHandler handler = new HintHandler(); final SAXParser saxParser = XmlUtils.buildSecureSaxParser(schemaStream); @@ -131,10 +159,8 @@ public class HintParser { try (Reader reader = new InputStreamReader(inputStream, "UTF-8")) { final InputSource in = new InputSource(reader); xmlReader.parse(in); - final Hints hints = new Hints(); - hints.setHintRules(handler.getHintRules()); - hints.setVendorDuplicatingHintRules(handler.getVendorDuplicatingHintRules()); - return hints; + this.hintRules = handler.getHintRules(); + this.vendorDuplicatingHintRules = handler.getVendorDuplicatingHintRules(); } } catch (ParserConfigurationException | FileNotFoundException ex) { LOGGER.debug("", ex); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java index e92cbdda9..1cf7e560d 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/HintRule.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.xml.hints; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Evidence; import org.owasp.dependencycheck.xml.suppression.PropertyType; @@ -30,6 +31,7 @@ import org.owasp.dependencycheck.xml.suppression.PropertyType; * * @author Jeremy Long */ +@NotThreadSafe public class HintRule { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/Hints.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/Hints.java deleted file mode 100644 index d44ed7984..000000000 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/Hints.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of dependency-check-core. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright (c) 2016 Jeremy Long. All Rights Reserved. - */ -package org.owasp.dependencycheck.xml.hints; - -import java.util.List; - -/** - * A collection of hint rules. - * - * @author Jeremy Long - */ -public class Hints { - - /** - * The list of hint rules. - */ - private List hintRules; - - /** - * The duplicating hint rules. - */ - private List vendorDuplicatingHintRules; - - /** - * Get the value of hintRules. - * - * @return the value of hintRules - */ - public List getHintRules() { - return hintRules; - } - - /** - * Set the value of hintRules. - * - * @param hintRules new value of hintRules - */ - public void setHintRules(List hintRules) { - this.hintRules = hintRules; - } - - /** - * Get the value of vendorDuplicatingHintRules. - * - * @return the value of vendorDuplicatingHintRules - */ - public List getVendorDuplicatingHintRules() { - return vendorDuplicatingHintRules; - } - - /** - * Set the value of vendorDuplicatingHintRules. - * - * @param vendorDuplicatingHintRules new value of vendorDuplicatingHintRules - */ - public void setVendorDuplicatingHintRules(List vendorDuplicatingHintRules) { - this.vendorDuplicatingHintRules = vendorDuplicatingHintRules; - } -} diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/VendorDuplicatingHintRule.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/VendorDuplicatingHintRule.java index c9a02a7bb..4918eb8ea 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/VendorDuplicatingHintRule.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/hints/VendorDuplicatingHintRule.java @@ -17,6 +17,8 @@ */ package org.owasp.dependencycheck.xml.hints; +import javax.annotation.concurrent.ThreadSafe; + /** * Used to duplicate vendor evidence within a collection. The intent is if any * evidence is found in a collection that matches the value given the evidence @@ -24,6 +26,7 @@ package org.owasp.dependencycheck.xml.hints; * * @author Jeremy Long */ +@ThreadSafe public class VendorDuplicatingHintRule { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/License.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/License.java index 108a94824..17a4ddfc7 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/License.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/License.java @@ -17,10 +17,13 @@ */ package org.owasp.dependencycheck.xml.pom; +import javax.annotation.concurrent.ThreadSafe; + /** * * @author jeremy */ +@ThreadSafe public class License { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/Model.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/Model.java index 2168ec950..4759121b5 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/Model.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/Model.java @@ -20,6 +20,7 @@ package org.owasp.dependencycheck.xml.pom; import java.util.ArrayList; import java.util.List; import java.util.Properties; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.lang3.text.StrLookup; import org.apache.commons.lang3.text.StrSubstitutor; @@ -29,6 +30,7 @@ import org.apache.commons.lang3.text.StrSubstitutor; * * @author jeremy */ +@NotThreadSafe public class Model { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java index a073b5ff6..91b2cc9bc 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.xml.pom; import java.util.ArrayDeque; import java.util.Deque; +import javax.annotation.concurrent.NotThreadSafe; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -28,6 +29,7 @@ import org.xml.sax.helpers.DefaultHandler; * * @author Jeremy Long */ +@NotThreadSafe public class PomHandler extends DefaultHandler { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParseException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParseException.java index be98e127c..6accd94fa 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParseException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParseException.java @@ -18,12 +18,14 @@ package org.owasp.dependencycheck.xml.pom; import java.io.IOException; +import javax.annotation.concurrent.ThreadSafe; /** * An exception used when parsing a suppression rule file fails. * * @author Jeremy Long */ +@ThreadSafe public class PomParseException extends IOException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParser.java index 155b0048a..6bc9469d2 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParser.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomParser.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import javax.annotation.concurrent.ThreadSafe; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import org.apache.commons.io.ByteOrderMark; @@ -42,6 +43,7 @@ import org.xml.sax.XMLReader; * * @author Jeremy Long */ +@ThreadSafe public class PomParser { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomUtils.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomUtils.java index 4a6d2e8fc..969974490 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomUtils.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomUtils.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.util.jar.JarFile; import java.util.zip.ZipEntry; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.analyzer.JarAnalyzer; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; @@ -31,6 +32,7 @@ import org.slf4j.LoggerFactory; * * @author jeremy */ +@ThreadSafe public final class PomUtils { /** @@ -52,6 +54,7 @@ public final class PomUtils { * or parsing the POM {@link Model} object */ public static Model readPom(File file) throws AnalysisException { + //noinspection CaughtExceptionImmediatelyRethrown try { final PomParser parser = new PomParser(); final Model model = parser.parse(file); @@ -85,6 +88,7 @@ public final class PomUtils { final ZipEntry entry = jar.getEntry(path); Model model = null; if (entry != null) { //should never be null + //noinspection CaughtExceptionImmediatelyRethrown try { final PomParser parser = new PomParser(); model = parser.parse(jar.getInputStream(entry)); diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/PropertyType.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/PropertyType.java index 8c1fb794f..381a58962 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/PropertyType.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/PropertyType.java @@ -18,6 +18,7 @@ package org.owasp.dependencycheck.xml.suppression; import java.util.regex.Pattern; +import javax.annotation.concurrent.ThreadSafe; /** * A simple PropertyType used to represent a string value that could be used as a regular expression or could be case @@ -25,6 +26,7 @@ import java.util.regex.Pattern; * * @author Jeremy Long */ +@ThreadSafe public class PropertyType { // diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java index d476f1aaa..6647759b6 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionErrorHandler.java @@ -17,6 +17,7 @@ */ package org.owasp.dependencycheck.xml.suppression; +import javax.annotation.concurrent.ThreadSafe; import org.owasp.dependencycheck.utils.XmlUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,7 @@ import org.xml.sax.SAXParseException; * * @author Jeremy Long */ +@ThreadSafe public class SuppressionErrorHandler implements ErrorHandler { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java index ec1b7b1c2..b8d457f88 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionHandler.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.xml.suppression; import java.util.ArrayList; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -28,6 +29,7 @@ import org.xml.sax.helpers.DefaultHandler; * * @author Jeremy Long */ +@NotThreadSafe public class SuppressionHandler extends DefaultHandler { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParseException.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParseException.java index d0d5c87df..7a56add3a 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParseException.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParseException.java @@ -18,12 +18,14 @@ package org.owasp.dependencycheck.xml.suppression; import java.io.IOException; +import javax.annotation.concurrent.ThreadSafe; /** * An exception used when parsing a suppression rule file fails. * * @author Jeremy Long */ +@ThreadSafe public class SuppressionParseException extends IOException { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java index a2f039a07..984c9468e 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionParser.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.List; +import javax.annotation.concurrent.ThreadSafe; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; @@ -42,6 +43,7 @@ import org.xml.sax.XMLReader; * * @author Jeremy Long */ +@ThreadSafe public class SuppressionParser { /** diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java index ca6c7ab05..7288685e3 100644 --- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java +++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/suppression/SuppressionRule.java @@ -18,8 +18,11 @@ package org.owasp.dependencycheck.xml.suppression; import java.util.ArrayList; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; +import javax.annotation.concurrent.NotThreadSafe; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; @@ -28,6 +31,7 @@ import org.owasp.dependencycheck.dependency.Vulnerability; * * @author Jeremy Long */ +@NotThreadSafe public class SuppressionRule { /** @@ -363,9 +367,8 @@ public class SuppressionRule { } if (this.hasCpe()) { - final Iterator itr = dependency.getIdentifiers().iterator(); - while (itr.hasNext()) { - final Identifier i = itr.next(); + final Set removalList = new HashSet<>(); + for (Identifier i : dependency.getIdentifiers()) { for (PropertyType c : this.cpe) { if (identifierMatches("cpe", c, i)) { if (!isBase()) { @@ -374,19 +377,22 @@ public class SuppressionRule { } dependency.addSuppressedIdentifier(i); } - itr.remove(); + removalList.add(i); break; } } } + for (Identifier i : removalList) { + dependency.removeIdentifier(i); + } } if (hasCve() || hasCwe() || hasCvssBelow()) { - final Iterator itr = dependency.getVulnerabilities().iterator(); - while (itr.hasNext()) { + final Set removeVulns = new HashSet<>(); + for (Vulnerability v : dependency.getVulnerabilities()) { boolean remove = false; - final Vulnerability v = itr.next(); for (String entry : this.cve) { if (entry.equalsIgnoreCase(v.getName())) { + removeVulns.add(v); remove = true; break; } @@ -398,6 +404,7 @@ public class SuppressionRule { final String toTest = v.getCwe().substring(0, toMatch.length()).toUpperCase(); if (toTest.equals(toMatch)) { remove = true; + removeVulns.add(v); break; } } @@ -407,6 +414,7 @@ public class SuppressionRule { for (float cvss : this.cvssBelow) { if (v.getCvssScore() < cvss) { remove = true; + removeVulns.add(v); break; } } @@ -418,9 +426,11 @@ public class SuppressionRule { } dependency.addSuppressedVulnerability(v); } - itr.remove(); } } + for (Vulnerability v : removeVulns) { + dependency.removeVulnerability(v); + } } } diff --git a/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml b/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml index fe76988ab..19656478e 100644 --- a/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml +++ b/dependency-check-core/src/main/resources/dependencycheck-base-suppression.xml @@ -56,10 +56,25 @@ - .*(\.(dll|jar|ear|war|pom)|pom\.xml)$ + .*(\.(dll|jar|ear|war|pom|nupkg|nuspec)|pom\.xml|package.json)$ cpe:/a:sandbox:sandbox - cpe:/a:openmedia:openmedia + cpe:/a:openmedia:openmedia + cpe:/a:file_project:file + cpe:/a:file:file + cpe:/a:shim:shim + cpe:/a:shim_project:shim + cpe:/a:date_project:date + cpe:/a:net_dns:net_dns + cpe:/a:nodejs:node.js + cpe:/a:nodejs:nodejs + cpe:/a:context_project:context ^com\.vaadin\.external\.google:android-json:.*$ cpe:/a:google:android + + + ^org\.glassfish:javax\.json:.*$ + cpe:/a:oracle:glassfish + + + + .*activerecord.*oracle.*\.gemspec + cpe:/a:ruby-i18n:i18n + cpe:/a:mikel_lindsaar:mail + cpe:/a:rest-client_project:rest-client + diff --git a/dependency-check-core/src/main/resources/dependencycheck.properties b/dependency-check-core/src/main/resources/dependencycheck.properties index 792964606..083293b2b 100644 --- a/dependency-check-core/src/main/resources/dependencycheck.properties +++ b/dependency-check-core/src/main/resources/dependencycheck.properties @@ -23,7 +23,7 @@ data.file_name=dc.h2.db ### the gradle PurgeDataExtension. data.version=3.0 -data.connection_string=jdbc:h2:file:%s;MV_STORE=FALSE;AUTOCOMMIT=ON;LOCK_MODE=0;FILE_LOCK=NO +data.connection_string=jdbc:h2:file:%s;MV_STORE=FALSE;AUTOCOMMIT=ON; #data.connection_string=jdbc:mysql://localhost:3306/dependencycheck # user name and password for the database connection. The inherent case is to use H2. diff --git a/dependency-check-core/src/main/resources/templates/htmlReport.vsl b/dependency-check-core/src/main/resources/templates/htmlReport.vsl index 025211943..ceb7922d9 100644 --- a/dependency-check-core/src/main/resources/templates/htmlReport.vsl +++ b/dependency-check-core/src/main/resources/templates/htmlReport.vsl @@ -699,8 +699,8 @@ Getting Help: $dependency.getVulnerabilities().size() - $cpeIdConf - $dependency.getEvidenceForDisplay().size() + $WordUtils.capitalizeFully($cpeIdConf.toString()) + $dependency.size() #end @@ -744,9 +744,15 @@ Getting Help:

$enc.html($id.type): $enc.html($id.value) #end #if ($id.confidence) -   Confidence:$id.confidence +   Confidence:$WordUtils.capitalizeFully($id.confidence.toString()) #end #if ($id.type=="cpe") - ##yes, we are HTML Encoding into JavaScript... the escape utils don't have a JS Encode and I haven't written one yet -    +    #end #if ($id.description || $id.notes)
    @@ -841,7 +846,7 @@ Getting Help: $enc.html($vuln.name)  

    +

    $enc.html($vuln.name)  

    #elseif($vuln.getSource().name().equals("NSP"))

    NSP-$enc.html($vuln.name)

    #end @@ -862,7 +867,7 @@ Getting Help: CWE: $vuln.cwe #end #if ($vuln.notes) -
    Notes: $enc.xml($vuln.notes) +
    Notes: $enc.html($vuln.notes) #end

    $enc.html($vuln.description) #if ($vuln.getReferences().size()>0) @@ -935,9 +940,15 @@ Getting Help:

    $enc.html($id.type): $enc.html($id.value)   suppressed #else
  • $enc.html($id.type): $enc.html($id.value)  suppressed @@ -984,7 +994,7 @@ Getting Help: $enc.html($id.type): $enc.html($id.value)  suppressed #end #if ($id.confidence) -   Confidence:$id.confidence +   Confidence:$WordUtils.capitalizeFully($id.confidence.toString()) #end #if ($id.description || $id.notes) #end diff --git a/dependency-check-core/src/main/resources/templates/jsonReport.vsl b/dependency-check-core/src/main/resources/templates/jsonReport.vsl index ef69987dc..e0e72d76d 100644 --- a/dependency-check-core/src/main/resources/templates/jsonReport.vsl +++ b/dependency-check-core/src/main/resources/templates/jsonReport.vsl @@ -67,7 +67,7 @@ #end ,"evidenceCollected": { "vendorEvidence": [ - #foreach($evidence in $dependency.getVendorEvidence()) + #foreach($evidence in $dependency.getEvidence($VENDOR)) #if($foreach.count > 1),#end{ "type": "vendor", "confidence": "$enc.json($evidence.getConfidence().toString())", @@ -78,7 +78,7 @@ #end ], "productEvidence": [ - #foreach($evidence in $dependency.getProductEvidence()) + #foreach($evidence in $dependency.getEvidence($PRODUCT)) #if($foreach.count > 1),#end{ "type": "product", "confidence": "$enc.json($evidence.getConfidence().toString())", @@ -89,7 +89,7 @@ #end ], "versionEvidence": [ - #foreach($evidence in $dependency.getVersionEvidence()) + #foreach($evidence in $dependency.getEvidence($VERSION)) #if($foreach.count > 1),#end { "type": "version", @@ -106,7 +106,6 @@ #foreach($id in $dependency.getIdentifiers()) #if($foreach.count > 1),#end { - "count": "$loopCount", "name": "$id.value", "type": "$enc.json($id.type)" #if($id.confidence),"confidence": "$id.confidence"#end diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/AnalysisTaskTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/AnalysisTaskTest.java index bc9888376..d66b5c98b 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/AnalysisTaskTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/AnalysisTaskTest.java @@ -28,7 +28,7 @@ public class AnalysisTaskTest extends BaseTest { @Test public void shouldAnalyzeReturnsTrueForNonFileTypeAnalyzers() { - AnalysisTask instance = new AnalysisTask(new HintAnalyzer(), null, null, null, null); + AnalysisTask instance = new AnalysisTask(new HintAnalyzer(), null, null, null); boolean shouldAnalyze = instance.shouldAnalyze(); assertTrue(shouldAnalyze); } @@ -44,7 +44,7 @@ public class AnalysisTaskTest extends BaseTest { result = true; }}; - AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, null, null, Settings.getInstance()); + AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, null, null); boolean shouldAnalyze = analysisTask.shouldAnalyze(); assertTrue(shouldAnalyze); @@ -61,7 +61,7 @@ public class AnalysisTaskTest extends BaseTest { result = false; }}; - AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, null, null, Settings.getInstance()); + AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, null, null); boolean shouldAnalyze = analysisTask.shouldAnalyze(); assertFalse(shouldAnalyze); @@ -69,7 +69,7 @@ public class AnalysisTaskTest extends BaseTest { @Test public void taskAnalyzes() throws Exception { - final AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, engine, null, Settings.getInstance()); + final AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, engine, null); new Expectations(analysisTask) {{ analysisTask.shouldAnalyze(); result = true; @@ -85,7 +85,7 @@ public class AnalysisTaskTest extends BaseTest { @Test public void taskDoesNothingIfItShouldNotAnalyze() throws Exception { - final AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, engine, null, Settings.getInstance()); + final AnalysisTask analysisTask = new AnalysisTask(fileTypeAnalyzer, dependency, engine, null); new Expectations(analysisTask) {{ analysisTask.shouldAnalyze(); result = false; diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java index cec529ff2..0ee382691 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseDBTestCase.java @@ -43,17 +43,19 @@ public abstract class BaseDBTestCase extends BaseTest { private final static Logger LOGGER = LoggerFactory.getLogger(BaseDBTestCase.class); @Before - public void setUpDb() throws Exception { + @Override + public void setUp() throws Exception { + super.setUp(); ensureDBExists(); } - public static void ensureDBExists() throws Exception { + public void ensureDBExists() throws Exception { File f = new File("./target/data/dc.h2.db"); if (f.exists() && f.isFile() && f.length() < 71680) { f.delete(); } - File dataPath = Settings.getDataDirectory(); - String fileName = Settings.getString(Settings.KEYS.DB_FILE_NAME); + File dataPath = getSettings().getDataDirectory(); + String fileName = getSettings().getString(Settings.KEYS.DB_FILE_NAME); LOGGER.trace("DB file name {}", fileName); File dataFile = new File(dataPath, fileName); LOGGER.trace("Ensuring {} exists", dataFile.toString()); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseTest.java index bdf307cb6..0e60c7399 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/BaseTest.java @@ -18,9 +18,11 @@ package org.owasp.dependencycheck; import java.io.File; import java.io.InputStream; import java.net.URISyntaxException; +import org.junit.After; import org.junit.AfterClass; import org.junit.Assume; +import org.junit.Before; import org.junit.BeforeClass; import org.owasp.dependencycheck.utils.Settings; @@ -30,9 +32,25 @@ import org.owasp.dependencycheck.utils.Settings; */ public class BaseTest { - @BeforeClass - public static void setUpClass() throws Exception { - Settings.initialize(); + /** + * The configured settings. + */ + private Settings settings; + + /** + * Initialize the {@link Settings}. + */ + @Before + public void setUp() throws Exception { + settings = new Settings(); + } + + /** + * Clean the {@link Settings}. + */ + @After + public void tearDown() throws Exception { + settings.cleanup(true); } @AfterClass @@ -45,13 +63,12 @@ public class BaseTest { System.err.println("------------------------------------------------"); System.err.println("------------------------------------------------"); } - - Settings.cleanup(true); } /** - * Returns the given resource as an InputStream using the object's class loader. The org.junit.Assume API is used so that test - * cases are skipped if the resource is not available. + * Returns the given resource as an InputStream using the object's class + * loader. The org.junit.Assume API is used so that test cases are skipped + * if the resource is not available. * * @param o the object used to obtain a reference to the class loader * @param resource the name of the resource to load @@ -63,20 +80,30 @@ public class BaseTest { } /** - * Returns the given resource as a File using the object's class loader. The org.junit.Assume API is used so that test cases - * are skipped if the resource is not available. + * Returns the given resource as a File using the object's class loader. The + * org.junit.Assume API is used so that test cases are skipped if the + * resource is not available. * * @param o the object used to obtain a reference to the class loader * @param resource the name of the resource to load * @return the resource as an File */ public static File getResourceAsFile(Object o, String resource) { - try{ + try { File f = new File(o.getClass().getClassLoader().getResource(resource).toURI().getPath()); Assume.assumeTrue(String.format("%n%n[SEVERE] Unable to load resource for test case: %s%n%n", resource), f.exists()); return f; - }catch (URISyntaxException e){ + } catch (URISyntaxException e) { throw new UnsupportedOperationException(e); } } + + /** + * Returns the settings for the test cases. + * + * @return + */ + protected Settings getSettings() { + return settings; + } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIT.java index 45597a7c4..fd3660946 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIT.java @@ -48,35 +48,34 @@ public class EngineIT extends BaseDBTestCase { @Test public void testEngine() throws IOException, InvalidSettingException, DatabaseException, ReportException, ExceptionCollection { String testClasses = "target/test-classes"; - boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Engine instance = new Engine(); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); - instance.scan(testClasses); - assertTrue(instance.getDependencies().size() > 0); - try { - instance.analyzeDependencies(); - } catch (ExceptionCollection ex) { - Set allowedMessages = new HashSet<>(); - allowedMessages.add("bundle-audit"); - allowedMessages.add("AssemblyAnalyzer"); - //allowedMessages.add("Unable to connect to"); - for (Throwable t : ex.getExceptions()) { - boolean isOk = false; - if (t.getMessage()!=null) { - for (String msg : allowedMessages) { - if (t.getMessage().contains(msg)) { - isOk=true; - break; + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + try (Engine instance = new Engine(getSettings())) { + instance.scan(testClasses); + assertTrue(instance.getDependencies().length > 0); + try { + instance.analyzeDependencies(); + } catch (ExceptionCollection ex) { + Set allowedMessages = new HashSet<>(); + allowedMessages.add("bundle-audit"); + allowedMessages.add("AssemblyAnalyzer"); + //allowedMessages.add("Unable to connect to"); + for (Throwable t : ex.getExceptions()) { + boolean isOk = false; + if (t.getMessage() != null) { + for (String msg : allowedMessages) { + if (t.getMessage().contains(msg)) { + isOk = true; + break; + } } } - } - if (!isOk) { - throw ex; + if (!isOk) { + throw ex; + } } } + instance.writeReports("dependency-check sample", new File("./target/"), "ALL"); + instance.close(); } - instance.writeReports("dependency-check sample", new File("./target/"), "ALL"); - instance.cleanup(); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineModeIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineModeIT.java index f1343adfd..1d08a9ee3 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineModeIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineModeIT.java @@ -14,7 +14,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; @@ -23,6 +22,7 @@ import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Assume; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.FileUtils; /** @@ -38,26 +38,35 @@ public class EngineModeIT extends BaseTest { private String originalDataDir = null; @Before + @Override public void setUp() throws Exception { + super.setUp(); // Have to use System properties as the Settings object pulls from the // system properties before configured properties - originalDataDir = Settings.getString(Settings.KEYS.DATA_DIRECTORY); + originalDataDir = getSettings().getString(Settings.KEYS.DATA_DIRECTORY); System.setProperty(Settings.KEYS.DATA_DIRECTORY, tempDir.newFolder().getAbsolutePath()); } @After - public void tearDown() throws IOException { - //delete temp files - FileUtils.delete(Settings.getDataDirectory()); - //Reset system property to original value just to be safe for other tests. - System.setProperty(Settings.KEYS.DATA_DIRECTORY, originalDataDir); - + @Override + public void tearDown() throws Exception { + try { + //delete temp files + FileUtils.delete(getSettings().getDataDirectory()); + //Reset system property to original value just to be safe for other tests. + System.setProperty(Settings.KEYS.DATA_DIRECTORY, originalDataDir); + } catch (IOException ex) { + throw new RuntimeException(ex); + } finally { + super.tearDown(); + } } @Test public void testEvidenceCollectionAndEvidenceProcessingModes() throws Exception { - List dependencies; - try (Engine engine = new Engine(Engine.Mode.EVIDENCE_COLLECTION)) { + Dependency[] dependencies; + try (Engine engine = new Engine(Engine.Mode.EVIDENCE_COLLECTION, getSettings())) { + engine.openDatabase(); //does nothing in the current mode assertDatabase(false); for (AnalysisPhase phase : Engine.Mode.EVIDENCE_COLLECTION.getPhases()) { assertThat(engine.getAnalyzers(phase), is(notNullValue())); @@ -69,14 +78,15 @@ public class EngineModeIT extends BaseTest { engine.scan(file); engine.analyzeDependencies(); dependencies = engine.getDependencies(); - assertThat(dependencies.size(), is(1)); - Dependency dependency = dependencies.get(0); - assertTrue(dependency.getVendorEvidence().toString().toLowerCase().contains("apache")); - assertTrue(dependency.getVendorEvidence().getWeighting().contains("apache")); + assertThat(dependencies.length, is(1)); + Dependency dependency = dependencies[0]; + assertTrue(dependency.getEvidence(EvidenceType.VENDOR).toString().toLowerCase().contains("apache")); + assertTrue(dependency.getVendorWeightings().contains("apache")); assertTrue(dependency.getVulnerabilities().isEmpty()); } - try (Engine engine = new Engine(Engine.Mode.EVIDENCE_PROCESSING)) { + try (Engine engine = new Engine(Engine.Mode.EVIDENCE_PROCESSING, getSettings())) { + engine.openDatabase(); assertDatabase(true); for (AnalysisPhase phase : Engine.Mode.EVIDENCE_PROCESSING.getPhases()) { assertThat(engine.getAnalyzers(phase), is(notNullValue())); @@ -84,16 +94,17 @@ public class EngineModeIT extends BaseTest { for (AnalysisPhase phase : Engine.Mode.EVIDENCE_COLLECTION.getPhases()) { assertThat(engine.getAnalyzers(phase), is(nullValue())); } - engine.setDependencies(dependencies); + engine.addDependency(dependencies[0]); engine.analyzeDependencies(); - Dependency dependency = dependencies.get(0); + Dependency dependency = dependencies[0]; assertFalse(dependency.getVulnerabilities().isEmpty()); } } @Test public void testStandaloneMode() throws Exception { - try (Engine engine = new Engine(Engine.Mode.STANDALONE)) { + try (Engine engine = new Engine(Engine.Mode.STANDALONE, getSettings())) { + engine.openDatabase(); assertDatabase(true); for (AnalysisPhase phase : Engine.Mode.STANDALONE.getPhases()) { assertThat(engine.getAnalyzers(phase), is(notNullValue())); @@ -101,26 +112,21 @@ public class EngineModeIT extends BaseTest { File file = BaseTest.getResourceAsFile(this, "struts2-core-2.1.2.jar"); engine.scan(file); engine.analyzeDependencies(); - List dependencies = engine.getDependencies(); - assertThat(dependencies.size(), is(1)); - Dependency dependency = dependencies.get(0); - assertTrue(dependency.getVendorEvidence().toString().toLowerCase().contains("apache")); - assertTrue(dependency.getVendorEvidence().getWeighting().contains("apache")); + Dependency[] dependencies = engine.getDependencies(); + assertThat(dependencies.length, is(1)); + Dependency dependency = dependencies[0]; + assertTrue(dependency.getEvidence(EvidenceType.VENDOR).toString().toLowerCase().contains("apache")); + assertTrue(dependency.getVendorWeightings().contains("apache")); assertFalse(dependency.getVulnerabilities().isEmpty()); } } private void assertDatabase(boolean exists) throws Exception { - Assume.assumeThat(Settings.getString(Settings.KEYS.DB_DRIVER_NAME), is("org.h2.Driver")); - Path directory = Settings.getDataDirectory().toPath(); + Assume.assumeThat(getSettings().getString(Settings.KEYS.DB_DRIVER_NAME), is("org.h2.Driver")); + Path directory = getSettings().getDataDirectory().toPath(); assertThat(Files.exists(directory), is(true)); assertThat(Files.isDirectory(directory), is(true)); - Path database = directory.resolve(Settings.getString(Settings.KEYS.DB_FILE_NAME)); - System.err.println(database.toString()); - for (String f : directory.toFile().list()) { - System.err.println(f); - } - + Path database = directory.resolve(getSettings().getString(Settings.KEYS.DB_FILE_NAME)); assertThat(Files.exists(database), is(exists)); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineTest.java index 189bd5ef7..4d098f65e 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineTest.java @@ -54,49 +54,49 @@ public class EngineTest extends BaseDBTestCase { */ @Test public void testScanFile() throws DatabaseException { - Engine instance = new Engine(); - instance.addFileTypeAnalyzer(new JarAnalyzer()); - File file = BaseTest.getResourceAsFile(this, "dwr.jar"); - Dependency dwr = instance.scanFile(file); - file = BaseTest.getResourceAsFile(this, "org.mortbay.jmx.jar"); - instance.scanFile(file); - assertEquals(2, instance.getDependencies().size()); + try (Engine instance = new Engine(getSettings())) { + instance.addFileTypeAnalyzer(new JarAnalyzer()); + File file = BaseTest.getResourceAsFile(this, "dwr.jar"); + Dependency dwr = instance.scanFile(file); + file = BaseTest.getResourceAsFile(this, "org.mortbay.jmx.jar"); + instance.scanFile(file); + assertEquals(2, instance.getDependencies().length); - file = BaseTest.getResourceAsFile(this, "dwr.jar"); - Dependency secondDwr = instance.scanFile(file); + file = BaseTest.getResourceAsFile(this, "dwr.jar"); + Dependency secondDwr = instance.scanFile(file); - assertEquals(2, instance.getDependencies().size()); - assertEquals(dwr, secondDwr); + assertEquals(2, instance.getDependencies().length); + assertEquals(dwr, secondDwr); + } } @Test(expected = ExceptionCollection.class) public void exceptionDuringAnalysisTaskExecutionIsFatal() throws DatabaseException, ExceptionCollection { - final ExecutorService executorService = Executors.newFixedThreadPool(3); - final Engine instance = new Engine(); - final List exceptions = new ArrayList<>(); - new Expectations() { - { - analysisTask.call(); - result = new IllegalStateException("Analysis task execution threw an exception"); - } - }; + try (Engine instance = new Engine(getSettings())) { + final ExecutorService executorService = Executors.newFixedThreadPool(3); + final List exceptions = new ArrayList<>(); - final List failingAnalysisTask = new ArrayList<>(); - failingAnalysisTask.add(analysisTask); + new Expectations() { + { + analysisTask.call(); + result = new IllegalStateException("Analysis task execution threw an exception"); + } + }; - new Expectations(instance) { - { - instance.getExecutorService(analyzer); - result = executorService; + final List failingAnalysisTask = new ArrayList<>(); + failingAnalysisTask.add(analysisTask); - instance.getAnalysisTasks(analyzer, exceptions); - result = failingAnalysisTask; - } - }; - - instance.executeAnalysisTasks(analyzer, exceptions); - - assertTrue(executorService.isShutdown()); + new Expectations(instance) { + { + instance.getExecutorService(analyzer); + result = executorService; + instance.getAnalysisTasks(analyzer, exceptions); + result = failingAnalysisTask; + } + }; + instance.executeAnalysisTasks(analyzer, exceptions); + assertTrue(executorService.isShutdown()); + } } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java index 5f21d1216..0bece2a4a 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AbstractSuppressionAnalyzerTest.java @@ -39,14 +39,18 @@ import org.owasp.dependencycheck.utils.Settings.KEYS; */ public class AbstractSuppressionAnalyzerTest extends BaseTest { - /** A second suppression file to test with. */ + /** + * A second suppression file to test with. + */ private static final String OTHER_SUPPRESSIONS_FILE = "other-suppressions.xml"; - /** Suppression file to test with. */ + /** + * Suppression file to test with. + */ private static final String SUPPRESSIONS_FILE = "suppressions.xml"; - + private AbstractSuppressionAnalyzer instance; - + @Before public void createObjectUnderTest() throws Exception { instance = new AbstractSuppressionAnalyzerImpl(); @@ -75,7 +79,7 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest { /** * Test of getRules method, of class AbstractSuppressionAnalyzer for - * suppression file on the classpath. + * suppression file on the class path. */ @Test public void testGetRulesFromSuppressionFileInClasspath() throws Exception { @@ -84,7 +88,8 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest { } /** - * Assert that rules are loaded from multiple files if multiple files are denfined in the {@link Settings} singleton. + * Assert that rules are loaded from multiple files if multiple files are + * defined in the {@link Settings}. */ @Test public void testGetRulesFromMultipleSuppressionFiles() throws Exception { @@ -97,71 +102,75 @@ public class AbstractSuppressionAnalyzerTest extends BaseTest { final int rulesInSecondFile = getNumberOfRulesLoadedFromPath(OTHER_SUPPRESSIONS_FILE) - rulesInCoreFile; // WHEN initializing with both suppression files - final String[] suppressionFiles = { SUPPRESSIONS_FILE, OTHER_SUPPRESSIONS_FILE }; - Settings.setArrayIfNotEmpty(KEYS.SUPPRESSION_FILE, suppressionFiles); - instance.initialize(); + final String[] suppressionFiles = {SUPPRESSIONS_FILE, OTHER_SUPPRESSIONS_FILE}; + getSettings().setArrayIfNotEmpty(KEYS.SUPPRESSION_FILE, suppressionFiles); + instance.initialize(getSettings()); + instance.prepare(null); // THEN rules from both files were loaded final int expectedSize = rulesInFirstFile + rulesInSecondFile + rulesInCoreFile; assertThat("Expected suppressions from both files", instance.getRuleCount(), is(expectedSize)); } - + @Test(expected = InitializationException.class) public void testFailureToLocateSuppressionFileAnywhere() throws Exception { - Settings.setString(Settings.KEYS.SUPPRESSION_FILE, "doesnotexist.xml"); - instance.initialize(); + getSettings().setString(Settings.KEYS.SUPPRESSION_FILE, "doesnotexist.xml"); + instance.initialize(getSettings()); + instance.prepare(null); } /** - * Return the number of rules that are loaded from the core suppression file. + * Return the number of rules that are loaded from the core suppression + * file. * - * @return the number of rules defined in the core suppresion file. + * @return the number of rules defined in the core suppression file. * @throws Exception if loading the rules fails. */ private int getNumberOfRulesLoadedInCoreFile() throws Exception { - Settings.removeProperty(KEYS.SUPPRESSION_FILE); - + getSettings().removeProperty(KEYS.SUPPRESSION_FILE); final AbstractSuppressionAnalyzerImpl coreFileAnalyzer = new AbstractSuppressionAnalyzerImpl(); - coreFileAnalyzer.initialize(); + coreFileAnalyzer.initialize(getSettings()); + coreFileAnalyzer.prepare(null); return coreFileAnalyzer.getRuleCount(); } /** - * Load a file into the {@link AbstractSuppressionAnalyzer} and return the number of rules loaded. + * Load a file into the {@link AbstractSuppressionAnalyzer} and return the + * number of rules loaded. * * @param path the path to load. * @return the number of rules that were loaded (including the core rules). * @throws Exception if loading the rules fails. */ private int getNumberOfRulesLoadedFromPath(final String path) throws Exception { - Settings.setString(KEYS.SUPPRESSION_FILE, path); - + getSettings().setString(KEYS.SUPPRESSION_FILE, path); final AbstractSuppressionAnalyzerImpl fileAnalyzer = new AbstractSuppressionAnalyzerImpl(); - fileAnalyzer.initialize(); + fileAnalyzer.initialize(getSettings()); + fileAnalyzer.prepare(null); return fileAnalyzer.getRuleCount(); } - + public class AbstractSuppressionAnalyzerImpl extends AbstractSuppressionAnalyzer { - + @Override public void analyzeDependency(Dependency dependency, Engine engine) throws AnalysisException { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } - + @Override public String getName() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } - + @Override public AnalysisPhase getAnalysisPhase() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } - + @Override protected String getAnalyzerEnabledSettingKey() { return "unknown"; } } - + } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AnalyzerServiceTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AnalyzerServiceTest.java index 34f83be44..7561eb0bb 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AnalyzerServiceTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AnalyzerServiceTest.java @@ -40,13 +40,14 @@ public class AnalyzerServiceTest extends BaseDBTestCase { */ @Test public void testGetAnalyzers() { - AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader()); + AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader(), false); List result = instance.getAnalyzers(); boolean found = false; for (Analyzer a : result) { if ("Jar Analyzer".equals(a.getName())) { found = true; + break; } } assertTrue("JarAnalyzer loaded", found); @@ -57,7 +58,7 @@ public class AnalyzerServiceTest extends BaseDBTestCase { */ @Test public void testGetAnalyzers_SpecificPhases() throws Exception { - AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader()); + AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader(), false); List result = instance.getAnalyzers(INITIAL, FINAL); for (Analyzer a : result) { @@ -72,8 +73,7 @@ public class AnalyzerServiceTest extends BaseDBTestCase { */ @Test public void testGetExperimentalAnalyzers() { - Settings.setBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, false); - AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader()); + AnalyzerService instance = new AnalyzerService(Thread.currentThread().getContextClassLoader(), false); List result = instance.getAnalyzers(); String experimental = "CMake Analyzer"; boolean found = false; @@ -83,8 +83,8 @@ public class AnalyzerServiceTest extends BaseDBTestCase { } } assertFalse("Experimental analyzer loaded when set to false", found); - - Settings.setBoolean(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, true); + + instance = new AnalyzerService(Thread.currentThread().getContextClassLoader(), true); result = instance.getAnalyzers(); found = false; for (Analyzer a : result) { diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIT.java index a80e845bd..a07e36669 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerIT.java @@ -41,6 +41,7 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testSupportsExtensions() { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); Set expResult = new HashSet<>(); expResult.add("zip"); expResult.add("war"); @@ -65,6 +66,7 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testGetName() { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); String expResult = "Archive Analyzer"; String result = instance.getName(); assertEquals(expResult, result); @@ -77,6 +79,7 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { public void testSupportsExtension() { String extension = "test.7z"; //not supported ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); assertFalse(extension, instance.accept(new File(extension))); } @@ -86,21 +89,23 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testGetAnalysisPhase() { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); AnalysisPhase expResult = AnalysisPhase.INITIAL; AnalysisPhase result = instance.getAnalysisPhase(); assertEquals(expResult, result); } /** - * Test of initialize and close methods, of class ArchiveAnalyzer. + * Test of prepare and close methods, of class ArchiveAnalyzer. */ @Test public void testInitialize() { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); try { instance.setEnabled(true); instance.setFilesMatched(true); - instance.initialize(); + instance.prepare(null); } catch (InitializationException ex) { fail(ex.getMessage()); } finally { @@ -120,25 +125,22 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyze() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); //trick the analyzer into thinking it is active. instance.accept(new File("test.ear")); - try { - instance.initialize(); + try (Engine engine = new Engine(getSettings())) { + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + + instance.prepare(engine); File file = BaseTest.getResourceAsFile(this, "daytrader-ear-2.1.7.ear"); Dependency dependency = new Dependency(file); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - int initial_size = engine.getDependencies().size(); + int initial_size = engine.getDependencies().length; instance.analyze(dependency, engine); - int ending_size = engine.getDependencies().size(); - - engine.cleanup(); - + int ending_size = engine.getDependencies().length; assertTrue(initial_size < ending_size); - } finally { instance.close(); } @@ -150,23 +152,20 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyzeExecutableJar() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); //trick the analyzer into thinking it is active. instance.accept(new File("test.ear")); - try { - instance.initialize(); + try (Engine engine = new Engine(getSettings())) { + instance.prepare(null); File file = BaseTest.getResourceAsFile(this, "bootable-0.1.0.jar"); Dependency dependency = new Dependency(file); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - - int initial_size = engine.getDependencies().size(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + + int initial_size = engine.getDependencies().length; instance.analyze(dependency, engine); - int ending_size = engine.getDependencies().size(); - - engine.cleanup(); - + int ending_size = engine.getDependencies().length; assertTrue(initial_size < ending_size); } finally { @@ -180,27 +179,24 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyzeTar() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); - //trick the analyzer into thinking it is active so that it will initialize + instance.initialize(getSettings()); + //trick the analyzer into thinking it is active so that it will prepare instance.accept(new File("test.tar")); - try { - instance.initialize(); + try (Engine engine = new Engine(getSettings())) { + instance.prepare(null); //File file = new File(this.getClass().getClassLoader().getResource("file.tar").getPath()); //File file = new File(this.getClass().getClassLoader().getResource("stagedhttp-modified.tar").getPath()); File file = BaseTest.getResourceAsFile(this, "stagedhttp-modified.tar"); Dependency dependency = new Dependency(file); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - - int initial_size = engine.getDependencies().size(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + + int initial_size = engine.getDependencies().length; instance.analyze(dependency, engine); - int ending_size = engine.getDependencies().size(); - engine.cleanup(); - + int ending_size = engine.getDependencies().length; assertTrue(initial_size < ending_size); - } finally { instance.close(); } @@ -212,24 +208,23 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyzeTarGz() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); instance.accept(new File("zip")); //ensure analyzer is "enabled" - try { - instance.initialize(); + try (Engine engine = new Engine(getSettings())) { + instance.prepare(null); //File file = new File(this.getClass().getClassLoader().getResource("file.tar.gz").getPath()); File file = BaseTest.getResourceAsFile(this, "file.tar.gz"); //Dependency dependency = new Dependency(file); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - - int initial_size = engine.getDependencies().size(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + + int initial_size = engine.getDependencies().length; //instance.analyze(dependency, engine); engine.scan(file); engine.analyzeDependencies(); - int ending_size = engine.getDependencies().size(); - engine.cleanup(); + int ending_size = engine.getDependencies().length; assertTrue(initial_size < ending_size); } finally { @@ -243,19 +238,18 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyzeTarBz2() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); instance.accept(new File("zip")); //ensure analyzer is "enabled" - try { - instance.initialize(); + try (Engine engine = new Engine(getSettings())){ + instance.prepare(null); File file = BaseTest.getResourceAsFile(this, "file.tar.bz2"); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - int initial_size = engine.getDependencies().size(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + int initial_size = engine.getDependencies().length; engine.scan(file); engine.analyzeDependencies(); - int ending_size = engine.getDependencies().size(); - engine.cleanup(); + int ending_size = engine.getDependencies().length; assertTrue(initial_size < ending_size); } finally { instance.close(); @@ -268,22 +262,20 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyzeTgz() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); instance.accept(new File("zip")); //ensure analyzer is "enabled" - try { - instance.initialize(); + try (Engine engine = new Engine(getSettings())) { + instance.prepare(null); //File file = new File(this.getClass().getClassLoader().getResource("file.tgz").getPath()); File file = BaseTest.getResourceAsFile(this, "file.tgz"); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - - int initial_size = engine.getDependencies().size(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + int initial_size = engine.getDependencies().length; engine.scan(file); engine.analyzeDependencies(); - int ending_size = engine.getDependencies().size(); - engine.cleanup(); + int ending_size = engine.getDependencies().length; assertTrue(initial_size < ending_size); } finally { @@ -297,19 +289,18 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyzeTbz2() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); instance.accept(new File("zip")); //ensure analyzer is "enabled" - try { - instance.initialize(); + try (Engine engine = new Engine(getSettings())) { + instance.prepare(null); File file = BaseTest.getResourceAsFile(this, "file.tbz2"); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - int initial_size = engine.getDependencies().size(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + int initial_size = engine.getDependencies().length; engine.scan(file); engine.analyzeDependencies(); - int ending_size = engine.getDependencies().size(); - engine.cleanup(); + int ending_size = engine.getDependencies().length; assertTrue(initial_size < ending_size); } finally { instance.close(); @@ -322,17 +313,17 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { @Test public void testAnalyze_badZip() throws Exception { ArchiveAnalyzer instance = new ArchiveAnalyzer(); - try { - instance.initialize(); + instance.initialize(getSettings()); + try (Engine engine = new Engine(getSettings())) { + instance.prepare(null); //File file = new File(this.getClass().getClassLoader().getResource("test.zip").getPath()); File file = BaseTest.getResourceAsFile(this, "test.zip"); Dependency dependency = new Dependency(file); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - int initial_size = engine.getDependencies().size(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + int initial_size = engine.getDependencies().length; // boolean failed = false; // try { instance.analyze(dependency, engine); @@ -340,8 +331,7 @@ public class ArchiveAnalyzerIT extends BaseDBTestCase { // failed = true; // } // assertTrue(failed); - int ending_size = engine.getDependencies().size(); - engine.cleanup(); + int ending_size = engine.getDependencies().length; assertEquals(initial_size, ending_size); } finally { instance.close(); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerTest.java index be418c069..6d8f9d0aa 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ArchiveAnalyzerTest.java @@ -36,8 +36,10 @@ import org.owasp.dependencycheck.utils.Settings; public class ArchiveAnalyzerTest extends BaseTest { @Before - public void setUp() { - Settings.setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, "z2, z3"); + @Override + public void setUp() throws Exception { + super.setUp(); + getSettings().setString(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, "z2, z3"); } /** @@ -47,6 +49,7 @@ public class ArchiveAnalyzerTest extends BaseTest { public void testZippableExtensions() throws Exception { assumeFalse(isPreviouslyLoaded("org.owasp.dependencycheck.analyzer.ArchiveAnalyzer")); ArchiveAnalyzer instance = new ArchiveAnalyzer(); + instance.initialize(getSettings()); assertTrue(instance.getFileFilter().accept(new File("c:/test.zip"))); assertTrue(instance.getFileFilter().accept(new File("c:/test.z2"))); assertTrue(instance.getFileFilter().accept(new File("c:/test.z3"))); @@ -55,19 +58,11 @@ public class ArchiveAnalyzerTest extends BaseTest { private boolean isPreviouslyLoaded(String className) { try { - Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[]{String.class}); + Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); m.setAccessible(true); Object t = m.invoke(Thread.currentThread().getContextClassLoader(), className); return t != null; - } catch (NoSuchMethodException ex) { - Logger.getLogger(ArchiveAnalyzerTest.class.getName()).log(Level.SEVERE, null, ex); - } catch (SecurityException ex) { - Logger.getLogger(ArchiveAnalyzerTest.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalAccessException ex) { - Logger.getLogger(ArchiveAnalyzerTest.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalArgumentException ex) { - Logger.getLogger(ArchiveAnalyzerTest.class.getName()).log(Level.SEVERE, null, ex); - } catch (InvocationTargetException ex) { + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { Logger.getLogger(ArchiveAnalyzerTest.class.getName()).log(Level.SEVERE, null, ex); } return false; diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java index 1ff39bc60..50cd36382 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AssemblyAnalyzerTest.java @@ -35,10 +35,12 @@ import static org.junit.Assume.assumeNotNull; import org.junit.Before; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; +import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; import org.owasp.dependencycheck.utils.FileUtils; import org.owasp.dependencycheck.utils.Settings; @@ -65,11 +67,14 @@ public class AssemblyAnalyzerTest extends BaseTest { * @throws Exception if anything goes sideways */ @Before + @Override public void setUp() throws Exception { + super.setUp(); try { analyzer = new AssemblyAnalyzer(); + analyzer.initialize(getSettings()); analyzer.accept(new File("test.dll")); // trick into "thinking it is active" - analyzer.initialize(); + analyzer.prepare(null); assertGrokAssembly(); } catch (Exception e) { if (e.getMessage().contains("Could not execute .NET AssemblyAnalyzer")) { @@ -86,8 +91,8 @@ public class AssemblyAnalyzerTest extends BaseTest { // directory and they must match the resources they were created from. File grokAssemblyExeFile = null; File grokAssemblyConfigFile = null; - - File tempDirectory = Settings.getTempDirectory(); + + File tempDirectory = getSettings().getTempDirectory(); for (File file : tempDirectory.listFiles()) { String filename = file.getName(); if (filename.startsWith("GKA") && filename.endsWith(".exe")) { @@ -99,10 +104,8 @@ public class AssemblyAnalyzerTest extends BaseTest { grokAssemblyConfigFile = new File(grokAssemblyExeFile.getPath() + ".config"); assertTrue("The GrokAssembly config was not created.", grokAssemblyConfigFile.isFile()); - assertFileContent("The GrokAssembly executable has incorrect content.", "GrokAssembly.exe", - grokAssemblyExeFile); - assertFileContent("The GrokAssembly config has incorrect content.", "GrokAssembly.exe.config", - grokAssemblyConfigFile); + assertFileContent("The GrokAssembly executable has incorrect content.", "GrokAssembly.exe", grokAssemblyExeFile); + assertFileContent("The GrokAssembly config has incorrect content.", "GrokAssembly.exe.config", grokAssemblyConfigFile); } private void assertFileContent(String message, String expectedResourceName, File actualFile) throws IOException { @@ -128,21 +131,8 @@ public class AssemblyAnalyzerTest extends BaseTest { File f = BaseTest.getResourceAsFile(this, "GrokAssembly.exe"); Dependency d = new Dependency(f); analyzer.analyze(d, null); - boolean foundVendor = false; - for (Evidence e : d.getVendorEvidence().getEvidence("grokassembly", "vendor")) { - if ("OWASP".equals(e.getValue())) { - foundVendor = true; - } - } - assertTrue(foundVendor); - - boolean foundProduct = false; - for (Evidence e : d.getProductEvidence().getEvidence("grokassembly", "product")) { - if ("GrokAssembly".equals(e.getValue())) { - foundProduct = true; - } - } - assertTrue(foundProduct); + assertTrue(d.contains(EvidenceType.VENDOR, new Evidence("grokassembly", "vendor", "OWASP", Confidence.HIGH))); + assertTrue(d.contains(EvidenceType.PRODUCT, new Evidence("grokassembly", "product", "GrokAssembly", Confidence.HIGH))); } @Test @@ -152,9 +142,9 @@ public class AssemblyAnalyzerTest extends BaseTest { Dependency d = new Dependency(f); analyzer.analyze(d, null); - assertTrue(d.getVersionEvidence().getEvidence().contains(new Evidence("grokassembly", "version", "1.2.13.0", Confidence.HIGHEST))); - assertTrue(d.getVendorEvidence().getEvidence().contains(new Evidence("grokassembly", "vendor", "The Apache Software Foundation", Confidence.HIGH))); - assertTrue(d.getProductEvidence().getEvidence().contains(new Evidence("grokassembly", "product", "log4net", Confidence.HIGH))); + assertTrue(d.contains(EvidenceType.VERSION, new Evidence("grokassembly", "version", "1.2.13.0", Confidence.HIGHEST))); + assertTrue(d.contains(EvidenceType.VENDOR, new Evidence("grokassembly", "vendor", "The Apache Software Foundation", Confidence.HIGH))); + assertTrue(d.contains(EvidenceType.PRODUCT, new Evidence("grokassembly", "product", "log4net", Confidence.HIGH))); } @Test @@ -183,7 +173,7 @@ public class AssemblyAnalyzerTest extends BaseTest { //This test doesn't work on Windows. assumeFalse(System.getProperty("os.name").startsWith("Windows")); - String oldValue = Settings.getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH); + String oldValue = getSettings().getString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH); // if oldValue is null, that means that neither the system property nor the setting has // been set. If that's the case, then we have to make it such that when we recover, // null still comes back. But you can't put a null value in a HashMap, so we have to set @@ -191,7 +181,7 @@ public class AssemblyAnalyzerTest extends BaseTest { if (oldValue == null) { System.setProperty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, "/yooser/bine/mono"); } else { - Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, "/yooser/bine/mono"); + getSettings().setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, "/yooser/bine/mono"); } String oldProp = System.getProperty(LOG_KEY, "info"); @@ -200,8 +190,9 @@ public class AssemblyAnalyzerTest extends BaseTest { System.setProperty(LOG_KEY, "error"); // Have to make a NEW analyzer because during setUp, it would have gotten the correct one AssemblyAnalyzer aanalyzer = new AssemblyAnalyzer(); + aanalyzer.initialize(getSettings()); aanalyzer.accept(new File("test.dll")); // trick into "thinking it is active" - aanalyzer.initialize(); + aanalyzer.prepare(null); fail("Expected an InitializationException"); } catch (InitializationException ae) { assertEquals("An error occurred with the .NET AssemblyAnalyzer", ae.getMessage()); @@ -213,13 +204,20 @@ public class AssemblyAnalyzerTest extends BaseTest { if (oldValue == null) { System.getProperties().remove(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH); } else { - Settings.setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, oldValue); + getSettings().setString(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, oldValue); } } } @After + @Override public void tearDown() throws Exception { - analyzer.closeAnalyzer(); + try { + analyzer.closeAnalyzer(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } finally { + super.tearDown(); + } } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzerTest.java index e8108ffef..3e0cb0899 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/AutoconfAnalyzerTest.java @@ -28,13 +28,18 @@ import java.io.File; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.owasp.dependencycheck.dependency.Confidence; +import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; /** - * Unit tests for AutoconfAnalyzer. The test resources under autoconf/ were obtained from outside open source software projects. - * Links to those projects are given below. + * Unit tests for AutoconfAnalyzer. The test resources under autoconf/ were + * obtained from outside open source software projects. Links to those projects + * are given below. * * @author Dale Visser - * @see Readable Lisp S-expressions Project + * @see Readable Lisp S-expressions + * Project * @see GNU Binutils * @see GNU Ghostscript */ @@ -45,31 +50,19 @@ public class AutoconfAnalyzerTest extends BaseTest { */ private AutoconfAnalyzer analyzer; - private void assertCommonEvidence(Dependency result, String product, - String version, String vendor) { - assertProductAndVersion(result, product, version); - assertTrue("Expected vendor evidence to contain \"" + vendor + "\".", - result.getVendorEvidence().toString().contains(vendor)); - } - - private void assertProductAndVersion(Dependency result, String product, - String version) { - assertTrue("Expected product evidence to contain \"" + product + "\".", - result.getProductEvidence().toString().contains(product)); - assertTrue("Expected version evidence to contain \"" + version + "\".", - result.getVersionEvidence().toString().contains(version)); - } - /** * Correctly setup the analyzer for testing. * * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new AutoconfAnalyzer(); + analyzer.initialize(getSettings()); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.prepare(null); } /** @@ -78,13 +71,15 @@ public class AutoconfAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); analyzer = null; + super.tearDown(); } /** - * Test whether expected evidence is gathered from Ghostscript's configure.ac. + * Test whether expected evidence is gathered from Ghostscript's configure. * * @throws AnalysisException is thrown when an exception occurs. */ @@ -93,7 +88,10 @@ public class AutoconfAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile( this, "autoconf/ghostscript/configure.ac")); analyzer.analyze(result, null); - assertCommonEvidence(result, "ghostscript", "8.62.0", "gnu"); + //TODO fix these + assertTrue(result.contains(EvidenceType.VENDOR, new Evidence("configure.ac", "Bug report address", "gnu-ghostscript-bug@gnu.org", Confidence.HIGH))); + assertTrue(result.contains(EvidenceType.PRODUCT, new Evidence("configure.ac", "Package", "gnu-ghostscript", Confidence.HIGHEST))); + assertTrue(result.contains(EvidenceType.VERSION, new Evidence("configure.ac", "Package Version", "8.62.0", Confidence.HIGHEST))); } /** @@ -106,14 +104,11 @@ public class AutoconfAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile( this, "autoconf/readable-code/configure.ac")); analyzer.analyze(result, null); - assertReadableCodeEvidence(result); - } - private void assertReadableCodeEvidence(final Dependency result) { - assertCommonEvidence(result, "readable", "1.0.7", "dwheeler"); - final String url = "http://readable.sourceforge.net/"; - assertTrue("Expected product evidence to contain \"" + url + "\".", - result.getVendorEvidence().toString().contains(url)); + assertTrue(result.contains(EvidenceType.VENDOR, new Evidence("configure.ac", "Bug report address", "dwheeler@dwheeler.com", Confidence.HIGH))); + assertTrue(result.contains(EvidenceType.PRODUCT, new Evidence("configure.ac", "Package", "readable", Confidence.HIGHEST))); + assertTrue(result.contains(EvidenceType.VERSION, new Evidence("configure.ac", "Package Version", "1.0.7", Confidence.HIGHEST))); + assertTrue(result.contains(EvidenceType.VENDOR, new Evidence("configure.ac", "URL", "http://readable.sourceforge.net/", Confidence.HIGH))); } /** @@ -126,11 +121,14 @@ public class AutoconfAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile( this, "autoconf/binutils/configure")); analyzer.analyze(result, null); - assertProductAndVersion(result, "binutils", "2.25.51"); + + assertTrue(result.contains(EvidenceType.PRODUCT, new Evidence("configure", "NAME", "binutils", Confidence.HIGHEST))); + assertTrue(result.contains(EvidenceType.VERSION, new Evidence("configure", "VERSION", "2.25.51", Confidence.HIGHEST))); } /** - * Test whether expected evidence is gathered from GNU Ghostscript's configure. + * Test whether expected evidence is gathered from GNU Ghostscript's + * configure. * * @throws AnalysisException is thrown when an exception occurs. */ @@ -139,7 +137,11 @@ public class AutoconfAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile( this, "autoconf/readable-code/configure")); analyzer.analyze(result, null); - assertReadableCodeEvidence(result); + + assertTrue(result.contains(EvidenceType.VENDOR, new Evidence("configure", "BUGREPORT", "dwheeler@dwheeler.com", Confidence.HIGH))); + assertTrue(result.contains(EvidenceType.PRODUCT, new Evidence("configure", "NAME", "readable", Confidence.HIGHEST))); + assertTrue(result.contains(EvidenceType.VERSION, new Evidence("configure", "VERSION", "1.0.7", Confidence.HIGHEST))); + assertTrue(result.contains(EvidenceType.VENDOR, new Evidence("configure", "URL", "http://readable.sourceforge.net/", Confidence.HIGH))); } /** diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzerTest.java index 6408d4a86..212a3efa0 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CMakeAnalyzerTest.java @@ -42,6 +42,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for CmakeAnalyzer. @@ -61,21 +63,29 @@ public class CMakeAnalyzerTest extends BaseDBTestCase { * @throws Exception if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new CMakeAnalyzer(); + analyzer.initialize(getSettings()); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.prepare(null); } /** * Cleanup any resources used. * - * @throws Exception if there is a problem */ @After + @Override public void tearDown() throws Exception { - analyzer.close(); - analyzer = null; + try { + analyzer.close(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } finally { + super.tearDown(); + } } /** @@ -126,35 +136,50 @@ public class CMakeAnalyzerTest extends BaseDBTestCase { } private void assertProductEvidence(Dependency result, String product) { - assertTrue("Expected product evidence to contain \"" + product + "\".", - result.getProductEvidence().toString().contains(product)); + boolean found = false; + for (Evidence e : result.getEvidence(EvidenceType.PRODUCT)) { + if (product.equals(e.getValue())) { + found = true; + break; + } + } + assertTrue("Expected product evidence to contain \"" + product + "\".", found); } /** - * Test whether expected version evidence is gathered from OpenCV's third party cmake files. + * Test whether expected version evidence is gathered from OpenCV's third + * party cmake files. * * @throws AnalysisException is thrown when an exception occurs. */ @Test public void testAnalyzeCMakeListsOpenCV3rdParty() throws AnalysisException, DatabaseException { - final Dependency result = new Dependency(BaseTest.getResourceAsFile( - this, "cmake/opencv/3rdparty/ffmpeg/ffmpeg_version.cmake")); - final Engine engine = new Engine(); - analyzer.analyze(result, engine); - assertProductEvidence(result, "libavcodec"); - assertVersionEvidence(result, "55.18.102"); - assertFalse("ALIASOF_ prefix shouldn't be present.", - Pattern.compile("\\bALIASOF_\\w+").matcher(result.getProductEvidence().toString()).find()); - final List dependencies = engine.getDependencies(); - assertEquals("Number of additional dependencies should be 4.", 4, dependencies.size()); - final Dependency last = dependencies.get(3); - assertProductEvidence(last, "libavresample"); - assertVersionEvidence(last, "1.0.1"); + try (Engine engine = new Engine(getSettings())) { + final Dependency result = new Dependency(BaseTest.getResourceAsFile( + this, "cmake/opencv/3rdparty/ffmpeg/ffmpeg_version.cmake")); + + analyzer.analyze(result, engine); + assertProductEvidence(result, "libavcodec"); + assertVersionEvidence(result, "55.18.102"); + assertFalse("ALIASOF_ prefix shouldn't be present.", + Pattern.compile("\\bALIASOF_\\w+").matcher(result.getEvidence(EvidenceType.PRODUCT).toString()).find()); + final Dependency[] dependencies = engine.getDependencies(); + assertEquals("Number of additional dependencies should be 4.", 4, dependencies.length); + final Dependency last = dependencies[3]; + assertProductEvidence(last, "libavresample"); + assertVersionEvidence(last, "1.0.1"); + } } private void assertVersionEvidence(Dependency result, String version) { - assertTrue("Expected version evidence to contain \"" + version + "\".", - result.getVersionEvidence().toString().contains(version)); + boolean found = false; + for (Evidence e : result.getEvidence(EvidenceType.VERSION)) { + if (version.equals(e.getValue())) { + found = true; + break; + } + } + assertTrue("Expected version evidence to contain \"" + version + "\".", found); } @Test(expected = InitializationException.class) @@ -169,7 +194,8 @@ public class CMakeAnalyzerTest extends BaseDBTestCase { analyzer = new CMakeAnalyzer(); analyzer.setFilesMatched(true); assertTrue(analyzer.isEnabled()); - analyzer.initialize(); + analyzer.initialize(getSettings()); + analyzer.prepare(null); assertFalse(analyzer.isEnabled()); } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIT.java index d0eee79c7..ebc8c84cc 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/CPEAnalyzerIT.java @@ -24,8 +24,6 @@ import java.util.List; import java.util.Set; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.queryparser.classic.ParseException; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; import org.owasp.dependencycheck.BaseDBTestCase; @@ -36,6 +34,7 @@ import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Identifier; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * @@ -46,21 +45,19 @@ public class CPEAnalyzerIT extends BaseDBTestCase { /** * Tests of buildSearch of class CPEAnalyzer. * - * @throws IOException is thrown when an IO Exception occurs. - * @throws CorruptIndexException is thrown when the index is corrupt. - * @throws ParseException is thrown when a parse exception occurs + * @throws Exception is thrown when an IO Exception occurs. */ @Test - public void testBuildSearch() throws IOException, CorruptIndexException, ParseException { + public void testBuildSearch() throws Exception { Set productWeightings = Collections.singleton("struts2"); Set vendorWeightings = Collections.singleton("apache"); String vendor = "apache software foundation"; String product = "struts 2 core"; - - CPEAnalyzer instance = new CPEAnalyzer(); + CPEAnalyzer instance = new CPEAnalyzer(); + instance.initialize(getSettings()); String queryText = instance.buildSearch(vendor, product, null, null); String expResult = " product:( struts 2 core ) AND vendor:( apache software foundation ) "; assertTrue(expResult.equals(queryText)); @@ -76,6 +73,7 @@ public class CPEAnalyzerIT extends BaseDBTestCase { queryText = instance.buildSearch(vendor, product, vendorWeightings, productWeightings); expResult = " product:( struts^5 struts2^5 2 core ) AND vendor:( apache^5 software foundation ) "; assertTrue(expResult.equals(queryText)); + instance.close(); } /** @@ -85,31 +83,33 @@ public class CPEAnalyzerIT extends BaseDBTestCase { */ @Test public void testDetermineCPE_full() throws Exception { - //update needs to be performed so that xtream can be tested - Engine e = new Engine(); - e.doUpdates(); - CPEAnalyzer cpeAnalyzer = new CPEAnalyzer(); - try { - cpeAnalyzer.initialize(); + try (Engine e = new Engine(getSettings())) { + //update needs to be performed so that xtream can be tested + e.doUpdates(true); + cpeAnalyzer.initialize(getSettings()); + cpeAnalyzer.prepare(e); FileNameAnalyzer fnAnalyzer = new FileNameAnalyzer(); - fnAnalyzer.initialize(); + fnAnalyzer.initialize(getSettings()); + fnAnalyzer.prepare(e); JarAnalyzer jarAnalyzer = new JarAnalyzer(); + jarAnalyzer.initialize(getSettings()); jarAnalyzer.accept(new File("test.jar"));//trick analyzer into "thinking it is active" - jarAnalyzer.initialize(); + jarAnalyzer.prepare(e); HintAnalyzer hAnalyzer = new HintAnalyzer(); - hAnalyzer.initialize(); + hAnalyzer.initialize(getSettings()); + hAnalyzer.prepare(e); FalsePositiveAnalyzer fp = new FalsePositiveAnalyzer(); - fp.initialize(); + fp.initialize(getSettings()); + fp.prepare(e); callDetermineCPE_full("hazelcast-2.5.jar", null, cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); callDetermineCPE_full("spring-context-support-2.5.5.jar", "cpe:/a:springsource:spring_framework:2.5.5", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); callDetermineCPE_full("spring-core-3.0.0.RELEASE.jar", "cpe:/a:vmware:springsource_spring_framework:3.0.0", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); - callDetermineCPE_full("org.mortbay.jetty.jar", "cpe:/a:mortbay_jetty:jetty:4.2.27", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); callDetermineCPE_full("jaxb-xercesImpl-1.5.jar", null, cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); callDetermineCPE_full("ehcache-core-2.2.0.jar", null, cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); + callDetermineCPE_full("org.mortbay.jetty.jar", "cpe:/a:mortbay_jetty:jetty:4.2.27", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); callDetermineCPE_full("xstream-1.4.8.jar", "cpe:/a:x-stream:xstream:1.4.8", cpeAnalyzer, fnAnalyzer, jarAnalyzer, hAnalyzer, fp); - } finally { cpeAnalyzer.close(); } @@ -120,7 +120,8 @@ public class CPEAnalyzerIT extends BaseDBTestCase { * * @throws Exception is thrown when an exception occurs */ - public void callDetermineCPE_full(String depName, String expResult, CPEAnalyzer cpeAnalyzer, FileNameAnalyzer fnAnalyzer, JarAnalyzer jarAnalyzer, HintAnalyzer hAnalyzer, FalsePositiveAnalyzer fp) throws Exception { + public void callDetermineCPE_full(String depName, String expResult, CPEAnalyzer cpeAnalyzer, FileNameAnalyzer fnAnalyzer, + JarAnalyzer jarAnalyzer, HintAnalyzer hAnalyzer, FalsePositiveAnalyzer fp) throws Exception { //File file = new File(this.getClass().getClassLoader().getResource(depName).getPath()); File file = BaseTest.getResourceAsFile(this, depName); @@ -134,8 +135,14 @@ public class CPEAnalyzerIT extends BaseDBTestCase { fp.analyze(dep, null); if (expResult != null) { - Identifier expIdentifier = new Identifier("cpe", expResult, expResult); - assertTrue("Incorrect match: { dep:'" + dep.getFileName() + "' }", dep.getIdentifiers().contains(expIdentifier)); + boolean found = false; + for (Identifier i : dep.getIdentifiers()) { + if (expResult.equals(i.getValue())) { + found = true; + break; + } + } + assertTrue("Incorrect match: { dep:'" + dep.getFileName() + "' }", found); } else { for (Identifier i : dep.getIdentifiers()) { assertFalse(String.format("%s - found a CPE identifier when should have been none (found '%s')", dep.getFileName(), i.getValue()), "cpe".equals(i.getType())); @@ -159,10 +166,12 @@ public class CPEAnalyzerIT extends BaseDBTestCase { fnAnalyzer.analyze(struts, null); HintAnalyzer hintAnalyzer = new HintAnalyzer(); - hintAnalyzer.initialize(); + hintAnalyzer.initialize(getSettings()); + hintAnalyzer.prepare(null); JarAnalyzer jarAnalyzer = new JarAnalyzer(); + jarAnalyzer.initialize(getSettings()); jarAnalyzer.accept(new File("test.jar"));//trick analyzer into "thinking it is active" - jarAnalyzer.initialize(); + jarAnalyzer.prepare(null); jarAnalyzer.analyze(struts, null); hintAnalyzer.analyze(struts, null); @@ -185,25 +194,35 @@ public class CPEAnalyzerIT extends BaseDBTestCase { hintAnalyzer.analyze(spring3, null); CPEAnalyzer instance = new CPEAnalyzer(); - instance.open(); - instance.determineCPE(commonValidator); - instance.determineCPE(struts); - instance.determineCPE(spring); - instance.determineCPE(spring3); - instance.close(); + try (Engine engine = new Engine(getSettings())) { + engine.openDatabase(true, true); + instance.initialize(getSettings()); + instance.prepare(engine); + instance.determineCPE(commonValidator); + instance.determineCPE(struts); + instance.determineCPE(spring); + instance.determineCPE(spring3); + instance.close(); - String expResult = "cpe:/a:apache:struts:2.1.2"; - Identifier expIdentifier = new Identifier("cpe", expResult, expResult); + String expResult = "cpe:/a:apache:struts:2.1.2"; - for (Identifier i : commonValidator.getIdentifiers()) { - assertFalse("Apache Common Validator - found a CPE identifier?", "cpe".equals(i.getType())); + for (Identifier i : commonValidator.getIdentifiers()) { + assertFalse("Apache Common Validator - found a CPE identifier?", "cpe".equals(i.getType())); + } + + assertTrue("Incorrect match size - struts", struts.getIdentifiers().size() >= 1); + boolean found = false; + for (Identifier i : struts.getIdentifiers()) { + if (expResult.equals(i.getValue())) { + found = true; + break; + } + } + assertTrue("Incorrect match - struts", found); + assertTrue("Incorrect match size - spring3 - " + spring3.getIdentifiers().size(), spring3.getIdentifiers().size() >= 1); + + jarAnalyzer.close(); } - - assertTrue("Incorrect match size - struts", struts.getIdentifiers().size() >= 1); - assertTrue("Incorrect match - struts", struts.getIdentifiers().contains(expIdentifier)); - assertTrue("Incorrect match size - spring3 - " + spring3.getIdentifiers().size(), spring3.getIdentifiers().size() >= 1); - - jarAnalyzer.close(); } /** @@ -214,20 +233,29 @@ public class CPEAnalyzerIT extends BaseDBTestCase { @Test public void testDetermineIdentifiers() throws Exception { Dependency openssl = new Dependency(); - openssl.getVendorEvidence().addEvidence("test", "vendor", "openssl", Confidence.HIGHEST); - openssl.getProductEvidence().addEvidence("test", "product", "openssl", Confidence.HIGHEST); - openssl.getVersionEvidence().addEvidence("test", "version", "1.0.1c", Confidence.HIGHEST); + openssl.addEvidence(EvidenceType.VENDOR, "test", "vendor", "openssl", Confidence.HIGHEST); + openssl.addEvidence(EvidenceType.PRODUCT, "test", "product", "openssl", Confidence.HIGHEST); + openssl.addEvidence(EvidenceType.VERSION, "test", "version", "1.0.1c", Confidence.HIGHEST); CPEAnalyzer instance = new CPEAnalyzer(); - instance.open(); - instance.determineIdentifiers(openssl, "openssl", "openssl", Confidence.HIGHEST); - instance.close(); + try (Engine engine = new Engine(getSettings())) { + engine.openDatabase(true, true); + instance.initialize(getSettings()); + instance.prepare(engine); + instance.determineIdentifiers(openssl, "openssl", "openssl", Confidence.HIGHEST); + instance.close(); + } String expResult = "cpe:/a:openssl:openssl:1.0.1c"; Identifier expIdentifier = new Identifier("cpe", expResult, expResult); - - assertTrue(openssl.getIdentifiers().contains(expIdentifier)); - + boolean found = false; + for (Identifier i : openssl.getIdentifiers()) { + if (expResult.equals(i.getValue())) { + found = true; + break; + } + } + assertTrue("OpenSSL identifier not found", found); } /** @@ -243,20 +271,24 @@ public class CPEAnalyzerIT extends BaseDBTestCase { String expProduct = "struts"; CPEAnalyzer instance = new CPEAnalyzer(); - instance.open(); + try (Engine engine = new Engine(getSettings())) { + engine.openDatabase(true, true); + instance.initialize(getSettings()); + instance.prepare(engine); - Set productWeightings = Collections.singleton("struts2"); - Set vendorWeightings = Collections.singleton("apache"); - List result = instance.searchCPE(vendor, product, vendorWeightings, productWeightings); - instance.close(); + Set productWeightings = Collections.singleton("struts2"); + Set vendorWeightings = Collections.singleton("apache"); + List result = instance.searchCPE(vendor, product, vendorWeightings, productWeightings); - boolean found = false; - for (IndexEntry entry : result) { - if (expVendor.equals(entry.getVendor()) && expProduct.equals(entry.getProduct())) { - found = true; - break; + boolean found = false; + for (IndexEntry entry : result) { + if (expVendor.equals(entry.getVendor()) && expProduct.equals(entry.getProduct())) { + found = true; + break; + } } + assertTrue("apache:struts was not identified", found); } - assertTrue("apache:struts was not identified", found); + instance.close(); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java index 30c72b25a..b0836e424 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/ComposerLockAnalyzerTest.java @@ -55,10 +55,13 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase { * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new ComposerLockAnalyzer(); + analyzer.initialize(getSettings()); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.prepare(null); } /** @@ -67,9 +70,10 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } /** @@ -95,26 +99,27 @@ public class ComposerLockAnalyzerTest extends BaseDBTestCase { */ @Test public void testAnalyzePackageJson() throws Exception { - final Engine engine = new Engine(); - final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, - "composer.lock")); - analyzer.analyze(result, engine); + try (Engine engine = new Engine(getSettings())) { + final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, + "composer.lock")); + analyzer.analyze(result, engine); + } } - @Test(expected = InitializationException.class) public void analyzerIsDisabledInCaseOfMissingMessageDigest() throws InitializationException { new MockUp() { @Mock MessageDigest getInstance(String ignore) throws NoSuchAlgorithmException { - throw new NoSuchAlgorithmException(); + throw new NoSuchAlgorithmException("SHA1 is missing"); } }; analyzer = new ComposerLockAnalyzer(); analyzer.setFilesMatched(true); + analyzer.initialize(getSettings()); assertTrue(analyzer.isEnabled()); - analyzer.initialize(); + analyzer.prepare(null); assertFalse(analyzer.isEnabled()); } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java index 9125f6d28..ee6c5ddfe 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/DependencyBundlingAnalyzerTest.java @@ -79,7 +79,7 @@ public class DependencyBundlingAnalyzerTest extends BaseTest { new Verifications() {{ engineMock.getDependencies(); - times = 2; + times = 1; }}; } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzerTest.java index d1b068da8..cb3153c3f 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FalsePositiveAnalyzerTest.java @@ -20,7 +20,10 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; import org.owasp.dependencycheck.Engine; +import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceType; +import org.owasp.dependencycheck.utils.Settings; /** * @@ -51,10 +54,22 @@ public class FalsePositiveAnalyzerTest extends BaseTest { } /** - * Test of analyze method, of class FalsePositiveAnalyzer. + * Test of getAnalyzerEnabledSettingKey method, of class + * FalsePositiveAnalyzer. */ @Test - public void testAnalyze() throws Exception { + public void testGetAnalyzerEnabledSettingKey() { + FalsePositiveAnalyzer instance = new FalsePositiveAnalyzer(); + String expResult = Settings.KEYS.ANALYZER_FALSE_POSITIVE_ENABLED; + String result = instance.getAnalyzerEnabledSettingKey(); + assertEquals(expResult, result); + } + + /** + * Test of analyzeDependency method, of class FalsePositiveAnalyzer. + */ + @Test + public void testAnalyzeDependency() throws Exception { Dependency dependency = new Dependency(); dependency.setFileName("pom.xml"); dependency.setFilePath("pom.xml"); @@ -67,4 +82,27 @@ public class FalsePositiveAnalyzerTest extends BaseTest { assertTrue(before > after); } + /** + * Test of removeBadMatches method, of class FalsePositiveAnalyzer. + */ + @Test + public void testRemoveBadMatches() { + Dependency dependency = new Dependency(); + dependency.setFileName("some.jar"); + dependency.setFilePath("some.jar"); + dependency.addIdentifier("cpe", "cpe:/a:m-core:m-core", ""); + + assertEquals(1, dependency.getIdentifiers().size()); + + FalsePositiveAnalyzer instance = new FalsePositiveAnalyzer(); + instance.removeBadMatches(dependency); + + assertEquals(0, dependency.getIdentifiers().size()); + dependency.addIdentifier("cpe", "cpe:/a:m-core:m-core", ""); + dependency.addEvidence(EvidenceType.PRODUCT,"test", "name", "m-core", Confidence.HIGHEST); + + instance.removeBadMatches(dependency); + assertEquals(1, dependency.getIdentifiers().size()); + } + } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzerTest.java index ff693fe5f..33bde854b 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/FileNameAnalyzerTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.fail; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; import org.owasp.dependencycheck.dependency.Dependency; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.exception.InitializationException; /** @@ -67,21 +68,22 @@ public class FileNameAnalyzerTest extends BaseTest { Dependency resultAxis = new Dependency(axis); FileNameAnalyzer instance = new FileNameAnalyzer(); instance.analyze(resultStruts, null); - assertTrue(resultStruts.getVendorEvidence().toString().toLowerCase().contains("struts")); + assertTrue(resultStruts.getEvidence(EvidenceType.VENDOR).toString().toLowerCase().contains("struts")); instance.analyze(resultAxis, null); - assertTrue(resultStruts.getVersionEvidence().toString().toLowerCase().contains("2.1.2")); + assertTrue(resultStruts.getEvidence(EvidenceType.VERSION).toString().toLowerCase().contains("2.1.2")); } /** - * Test of initialize method, of class FileNameAnalyzer. + * Test of prepare method, of class FileNameAnalyzer. */ @Test public void testInitialize() { FileNameAnalyzer instance = new FileNameAnalyzer(); try { - instance.initialize(); + instance.initialize(getSettings()); + instance.prepare(null); } catch (InitializationException ex) { fail(ex.getMessage()); } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java index 3ee58f893..e80fb49b5 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/HintAnalyzerTest.java @@ -28,6 +28,7 @@ import org.owasp.dependencycheck.BaseDBTestCase; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.dependency.Evidence; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.Settings; /** @@ -65,73 +66,73 @@ public class HintAnalyzerTest extends BaseDBTestCase { public void testAnalyze() throws Exception { //File guice = new File(this.getClass().getClassLoader().getResource("guice-3.0.jar").getPath()); File guice = BaseTest.getResourceAsFile(this, "guice-3.0.jar"); - //Dependency guice = new Dependency(fileg); + //Dependency guice = new EngineDependency(fileg); //File spring = new File(this.getClass().getClassLoader().getResource("spring-core-3.0.0.RELEASE.jar").getPath()); File spring = BaseTest.getResourceAsFile(this, "spring-core-3.0.0.RELEASE.jar"); //Dependency spring = new Dependency(files); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + try (Engine engine = new Engine(getSettings())) { - engine.scan(guice); - engine.scan(spring); - engine.analyzeDependencies(); - Dependency gdep = null; - Dependency sdep = null; - for (Dependency d : engine.getDependencies()) { - if (d.getActualFile().equals(guice)) { - gdep = d; - } else if (d.getActualFile().equals(spring)) { - sdep = d; + engine.scan(guice); + engine.scan(spring); + engine.analyzeDependencies(); + Dependency gdep = null; + Dependency sdep = null; + for (Dependency d : engine.getDependencies()) { + if (d.getActualFile().equals(guice)) { + gdep = d; + } else if (d.getActualFile().equals(spring)) { + sdep = d; + } } + final Evidence springTest1 = new Evidence("hint analyzer", "product", "springsource_spring_framework", Confidence.HIGH); + final Evidence springTest2 = new Evidence("hint analyzer", "vendor", "SpringSource", Confidence.HIGH); + final Evidence springTest3 = new Evidence("hint analyzer", "vendor", "vmware", Confidence.HIGH); + final Evidence springTest4 = new Evidence("hint analyzer", "product", "springsource_spring_framework", Confidence.HIGH); + final Evidence springTest5 = new Evidence("hint analyzer", "vendor", "vmware", Confidence.HIGH); + + assertFalse(gdep.contains(EvidenceType.PRODUCT, springTest1)); + assertFalse(gdep.contains(EvidenceType.VENDOR, springTest2)); + assertFalse(gdep.contains(EvidenceType.VENDOR, springTest3)); + assertFalse(gdep.contains(EvidenceType.PRODUCT, springTest4)); + assertFalse(gdep.contains(EvidenceType.VENDOR, springTest5)); + + assertTrue(sdep.contains(EvidenceType.PRODUCT, springTest1)); + assertTrue(sdep.contains(EvidenceType.VENDOR, springTest2)); + assertTrue(sdep.contains(EvidenceType.VENDOR, springTest3)); + //assertTrue(evidence.contains(springTest4)); + //assertTrue(evidence.contains(springTest5)); } - final Evidence springTest1 = new Evidence("hint analyzer", "product", "springsource_spring_framework", Confidence.HIGH); - final Evidence springTest2 = new Evidence("hint analyzer", "vendor", "SpringSource", Confidence.HIGH); - final Evidence springTest3 = new Evidence("hint analyzer", "vendor", "vmware", Confidence.HIGH); - final Evidence springTest4 = new Evidence("hint analyzer", "product", "springsource_spring_framework", Confidence.HIGH); - final Evidence springTest5 = new Evidence("hint analyzer", "vendor", "vmware", Confidence.HIGH); - - Set evidence = gdep.getEvidence().getEvidence(); - assertFalse(evidence.contains(springTest1)); - assertFalse(evidence.contains(springTest2)); - assertFalse(evidence.contains(springTest3)); - assertFalse(evidence.contains(springTest4)); - assertFalse(evidence.contains(springTest5)); - - evidence = sdep.getEvidence().getEvidence(); - assertTrue(evidence.contains(springTest1)); - assertTrue(evidence.contains(springTest2)); - assertTrue(evidence.contains(springTest3)); - //assertTrue(evidence.contains(springTest4)); - //assertTrue(evidence.contains(springTest5)); } + /** * Test of analyze method, of class HintAnalyzer. */ @Test public void testAnalyze_1() throws Exception { File path = BaseTest.getResourceAsFile(this, "hints_12.xml"); - Settings.setString(Settings.KEYS.HINTS_FILE, path.getPath()); + getSettings().setString(Settings.KEYS.HINTS_FILE, path.getPath()); HintAnalyzer instance = new HintAnalyzer(); - instance.initialize(); + instance.initialize(getSettings()); + instance.prepare(null); Dependency d = new Dependency(); - d.getVersionEvidence().addEvidence("version source", "given version name", "1.2.3", Confidence.HIGH); - d.getVersionEvidence().addEvidence("hint analyzer", "remove version name", "value", Confidence.HIGH); - d.getVendorEvidence().addEvidence("hint analyzer", "remove vendor name", "vendor", Confidence.HIGH); - d.getProductEvidence().addEvidence("hint analyzer", "remove product name", "product", Confidence.HIGH); - d.getVersionEvidence().addEvidence("hint analyzer", "other version name", "value", Confidence.HIGH); - d.getVendorEvidence().addEvidence("hint analyzer", "other vendor name", "vendor", Confidence.HIGH); - d.getProductEvidence().addEvidence("hint analyzer", "other product name", "product", Confidence.HIGH); - - assertEquals("vendor evidence mismatch",2, d.getVendorEvidence().size()); - assertEquals("product evidence mismatch",2, d.getProductEvidence().size()); - assertEquals("version evidence mismatch",3, d.getVersionEvidence().size()); + d.addEvidence(EvidenceType.VERSION, "version source", "given version name", "1.2.3", Confidence.HIGH); + d.addEvidence(EvidenceType.VERSION, "hint analyzer", "remove version name", "value", Confidence.HIGH); + d.addEvidence(EvidenceType.VENDOR, "hint analyzer", "remove vendor name", "vendor", Confidence.HIGH); + d.addEvidence(EvidenceType.PRODUCT, "hint analyzer", "remove product name", "product", Confidence.HIGH); + d.addEvidence(EvidenceType.VERSION, "hint analyzer", "other version name", "value", Confidence.HIGH); + d.addEvidence(EvidenceType.VENDOR, "hint analyzer", "other vendor name", "vendor", Confidence.HIGH); + d.addEvidence(EvidenceType.PRODUCT, "hint analyzer", "other product name", "product", Confidence.HIGH); + + assertEquals("vendor evidence mismatch", 2, d.getEvidence(EvidenceType.VENDOR).size()); + assertEquals("product evidence mismatch", 2, d.getEvidence(EvidenceType.PRODUCT).size()); + assertEquals("version evidence mismatch", 3, d.getEvidence(EvidenceType.VERSION).size()); instance.analyze(d, null); - assertEquals("vendor evidence mismatch",1, d.getVendorEvidence().size()); - assertEquals("product evidence mismatch",1, d.getProductEvidence().size()); - assertEquals("version evidence mismatch",2, d.getVersionEvidence().size()); - - + assertEquals("vendor evidence mismatch", 1, d.getEvidence(EvidenceType.VENDOR).size()); + assertEquals("product evidence mismatch", 1, d.getEvidence(EvidenceType.PRODUCT).size()); + assertEquals("version evidence mismatch", 2, d.getEvidence(EvidenceType.VERSION).size()); + } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/JarAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/JarAnalyzerTest.java index 4d9684f3d..9ade6d030 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/JarAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/JarAnalyzerTest.java @@ -33,6 +33,7 @@ import org.owasp.dependencycheck.utils.Settings; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * @author Jeremy Long @@ -50,16 +51,17 @@ public class JarAnalyzerTest extends BaseTest { File file = BaseTest.getResourceAsFile(this, "struts2-core-2.1.2.jar"); Dependency result = new Dependency(file); JarAnalyzer instance = new JarAnalyzer(); - instance.initializeFileTypeAnalyzer(); + instance.initialize(getSettings()); + instance.prepareFileTypeAnalyzer(null); instance.analyze(result, null); - assertTrue(result.getVendorEvidence().toString().toLowerCase().contains("apache")); - assertTrue(result.getVendorEvidence().getWeighting().contains("apache")); + assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().toLowerCase().contains("apache")); + assertTrue(result.getVendorWeightings().contains("apache")); file = BaseTest.getResourceAsFile(this, "dwr.jar"); result = new Dependency(file); instance.analyze(result, null); boolean found = false; - for (Evidence e : result.getVendorEvidence()) { + for (Evidence e : result.getEvidence(EvidenceType.VENDOR)) { if (e.getName().equals("url")) { assertEquals("Project url was not as expected in dwr.jar", e.getValue(), "http://getahead.ltd.uk/dwr"); found = true; @@ -73,7 +75,7 @@ public class JarAnalyzerTest extends BaseTest { result = new Dependency(file); instance.analyze(result, null); found = false; - for (Evidence e : result.getProductEvidence()) { + for (Evidence e : result.getEvidence(EvidenceType.PRODUCT)) { if (e.getName().equalsIgnoreCase("package-title") && e.getValue().equalsIgnoreCase("org.mortbay.http")) { found = true; @@ -83,7 +85,7 @@ public class JarAnalyzerTest extends BaseTest { assertTrue("package-title of org.mortbay.http not found in org.mortbay.jetty.jar", found); found = false; - for (Evidence e : result.getVendorEvidence()) { + for (Evidence e : result.getEvidence(EvidenceType.VENDOR)) { if (e.getName().equalsIgnoreCase("implementation-url") && e.getValue().equalsIgnoreCase("http://jetty.mortbay.org")) { found = true; @@ -93,7 +95,7 @@ public class JarAnalyzerTest extends BaseTest { assertTrue("implementation-url of http://jetty.mortbay.org not found in org.mortbay.jetty.jar", found); found = false; - for (Evidence e : result.getVersionEvidence()) { + for (Evidence e : result.getEvidence(EvidenceType.VERSION)) { if (e.getName().equalsIgnoreCase("Implementation-Version") && e.getValue().equalsIgnoreCase("4.2.27")) { found = true; @@ -106,7 +108,7 @@ public class JarAnalyzerTest extends BaseTest { file = BaseTest.getResourceAsFile(this, "org.mortbay.jmx.jar"); result = new Dependency(file); instance.analyze(result, null); - assertEquals("org.mortbar.jmx.jar has version evidence?", result.getVersionEvidence().size(), 0); + assertEquals("org.mortbar.jmx.jar has version evidence?", result.getEvidence(EvidenceType.VERSION).size(), 0); } /** @@ -115,7 +117,8 @@ public class JarAnalyzerTest extends BaseTest { @Test public void testAcceptSupportedExtensions() throws Exception { JarAnalyzer instance = new JarAnalyzer(); - instance.initialize(); + instance.initialize(getSettings()); + instance.prepare(null); instance.setEnabled(true); String[] files = {"test.jar", "test.war"}; for (String name : files) { @@ -142,7 +145,7 @@ public class JarAnalyzerTest extends BaseTest { List cni = new ArrayList<>(); instance.parseManifest(result, cni); - assertTrue(result.getVersionEvidence().getEvidence("manifest: org/apache/xalan/").size() > 0); + assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("manifest: org/apache/xalan/")); } /** @@ -181,14 +184,15 @@ public class JarAnalyzerTest extends BaseTest { JarAnalyzer instance = new JarAnalyzer(); Dependency macOSMetaDataFile = new Dependency(); macOSMetaDataFile - .setActualFilePath(FileUtils.getFile("src", "test", "resources", "._avro-ipc-1.5.0.jar").getAbsolutePath()); + .setActualFilePath(FileUtils.getFile("src", "test", "resources", "._avro-ipc-1.5.0.jar").getAbsolutePath()); macOSMetaDataFile.setFileName("._avro-ipc-1.5.0.jar"); Dependency actualJarFile = new Dependency(); actualJarFile.setActualFilePath(BaseTest.getResourceAsFile(this, "avro-ipc-1.5.0.jar").getAbsolutePath()); actualJarFile.setFileName("avro-ipc-1.5.0.jar"); - Engine engine = new Engine(); - engine.setDependencies(Arrays.asList(macOSMetaDataFile, actualJarFile)); - instance.analyzeDependency(macOSMetaDataFile, engine); + try (Engine engine = new Engine(getSettings())) { + engine.setDependencies(Arrays.asList(macOSMetaDataFile, actualJarFile)); + instance.analyzeDependency(macOSMetaDataFile, engine); + } } @Test @@ -196,10 +200,11 @@ public class JarAnalyzerTest extends BaseTest { JarAnalyzer instance = new JarAnalyzer(); Dependency textFileWithJarExtension = new Dependency(); textFileWithJarExtension - .setActualFilePath(BaseTest.getResourceAsFile(this, "textFileWithJarExtension.jar").getAbsolutePath()); + .setActualFilePath(BaseTest.getResourceAsFile(this, "textFileWithJarExtension.jar").getAbsolutePath()); textFileWithJarExtension.setFileName("textFileWithJarExtension.jar"); - Engine engine = new Engine(); - engine.setDependencies(Collections.singletonList(textFileWithJarExtension)); - instance.analyzeDependency(textFileWithJarExtension, engine); + try (Engine engine = new Engine(getSettings())) { + engine.setDependencies(Collections.singletonList(textFileWithJarExtension)); + instance.analyzeDependency(textFileWithJarExtension, engine); + } } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzerTest.java index 50c93a3eb..73d94abf8 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NodePackageAnalyzerTest.java @@ -29,6 +29,7 @@ import java.io.File; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for NodePackageAnalyzer. @@ -48,10 +49,13 @@ public class NodePackageAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new NodePackageAnalyzer(); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.initialize(getSettings()); + analyzer.prepare(null); } /** @@ -60,9 +64,10 @@ public class NodePackageAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } /** @@ -91,10 +96,10 @@ public class NodePackageAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nodejs/node_modules/dns-sync/package.json")); analyzer.analyze(result, null); - final String vendorString = result.getVendorEvidence().toString(); + final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString(); assertThat(vendorString, containsString("Sanjeev Koranga")); assertThat(vendorString, containsString("dns-sync_project")); - assertThat(result.getProductEvidence().toString(), containsString("dns-sync")); - assertThat(result.getVersionEvidence().toString(), containsString("0.1.0")); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("dns-sync")); + assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("0.1.0")); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NspAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NspAnalyzerTest.java index 9dca7d643..5d9d7b747 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NspAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NspAnalyzerTest.java @@ -11,21 +11,27 @@ import java.io.File; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; +import org.owasp.dependencycheck.dependency.EvidenceType; public class NspAnalyzerTest extends BaseTest { + private NspAnalyzer analyzer; @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new NspAnalyzer(); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.initialize(getSettings()); + analyzer.prepare(null); } @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } @Test @@ -43,18 +49,19 @@ public class NspAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/package.json")); analyzer.analyze(result, null); - assertEquals(result.getVendorEvidence().toString(), "owasp-nodejs-goat_project "); - assertEquals(result.getProductEvidence().toString(), "A tool to learn OWASP Top 10 for node.js developers owasp-nodejs-goat "); - assertEquals(result.getVersionEvidence().toString(), "1.3.0 "); + assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("owasp-nodejs-goat_project")); + assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("A tool to learn OWASP Top 10 for node.js developers")); + assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("1.3.0")); } + @Test public void testAnalyzeEmpty() throws AnalysisException { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/empty.json")); analyzer.analyze(result, null); - assertEquals(result.getVendorEvidence().size(), 0); - assertEquals(result.getProductEvidence().size(), 0); - assertEquals(result.getVersionEvidence().size(), 0); + assertEquals(result.getEvidence(EvidenceType.VENDOR).size(), 0); + assertEquals(result.getEvidence(EvidenceType.PRODUCT).size(), 0); + assertEquals(result.getEvidence(EvidenceType.VERSION).size(), 0); } @Test @@ -62,9 +69,9 @@ public class NspAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/bundled.deps.package.json")); analyzer.analyze(result, null); - assertEquals(result.getVendorEvidence().toString(), "Philipp Dunkel fsevents_project "); - assertEquals(result.getProductEvidence().toString(), "Native Access to Mac OS-X FSEvents fsevents "); - assertEquals(result.getVersionEvidence().toString(), "1.1.1 "); + assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("Philipp Dunkel ")); + assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("Native Access to Mac OS-X FSEvents")); + assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("1.1.1")); } @Test @@ -72,20 +79,16 @@ public class NspAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nsp/license.obj.package.json")); analyzer.analyze(result, null); - assertEquals(result.getVendorEvidence().toString(), "Twitter, Inc. bootstrap_project "); - assertEquals(result.getProductEvidence().toString(), "The most popular front-end framework for developing responsive, mobile first projects on the web. bootstrap "); - assertEquals(result.getVersionEvidence().toString(), "3.2.0 "); + assertTrue(result.getEvidence(EvidenceType.VENDOR).toString().contains("Twitter, Inc.")); + assertTrue(result.getEvidence(EvidenceType.PRODUCT).toString().contains("The most popular front-end framework for developing responsive, mobile first projects on the web")); + assertTrue(result.getEvidence(EvidenceType.VERSION).toString().contains("3.2.0")); } @Test public void testAnalyzePackageJsonInNodeModulesDirectory() throws AnalysisException { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "nodejs/node_modules/dns-sync/package.json")); analyzer.analyze(result, null); - final String vendorString = result.getVendorEvidence().toString(); - - // node modules are not scanned - assertTrue(vendorString.isEmpty()); - assertEquals(result.getProductEvidence().size(), 0); - assertEquals(result.getVersionEvidence().size(), 0); + // node modules are not scanned - no evidence is collected + assertTrue(result.size() == 0); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzerTest.java index 6d184dd2a..1382988f7 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/NuspecAnalyzerTest.java @@ -31,9 +31,12 @@ public class NuspecAnalyzerTest extends BaseTest { private NuspecAnalyzer instance; @Before + @Override public void setUp() throws Exception { + super.setUp(); instance = new NuspecAnalyzer(); - instance.initialize(); + instance.initialize(getSettings()); + instance.prepare(null); instance.setEnabled(true); } @@ -53,5 +56,3 @@ public class NuspecAnalyzerTest extends BaseTest { assertEquals(AnalysisPhase.INFORMATION_COLLECTION, instance.getAnalysisPhase()); } } - -// vim: cc=120:sw=4:ts=4:sts=4 diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java index b3cca4878..1daf7fd4a 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/OpenSSLAnalyzerTest.java @@ -28,6 +28,7 @@ import java.io.File; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.*; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for OpenSSLAnalyzerAnalyzer. @@ -47,10 +48,13 @@ public class OpenSSLAnalyzerTest extends BaseTest { * @throws Exception if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new OpenSSLAnalyzer(); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.initialize(getSettings()); + analyzer.prepare(null); } /** @@ -59,9 +63,10 @@ public class OpenSSLAnalyzerTest extends BaseTest { * @throws Exception if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } /** @@ -69,8 +74,7 @@ public class OpenSSLAnalyzerTest extends BaseTest { */ @Test public void testGetName() { - assertEquals("Analyzer name wrong.", "OpenSSL Source Analyzer", - analyzer.getName()); + assertEquals("Analyzer name wrong.", "OpenSSL Source Analyzer", analyzer.getName()); } /** @@ -105,8 +109,8 @@ public class OpenSSLAnalyzerTest extends BaseTest { this, "openssl/opensslv.h")); analyzer.analyze(result, null); - assertThat(result.getProductEvidence().toString(), containsString("OpenSSL")); - assertThat(result.getVendorEvidence().toString(), containsString("OpenSSL")); - assertThat(result.getVersionEvidence().toString(), containsString("1.0.2c")); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("OpenSSL")); + assertThat(result.getEvidence(EvidenceType.VENDOR).toString(), containsString("OpenSSL")); + assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("1.0.2c")); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzerTest.java index f788b9965..89910a90b 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonDistributionAnalyzerTest.java @@ -30,6 +30,7 @@ import java.io.File; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for PythonDistributionAnalyzer. @@ -49,10 +50,13 @@ public class PythonDistributionAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new PythonDistributionAnalyzer(); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.initialize(getSettings()); + analyzer.prepare(null); } /** @@ -61,9 +65,10 @@ public class PythonDistributionAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } /** @@ -123,8 +128,8 @@ public class PythonDistributionAnalyzerTest extends BaseTest { boolean found = false; analyzer.analyze(result, null); assertTrue("Expected vendor evidence to contain \"djangoproject\".", - result.getVendorEvidence().toString().contains("djangoproject")); - for (final Evidence e : result.getVersionEvidence()) { + result.getEvidence(EvidenceType.VENDOR).toString().contains("djangoproject")); + for (final Evidence e : result.getEvidence(EvidenceType.VERSION)) { if ("Version".equals(e.getName()) && "1.7.2".equals(e.getValue())) { found = true; break; @@ -175,8 +180,8 @@ public class PythonDistributionAnalyzerTest extends BaseTest { context, resource)); analyzer.analyze(result, null); assertTrue("Expected vendor evidence to contain \"example\".", result - .getVendorEvidence().toString().contains("example")); - for (final Evidence e : result.getVersionEvidence()) { + .getEvidence(EvidenceType.VENDOR).toString().contains("example")); + for (final Evidence e : result.getEvidence(EvidenceType.VERSION)) { if ("0.0.1".equals(e.getValue())) { found = true; break; diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzerTest.java index 74d594535..b78d32f5d 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/PythonPackageAnalyzerTest.java @@ -29,6 +29,7 @@ import java.io.File; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for PythonPackageAnalyzer. @@ -48,10 +49,13 @@ public class PythonPackageAnalyzerTest extends BaseTest { * @throws Exception if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new PythonPackageAnalyzer(); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.initialize(getSettings()); + analyzer.prepare(null); } /** @@ -60,9 +64,10 @@ public class PythonPackageAnalyzerTest extends BaseTest { * @throws Exception if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } /** @@ -89,9 +94,9 @@ public class PythonPackageAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile( this, "python/eggtest/__init__.py")); analyzer.analyze(result, null); - assertTrue("Expected vendor evidence to contain \"example\".", result - .getVendorEvidence().toString().contains("example")); - for (final Evidence e : result.getVersionEvidence()) { + assertTrue("Expected vendor evidence to contain \"example\".", + result.getEvidence(EvidenceType.VENDOR).toString().contains("example")); + for (final Evidence e : result.getEvidence(EvidenceType.VERSION)) { if ("0.0.1".equals(e.getValue())) { found = true; break; @@ -99,5 +104,4 @@ public class PythonPackageAnalyzerTest extends BaseTest { } assertTrue("Version 0.0.1 not found in EggTest dependency.", found); } - } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerIT.java similarity index 56% rename from dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java rename to dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerIT.java index b7b2b9b85..1f44f88f7 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundleAuditAnalyzerIT.java @@ -17,18 +17,13 @@ */ package org.owasp.dependencycheck.analyzer; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import edu.emory.mathcs.backport.java.util.Arrays; import java.io.File; -import java.util.Iterator; +import java.util.ArrayList; import java.util.List; -import java.util.Set; import org.junit.After; -import static org.junit.Assert.assertNotNull; import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -38,24 +33,28 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.Evidence; -import org.owasp.dependencycheck.dependency.Identifier; import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.exception.ExceptionCollection; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.junit.Assert.fail; +import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.exception.InitializationException; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for {@link RubyBundleAuditAnalyzer}. * * @author Dale Visser */ -public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { +public class RubyBundleAuditAnalyzerIT extends BaseDBTestCase { - private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzerTest.class); + private static final Logger LOGGER = LoggerFactory.getLogger(RubyBundleAuditAnalyzerIT.class); /** * The analyzer to test. @@ -68,11 +67,15 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + super.setUp(); + //test testAddCriticalityToVulnerability requires CVE-2015-3225 so we must ensure db is updated. + //getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); analyzer = new RubyBundleAuditAnalyzer(); + analyzer.initialize(getSettings()); analyzer.setFilesMatched(true); } @@ -82,11 +85,13 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { if (analyzer != null) { analyzer.close(); analyzer = null; } + super.tearDown(); } /** @@ -112,20 +117,27 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { */ @Test public void testAnalysis() throws AnalysisException, DatabaseException { - try { - analyzer.initialize(); + try (Engine engine = new Engine(getSettings())) { + engine.openDatabase(); + analyzer.prepare(engine); final String resource = "ruby/vulnerable/gems/rails-4.1.15/Gemfile.lock"; final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, resource)); - final Engine engine = new Engine(); analyzer.analyze(result, engine); - int size = engine.getDependencies().size(); + final Dependency[] dependencies = engine.getDependencies(); + final int size = dependencies.length; assertTrue(size >= 1); + boolean found = false; + for (Dependency dependency : dependencies) { + found = dependency.getEvidence(EvidenceType.PRODUCT).toString().toLowerCase().contains("redcarpet"); + found &= dependency.getEvidence(EvidenceType.VERSION).toString().toLowerCase().contains("2.2.2"); + found &= dependency.getFilePath().endsWith(resource); + found &= dependency.getFileName().equals("Gemfile.lock"); + if (found) { + break; + } + } + assertTrue("redcarpet was not identified", found); - Dependency dependency = engine.getDependencies().get(0); - assertTrue(dependency.getProductEvidence().toString().toLowerCase().contains("redcarpet")); - assertTrue(dependency.getVersionEvidence().toString().toLowerCase().contains("2.2.2")); - assertTrue(dependency.getFilePath().endsWith(resource)); - assertTrue(dependency.getFileName().equals("Gemfile.lock")); } catch (InitializationException | DatabaseException | AnalysisException e) { LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Make sure Ruby gem bundle-audit is installed. You may also need to set property \"analyzer.bundle.audit.path\"."); Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", e); @@ -137,19 +149,18 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { */ @Test public void testAddCriticalityToVulnerability() throws AnalysisException, DatabaseException { - try { - analyzer.initialize(); + try (Engine engine = new Engine(getSettings())) { + engine.doUpdates(true); + analyzer.prepare(engine); final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "ruby/vulnerable/gems/sinatra/Gemfile.lock")); - final Engine engine = new Engine(); analyzer.analyze(result, engine); - - Dependency dependency = engine.getDependencies().get(0); + Dependency dependency = engine.getDependencies()[0]; Vulnerability vulnerability = dependency.getVulnerabilities().first(); assertEquals(vulnerability.getCvssScore(), 5.0f, 0.0); - } catch (InitializationException | DatabaseException | AnalysisException e) { + } catch (InitializationException | DatabaseException | AnalysisException | UpdateException e) { LOGGER.warn("Exception setting up RubyBundleAuditAnalyzer. Make sure Ruby gem bundle-audit is installed. You may also need to set property \"analyzer.bundle.audit.path\"."); Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", e); } @@ -162,18 +173,21 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { */ @Test public void testMissingBundleAudit() throws AnalysisException, DatabaseException { + //TODO - this test is invalid as phantom bundle audit may not exist - but if bundle-audit + // is still on the path then initialization works and the bundle-audit on the path works. //set a non-exist bundle-audit - Settings.setString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, "phantom-bundle-audit"); - try { - //initialize should fail. - analyzer.initialize(); - } catch (Exception e) { - //expected, so ignore. - assertNotNull(e); - } finally { - assertThat(analyzer.isEnabled(), is(false)); - LOGGER.info("phantom-bundle-audit is not available. Ruby Bundle Audit Analyzer is disabled as expected."); - } +// getSettings().setString(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, "phantom-bundle-audit"); +// analyzer.initialize(getSettings()); +// try { +// //initialize should fail. +// analyzer.prepare(null); +// } catch (Exception e) { +// //expected, so ignore. +// assertNotNull(e); +// } finally { +// assertThat(analyzer.isEnabled(), is(false)); +// LOGGER.info("phantom-bundle-audit is not available. Ruby Bundle Audit Analyzer is disabled as expected."); +// } } /** @@ -184,51 +198,19 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { */ @Test public void testDependenciesPath() throws AnalysisException, DatabaseException { - final Engine engine = new Engine(); - engine.scan(BaseTest.getResourceAsFile(this, - "ruby/vulnerable/gems/rails-4.1.15/")); - try { - engine.analyzeDependencies(); - } catch (NullPointerException ex) { - LOGGER.error("NPE", ex); - fail(ex.getMessage()); - } catch (ExceptionCollection ex) { - Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", ex); - return; - } - List dependencies = engine.getDependencies(); - LOGGER.info("{} dependencies found.", dependencies.size()); - Iterator dIterator = dependencies.iterator(); - while (dIterator.hasNext()) { - Dependency dept = dIterator.next(); - LOGGER.info("dept path: {}", dept.getActualFilePath()); - - Set identifiers = dept.getIdentifiers(); - Iterator idIterator = identifiers.iterator(); - while (idIterator.hasNext()) { - Identifier id = idIterator.next(); - LOGGER.info(" Identifier: {}, type={}, url={}, conf={}", id.getValue(), id.getType(), id.getUrl(), id.getConfidence()); - } - - Set prodEv = dept.getProductEvidence().getEvidence(); - Iterator it = prodEv.iterator(); - while (it.hasNext()) { - Evidence e = it.next(); - LOGGER.info(" prod: name={}, value={}, source={}, confidence={}", e.getName(), e.getValue(), e.getSource(), e.getConfidence()); - } - Set versionEv = dept.getVersionEvidence().getEvidence(); - Iterator vIt = versionEv.iterator(); - while (vIt.hasNext()) { - Evidence e = vIt.next(); - LOGGER.info(" version: name={}, value={}, source={}, confidence={}", e.getName(), e.getValue(), e.getSource(), e.getConfidence()); - } - - Set vendorEv = dept.getVendorEvidence().getEvidence(); - Iterator vendorIt = vendorEv.iterator(); - while (vendorIt.hasNext()) { - Evidence e = vendorIt.next(); - LOGGER.info(" vendor: name={}, value={}, source={}, confidence={}", e.getName(), e.getValue(), e.getSource(), e.getConfidence()); + try (Engine engine = new Engine(getSettings())) { + try { + engine.scan(BaseTest.getResourceAsFile(this, "ruby/vulnerable/gems/rails-4.1.15/")); + engine.analyzeDependencies(); + } catch (NullPointerException ex) { + LOGGER.error("NPE", ex); + fail(ex.getMessage()); + } catch (ExceptionCollection ex) { + Assume.assumeNoException("Exception setting up RubyBundleAuditAnalyzer; bundle audit may not be installed, or property \"analyzer.bundle.audit.path\" may not be set.", ex); + return; } + List dependencies = new ArrayList<>(Arrays.asList(engine.getDependencies())); + LOGGER.info("{} dependencies found.", dependencies.size()); } } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzerTest.java index cfab09c4e..fc8edc102 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyBundlerAnalyzerTest.java @@ -29,6 +29,7 @@ import java.io.File; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for {@link RubyBundlerAnalyzer}. @@ -48,10 +49,13 @@ public class RubyBundlerAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new RubyBundlerAnalyzer(); + analyzer.initialize(getSettings()); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.prepare(null); } /** @@ -60,9 +64,10 @@ public class RubyBundlerAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } /** @@ -93,14 +98,14 @@ public class RubyBundlerAnalyzerTest extends BaseTest { "ruby/vulnerable/gems/rails-4.1.15/vendor/bundle/ruby/2.2.0/specifications/dalli-2.7.5.gemspec")); analyzer.analyze(result, null); - final String vendorString = result.getVendorEvidence().toString(); + final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString(); assertThat(vendorString, containsString("Peter M. Goldstein")); assertThat(vendorString, containsString("Mike Perham")); assertThat(vendorString, containsString("peter.m.goldstein@gmail.com")); assertThat(vendorString, containsString("https://github.com/petergoldstein/dalli")); assertThat(vendorString, containsString("MIT")); - assertThat(result.getProductEvidence().toString(), containsString("dalli")); - assertThat(result.getProductEvidence().toString(), containsString("High performance memcached client for Ruby")); - assertThat(result.getVersionEvidence().toString(), containsString("2.7.5")); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("dalli")); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("High performance memcached client for Ruby")); + assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("2.7.5")); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java index 4521504be..11dea120a 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/RubyGemspecAnalyzerTest.java @@ -29,6 +29,7 @@ import java.io.File; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for {@link RubyGemspecAnalyzer}. @@ -48,10 +49,13 @@ public class RubyGemspecAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); analyzer = new RubyGemspecAnalyzer(); + analyzer.initialize(getSettings()); analyzer.setFilesMatched(true); - analyzer.initialize(); + analyzer.prepare(null); } /** @@ -60,9 +64,10 @@ public class RubyGemspecAnalyzerTest extends BaseTest { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { analyzer.close(); - analyzer = null; + super.tearDown(); } /** @@ -92,13 +97,13 @@ public class RubyGemspecAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "ruby/vulnerable/gems/specifications/rest-client-1.7.2.gemspec")); analyzer.analyze(result, null); - final String vendorString = result.getVendorEvidence().toString(); + final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString(); assertThat(vendorString, containsString("REST Client Team")); assertThat(vendorString, containsString("rest-client_project")); assertThat(vendorString, containsString("rest.client@librelist.com")); assertThat(vendorString, containsString("https://github.com/rest-client/rest-client")); - assertThat(result.getProductEvidence().toString(), containsString("rest-client")); - assertThat(result.getVersionEvidence().toString(), containsString("1.7.2")); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("rest-client")); + assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("1.7.2")); } /** @@ -111,6 +116,6 @@ public class RubyGemspecAnalyzerTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "ruby/vulnerable/gems/rails-4.1.15/vendor/bundle/ruby/2.2.0/gems/pg-0.18.4/Rakefile")); analyzer.analyze(result, null); - assertTrue(result.getEvidence().size()>0); + assertTrue(result.size()>0); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java index 8b81bbe3e..b430f0c21 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/SwiftAnalyzersTest.java @@ -12,6 +12,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import java.io.File; +import org.owasp.dependencycheck.dependency.EvidenceType; /** * Unit tests for CocoaPodsAnalyzer. @@ -32,14 +33,18 @@ public class SwiftAnalyzersTest extends BaseTest { * @throws Exception thrown if there is a problem */ @Before + @Override public void setUp() throws Exception { + super.setUp(); podsAnalyzer = new CocoaPodsAnalyzer(); + podsAnalyzer.initialize(getSettings()); podsAnalyzer.setFilesMatched(true); - podsAnalyzer.initialize(); + podsAnalyzer.prepare(null); spmAnalyzer = new SwiftPackageManagerAnalyzer(); + spmAnalyzer.initialize(getSettings()); spmAnalyzer.setFilesMatched(true); - spmAnalyzer.initialize(); + spmAnalyzer.prepare(null); } /** @@ -48,12 +53,15 @@ public class SwiftAnalyzersTest extends BaseTest { * @throws Exception thrown if there is a problem */ @After + @Override public void tearDown() throws Exception { podsAnalyzer.close(); podsAnalyzer = null; spmAnalyzer.close(); spmAnalyzer = null; + + super.tearDown(); } /** @@ -98,13 +106,13 @@ public class SwiftAnalyzersTest extends BaseTest { final Dependency result = new Dependency(BaseTest.getResourceAsFile(this, "swift/cocoapods/EasyPeasy.podspec")); podsAnalyzer.analyze(result, null); - final String vendorString = result.getVendorEvidence().toString(); + final String vendorString = result.getEvidence(EvidenceType.VENDOR).toString(); assertThat(vendorString, containsString("Carlos Vidal")); assertThat(vendorString, containsString("https://github.com/nakiostudio/EasyPeasy")); assertThat(vendorString, containsString("MIT")); - assertThat(result.getProductEvidence().toString(), containsString("EasyPeasy")); - assertThat(result.getVersionEvidence().toString(), containsString("0.2.3")); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("EasyPeasy")); + assertThat(result.getEvidence(EvidenceType.VERSION).toString(), containsString("0.2.3")); } /** @@ -118,6 +126,6 @@ public class SwiftAnalyzersTest extends BaseTest { "swift/Gloss/Package.swift")); spmAnalyzer.analyze(result, null); - assertThat(result.getProductEvidence().toString(), containsString("Gloss")); + assertThat(result.getEvidence(EvidenceType.PRODUCT).toString(), containsString("Gloss")); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzerTest.java index f92ea8809..a00b7f3e9 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzerTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VersionFilterAnalyzerTest.java @@ -22,7 +22,7 @@ import static org.junit.Assert.*; import org.owasp.dependencycheck.BaseTest; import org.owasp.dependencycheck.dependency.Confidence; import org.owasp.dependencycheck.dependency.Dependency; -import org.owasp.dependencycheck.dependency.EvidenceCollection; +import org.owasp.dependencycheck.dependency.EvidenceType; import org.owasp.dependencycheck.utils.Settings; /** @@ -48,6 +48,7 @@ public class VersionFilterAnalyzerTest extends BaseTest { @Test public void testGetAnalysisPhase() { VersionFilterAnalyzer instance = new VersionFilterAnalyzer(); + instance.initialize(getSettings()); AnalysisPhase expResult = AnalysisPhase.POST_INFORMATION_COLLECTION; AnalysisPhase result = instance.getAnalysisPhase(); assertEquals(expResult, result); @@ -60,6 +61,7 @@ public class VersionFilterAnalyzerTest extends BaseTest { @Test public void testGetAnalyzerEnabledSettingKey() { VersionFilterAnalyzer instance = new VersionFilterAnalyzer(); + instance.initialize(getSettings()); String expResult = Settings.KEYS.ANALYZER_VERSION_FILTER_ENABLED; String result = instance.getAnalyzerEnabledSettingKey(); assertEquals(expResult, result); @@ -71,39 +73,39 @@ public class VersionFilterAnalyzerTest extends BaseTest { @Test public void testAnalyzeDependency() throws Exception { Dependency dependency = new Dependency(); - EvidenceCollection versions = dependency.getVersionEvidence(); - versions.addEvidence("util", "version", "33.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); - versions.addEvidence("other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "util", "version", "33.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); VersionFilterAnalyzer instance = new VersionFilterAnalyzer(); + instance.initialize(getSettings()); instance.analyzeDependency(dependency, null); - assertEquals(3, versions.size()); + assertEquals(3, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("pom", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "pom", "version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(4, versions.size()); + assertEquals(4, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("file", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "file", "version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(2, versions.size()); + assertEquals(2, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("Manifest", "Implementation-Version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "Manifest", "Implementation-Version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(3, versions.size()); + assertEquals(3, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("nexus", "version", "1.2.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "nexus", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(4, versions.size()); + assertEquals(4, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("central", "version", "1.2.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "central", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(5, versions.size()); + assertEquals(5, dependency.getEvidence(EvidenceType.VERSION).size()); } /** @@ -112,35 +114,35 @@ public class VersionFilterAnalyzerTest extends BaseTest { @Test public void testAnalyzeDependencyFilePom() throws Exception { Dependency dependency = new Dependency(); - EvidenceCollection versions = dependency.getVersionEvidence(); - versions.addEvidence("util", "version", "33.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); - versions.addEvidence("other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "util", "version", "33.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); VersionFilterAnalyzer instance = new VersionFilterAnalyzer(); + instance.initialize(getSettings()); instance.analyzeDependency(dependency, null); - assertEquals(3, versions.size()); + assertEquals(3, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("pom", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "pom", "version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(4, versions.size()); + assertEquals(4, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("file", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "file", "version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(2, versions.size()); + assertEquals(2, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("nexus", "version", "1.2.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "nexus", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(3, versions.size()); + assertEquals(3, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("central", "version", "1.2.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "central", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(4, versions.size()); + assertEquals(4, dependency.getEvidence(EvidenceType.VERSION).size()); } /** @@ -149,25 +151,25 @@ public class VersionFilterAnalyzerTest extends BaseTest { @Test public void testAnalyzeDependencyFileManifest() throws Exception { Dependency dependency = new Dependency(); - EvidenceCollection versions = dependency.getVersionEvidence(); - versions.addEvidence("util", "version", "33.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); - versions.addEvidence("other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "util", "version", "33.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); VersionFilterAnalyzer instance = new VersionFilterAnalyzer(); + instance.initialize(getSettings()); instance.analyzeDependency(dependency, null); - assertEquals(3, versions.size()); + assertEquals(3, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("Manifest", "Implementation-Version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "Manifest", "Implementation-Version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(4, versions.size()); + assertEquals(4, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("file", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "file", "version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(2, versions.size()); + assertEquals(2, dependency.getEvidence(EvidenceType.VERSION).size()); } /** @@ -176,35 +178,34 @@ public class VersionFilterAnalyzerTest extends BaseTest { @Test public void testAnalyzeDependencyPomManifest() throws Exception { Dependency dependency = new Dependency(); - EvidenceCollection versions = dependency.getVersionEvidence(); - versions.addEvidence("util", "version", "33.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); - versions.addEvidence("other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "util", "version", "33.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "Implementation-Version", "1.2.3", Confidence.HIGHEST); VersionFilterAnalyzer instance = new VersionFilterAnalyzer(); + instance.initialize(getSettings()); instance.analyzeDependency(dependency, null); - assertEquals(3, versions.size()); + assertEquals(3, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("pom", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "pom", "version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(4, versions.size()); + assertEquals(4, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("Manifest", "Implementation-Version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "Manifest", "Implementation-Version", "1.2.3", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(2, versions.size()); + assertEquals(2, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("nexus", "version", "1.2.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "nexus", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(3, versions.size()); + assertEquals(3, dependency.getEvidence(EvidenceType.VERSION).size()); - versions.addEvidence("central", "version", "1.2.3", Confidence.HIGHEST); - versions.addEvidence("other", "version", "alpha", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "central", "version", "1.2.3", Confidence.HIGHEST); + dependency.addEvidence(EvidenceType.VERSION, "other", "version", "alpha", Confidence.HIGHEST); instance.analyzeDependency(dependency, null); - assertEquals(4, versions.size()); + assertEquals(4, dependency.getEvidence(EvidenceType.VERSION).size()); } - } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIT.java index 807a84666..79bf7761f 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/analyzer/VulnerabilitySuppressionAnalyzerIT.java @@ -42,17 +42,20 @@ public class VulnerabilitySuppressionAnalyzerIT extends BaseDBTestCase { @Test public void testGetName() { VulnerabilitySuppressionAnalyzer instance = new VulnerabilitySuppressionAnalyzer(); + instance.initialize(getSettings()); String expResult = "Vulnerability Suppression Analyzer"; String result = instance.getName(); assertEquals(expResult, result); } /** - * Test of getAnalysisPhase method, of class VulnerabilitySuppressionAnalyzer. + * Test of getAnalysisPhase method, of class + * VulnerabilitySuppressionAnalyzer. */ @Test public void testGetAnalysisPhase() { VulnerabilitySuppressionAnalyzer instance = new VulnerabilitySuppressionAnalyzer(); + instance.initialize(getSettings()); AnalysisPhase expResult = AnalysisPhase.POST_FINDING_ANALYSIS; AnalysisPhase result = instance.getAnalysisPhase(); assertEquals(expResult, result); @@ -68,26 +71,27 @@ public class VulnerabilitySuppressionAnalyzerIT extends BaseDBTestCase { File file = BaseTest.getResourceAsFile(this, "commons-fileupload-1.2.1.jar"); //File suppression = new File(this.getClass().getClassLoader().getResource("commons-fileupload-1.2.1.suppression.xml").getPath()); File suppression = BaseTest.getResourceAsFile(this, "commons-fileupload-1.2.1.suppression.xml"); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); - Settings.setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); - Engine engine = new Engine(); - engine.scan(file); - engine.analyzeDependencies(); - Dependency dependency = getDependency(engine, file); - int cveSize = dependency.getVulnerabilities().size(); - int cpeSize = dependency.getIdentifiers().size(); - assertTrue(cveSize > 0); - assertTrue(cpeSize > 0); - Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppression.getAbsolutePath()); - VulnerabilitySuppressionAnalyzer instance = new VulnerabilitySuppressionAnalyzer(); - instance.initialize(); - instance.analyze(dependency, engine); - cveSize = cveSize > 1 ? cveSize - 2 : 0; - cpeSize = cpeSize > 0 ? cpeSize - 1 : 0; - assertTrue(dependency.getVulnerabilities().size() == cveSize); - assertTrue(dependency.getIdentifiers().size() == cpeSize); - engine.cleanup(); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_NEXUS_ENABLED, false); + getSettings().setBoolean(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, false); + try (Engine engine = new Engine(getSettings())) { + engine.scan(file); + engine.analyzeDependencies(); + Dependency dependency = getDependency(engine, file); + int cveSize = dependency.getVulnerabilities().size(); + int cpeSize = dependency.getIdentifiers().size(); + assertTrue(cveSize > 0); + assertTrue(cpeSize > 0); + getSettings().setString(Settings.KEYS.SUPPRESSION_FILE, suppression.getAbsolutePath()); + VulnerabilitySuppressionAnalyzer instance = new VulnerabilitySuppressionAnalyzer(); + instance.initialize(getSettings()); + instance.prepare(engine); + instance.analyze(dependency, engine); + cveSize = cveSize > 1 ? cveSize - 2 : 0; + cpeSize = cpeSize > 0 ? cpeSize - 1 : 0; + assertTrue(dependency.getVulnerabilities().size() == cveSize); + assertTrue(dependency.getIdentifiers().size() == cpeSize); + } } /** diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/central/CentralSearchTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/central/CentralSearchTest.java index a23f4e866..0cccfe79d 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/central/CentralSearchTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/central/CentralSearchTest.java @@ -22,10 +22,10 @@ public class CentralSearchTest extends BaseTest { private CentralSearch searcher; @Before + @Override public void setUp() throws Exception { - String centralUrl = Settings.getString(Settings.KEYS.ANALYZER_CENTRAL_URL); - LOGGER.debug(centralUrl); - searcher = new CentralSearch(new URL(centralUrl)); + super.setUp(); + searcher = new CentralSearch(getSettings()); } @Test(expected = IllegalArgumentException.class) diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/composer/ComposerLockParserTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/composer/ComposerLockParserTest.java index b325decf0..162258db6 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/composer/ComposerLockParserTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/composer/ComposerLockParserTest.java @@ -35,7 +35,9 @@ public class ComposerLockParserTest extends BaseTest { private InputStream inputStream; @Before - public void setUp() { + @Override + public void setUp() throws Exception { + super.setUp(); inputStream = this.getClass().getClassLoader().getResourceAsStream("composer.lock"); } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/lucene/SearchFieldAnalyzerTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/lucene/SearchFieldAnalyzerTest.java new file mode 100644 index 000000000..b2b56ba81 --- /dev/null +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/lucene/SearchFieldAnalyzerTest.java @@ -0,0 +1,39 @@ +/* + * This file is part of dependency-check-core. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (c) 2017 Jeremy Long. All Rights Reserved. + */ +package org.owasp.dependencycheck.data.lucene; + +import org.apache.lucene.analysis.util.CharArraySet; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author jeremy long + */ +public class SearchFieldAnalyzerTest { + + /** + * Test of getStopWords method, of class SearchFieldAnalyzer. + */ + @Test + public void testGetStopWords() { + CharArraySet result = SearchFieldAnalyzer.getStopWords(); + assertTrue(result.size() > 20); + assertTrue(result.contains("software")); + } +} diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nexus/NexusSearchTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nexus/NexusSearchTest.java index e565bf524..c77c90051 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nexus/NexusSearchTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nexus/NexusSearchTest.java @@ -18,7 +18,6 @@ package org.owasp.dependencycheck.data.nexus; import java.io.FileNotFoundException; -import java.net.URL; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.junit.Assume; @@ -26,7 +25,6 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; -import org.owasp.dependencycheck.analyzer.NexusAnalyzer; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,10 +35,12 @@ public class NexusSearchTest extends BaseTest { private NexusSearch searcher; @Before + @Override public void setUp() throws Exception { - String nexusUrl = Settings.getString(Settings.KEYS.ANALYZER_NEXUS_URL); + super.setUp(); + String nexusUrl = getSettings().getString(Settings.KEYS.ANALYZER_NEXUS_URL); LOGGER.debug(nexusUrl); - searcher = new NexusSearch(new URL(nexusUrl), NexusAnalyzer.useProxy()); + searcher = new NexusSearch(getSettings(), false); Assume.assumeTrue(searcher.preflightRequest()); } @@ -78,5 +78,3 @@ public class NexusSearchTest extends BaseTest { searcher.searchSha1("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); } } - -// vim: cc=120:sw=4:ts=4:sts=4 diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/NspSearchTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/NspSearchTest.java index 57569224a..d1d6298e6 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/NspSearchTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/NspSearchTest.java @@ -22,7 +22,6 @@ import org.junit.Before; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; -import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.json.Json; @@ -30,7 +29,6 @@ import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import javax.json.JsonReader; import java.io.InputStream; -import java.net.URL; import java.util.List; import static org.junit.Assume.assumeFalse; import org.owasp.dependencycheck.utils.URLConnectionFailureException; @@ -41,10 +39,10 @@ public class NspSearchTest extends BaseTest { private NspSearch searcher; @Before + @Override public void setUp() throws Exception { - String url = Settings.getString(Settings.KEYS.ANALYZER_NSP_URL); - LOGGER.debug(url); - searcher = new NspSearch(new URL(url)); + super.setUp(); + searcher = new NspSearch(getSettings()); } @Test diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/SanitizePackageTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/SanitizePackageTest.java index 8f0c74d7b..45cace154 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/SanitizePackageTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nsp/SanitizePackageTest.java @@ -61,5 +61,4 @@ public class SanitizePackageTest { Assert.assertFalse(sanitized.containsKey("license")); Assert.assertFalse(sanitized.containsKey("main")); } - } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactoryTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactoryTest.java index 4316c7311..18d1f778f 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactoryTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/ConnectionFactoryTest.java @@ -35,10 +35,11 @@ public class ConnectionFactoryTest extends BaseDBTestCase { */ @Test public void testInitialize() throws DatabaseException, SQLException { - ConnectionFactory.initialize(); - try (Connection result = ConnectionFactory.getConnection()) { + ConnectionFactory factory = new ConnectionFactory(getSettings()); + factory.initialize(); + try (Connection result = factory.getConnection()) { assertNotNull(result); } - ConnectionFactory.cleanup(); + factory.cleanup(); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBIT.java index 3ff98ee36..2ae6e8409 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBIT.java @@ -27,51 +27,47 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.junit.After; import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.junit.Before; /** * * @author Jeremy Long */ public class CveDBIT extends BaseDBTestCase { + + CveDB instance = null; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + instance = new CveDB(getSettings()); + } + + @After + @Override + public void tearDown() throws Exception { + instance.close(); + super.tearDown(); + } /** * Pretty useless tests of open, commit, and close methods, of class CveDB. */ @Test public void testOpen() { - CveDB instance = null; + try { - instance = CveDB.getInstance(); instance.commit(); } catch (DatabaseException | SQLException ex) { fail(ex.getMessage()); - } finally { - int start = instance.getUsageCount(); - instance.close(); - int end = instance.getUsageCount(); - assertTrue( end < start); } } @@ -80,12 +76,10 @@ public class CveDBIT extends BaseDBTestCase { */ @Test public void testGetCPEs() throws Exception { - CveDB instance = CveDB.getInstance(); String vendor = "apache"; String product = "struts"; Set result = instance.getCPEs(vendor, product); assertTrue(result.size() > 5); - instance.close(); } /** @@ -93,10 +87,8 @@ public class CveDBIT extends BaseDBTestCase { */ @Test public void testgetVulnerability() throws Exception { - CveDB instance = CveDB.getInstance(); Vulnerability result = instance.getVulnerability("CVE-2014-0094"); assertEquals("The ParametersInterceptor in Apache Struts before 2.3.16.1 allows remote attackers to \"manipulate\" the ClassLoader via the class parameter, which is passed to the getClass method.", result.getDescription()); - instance.close(); } /** @@ -105,7 +97,6 @@ public class CveDBIT extends BaseDBTestCase { @Test public void testGetVulnerabilities() throws Exception { String cpeStr = "cpe:/a:apache:struts:2.1.2"; - CveDB instance = CveDB.getInstance(); List results; results = instance.getVulnerabilities(cpeStr); @@ -133,7 +124,6 @@ public class CveDBIT extends BaseDBTestCase { } } assertTrue("Expected " + expected + ", but was not identified", found); - instance.close(); } /** @@ -141,7 +131,6 @@ public class CveDBIT extends BaseDBTestCase { */ @Test public void testGetMatchingSoftware() throws Exception { - CveDB instance = CveDB.getInstance(); Map versions = new HashMap<>(); DependencyVersion identifiedVersion = new DependencyVersion("1.0.1o"); versions.put("cpe:/a:openssl:openssl:1.0.1e", Boolean.FALSE); @@ -189,6 +178,5 @@ public class CveDBIT extends BaseDBTestCase { identifiedVersion = new DependencyVersion("1.6.3"); results = instance.getMatchingSoftware(versions, "springsource", "spring_framework", identifiedVersion); assertNotNull(results); - instance.close(); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBMySqlIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBMySqlIT.java index f3e6657c8..343b1c02b 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBMySqlIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBMySqlIT.java @@ -17,8 +17,10 @@ */ package org.owasp.dependencycheck.data.nvdcve; +import java.sql.SQLException; import java.util.List; import java.util.Set; +import org.junit.After; import org.junit.Test; import org.owasp.dependencycheck.BaseTest; @@ -26,6 +28,7 @@ import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.VulnerableSoftware; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.junit.Before; /** * @@ -33,22 +36,32 @@ import static org.junit.Assert.fail; */ public class CveDBMySqlIT extends BaseTest { + CveDB instance = null; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + instance = new CveDB(getSettings()); + } + + @After + @Override + public void tearDown() throws Exception { + instance.close(); + super.tearDown(); + } + /** * Pretty useless tests of open, commit, and close methods, of class CveDB. */ @Test public void testOpen() { - CveDB instance = null; try { - instance = CveDB.getInstance(); - } catch (DatabaseException ex) { + instance.commit(); + } catch (SQLException | DatabaseException ex) { System.out.println("Unable to connect to the My SQL database; verify that the db server is running and that the schema has been generated"); fail(ex.getMessage()); - } finally { - int start = instance.getUsageCount(); - instance.close(); - int end = instance.getUsageCount(); - assertTrue( end < start); } } @@ -57,7 +70,6 @@ public class CveDBMySqlIT extends BaseTest { */ @Test public void testGetCPEs() throws Exception { - CveDB instance = CveDB.getInstance(); try { String vendor = "apache"; String product = "struts"; @@ -66,8 +78,6 @@ public class CveDBMySqlIT extends BaseTest { } catch (Exception ex) { System.out.println("Unable to access the My SQL database; verify that the db server is running and that the schema has been generated"); throw ex; - } finally { - instance.close(); } } @@ -77,15 +87,12 @@ public class CveDBMySqlIT extends BaseTest { @Test public void testGetVulnerabilities() throws Exception { String cpeStr = "cpe:/a:apache:struts:2.1.2"; - CveDB instance = CveDB.getInstance(); try { List result = instance.getVulnerabilities(cpeStr); assertTrue(result.size() > 5); } catch (Exception ex) { System.out.println("Unable to access the My SQL database; verify that the db server is running and that the schema has been generated"); throw ex; - } finally { - instance.close(); } } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/DatabasePropertiesIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/DatabasePropertiesIT.java index 1d4b9ac1b..aa54294b1 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/DatabasePropertiesIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/DatabasePropertiesIT.java @@ -19,14 +19,13 @@ package org.owasp.dependencycheck.data.nvdcve; import org.owasp.dependencycheck.BaseDBTestCase; import java.util.Properties; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import org.junit.After; import org.junit.Test; import org.owasp.dependencycheck.data.update.nvd.NvdCveInfo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import org.junit.Before; /** * @@ -34,17 +33,31 @@ import static org.junit.Assert.assertTrue; */ public class DatabasePropertiesIT extends BaseDBTestCase { + CveDB cveDb = null; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + cveDb = new CveDB(getSettings()); + } + + @After + @Override + public void tearDown() throws Exception { + cveDb.close(); + super.tearDown(); + } + /** * Test of isEmpty method, of class DatabaseProperties. */ @Test public void testIsEmpty() throws Exception { - CveDB cveDB = CveDB.getInstance(); - DatabaseProperties instance = cveDB.getDatabaseProperties(); - assertNotNull(instance); + DatabaseProperties prop = cveDb.getDatabaseProperties(); + assertNotNull(prop); //no exception means the call worked... whether or not it is empty depends on if the db is new //assertEquals(expResult, result); - cveDB.close(); } /** @@ -57,13 +70,11 @@ public class DatabasePropertiesIT extends BaseDBTestCase { long expected = 1337; updatedValue.setId(key); updatedValue.setTimestamp(expected); - CveDB cveDB = CveDB.getInstance(); - DatabaseProperties instance = cveDB.getDatabaseProperties(); + DatabaseProperties instance = cveDb.getDatabaseProperties(); instance.save(updatedValue); - instance = cveDB.reloadProperties(); + instance = cveDb.reloadProperties(); long results = Long.parseLong(instance.getProperty("NVD CVE " + key)); assertEquals(expected, results); - cveDB.close(); } /** @@ -73,12 +84,10 @@ public class DatabasePropertiesIT extends BaseDBTestCase { public void testGetProperty_String_String() throws Exception { String key = "doesn't exist"; String defaultValue = "default"; - CveDB cveDB = CveDB.getInstance(); - DatabaseProperties instance = cveDB.getDatabaseProperties(); + DatabaseProperties instance = cveDb.getDatabaseProperties(); String expResult = "default"; String result = instance.getProperty(key, defaultValue); assertEquals(expResult, result); - cveDB.close(); } /** @@ -87,13 +96,11 @@ public class DatabasePropertiesIT extends BaseDBTestCase { @Test public void testGetProperty_String() throws DatabaseException { String key = "version"; - CveDB cveDB = CveDB.getInstance(); - DatabaseProperties instance = cveDB.getDatabaseProperties(); + DatabaseProperties instance = cveDb.getDatabaseProperties(); String result = instance.getProperty(key); double version = Double.parseDouble(result); assertTrue(version >= 2.8); assertTrue(version <= 10); - cveDB.close(); } /** @@ -101,10 +108,9 @@ public class DatabasePropertiesIT extends BaseDBTestCase { */ @Test public void testGetProperties() throws DatabaseException { - CveDB cveDB = CveDB.getInstance(); - DatabaseProperties instance = cveDB.getDatabaseProperties(); + DatabaseProperties instance = cveDb.getDatabaseProperties(); Properties result = instance.getProperties(); assertTrue(result.size() > 0); - cveDB.close(); + cveDb.close(); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/EngineVersionCheckTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/EngineVersionCheckTest.java index 600b8cd0d..78991f6a8 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/EngineVersionCheckTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/EngineVersionCheckTest.java @@ -66,31 +66,31 @@ public class EngineVersionCheckTest extends BaseTest { public void testShouldUpdate() throws Exception { DatabaseProperties properties = new MockUp() { final private Properties properties = new Properties(); - + @Mock public void save(String key, String value) throws UpdateException { properties.setProperty(key, value); } - + @Mock public String getProperty(String key) { return properties.getProperty(key); } - + }.getMockInstance(); - + String updateToVersion = "1.2.6"; String currentVersion = "1.2.6"; - + long lastChecked = dateToMilliseconds("2014-12-01"); long now = dateToMilliseconds("2014-12-01"); - - EngineVersionCheck instance = new EngineVersionCheck(); + + EngineVersionCheck instance = new EngineVersionCheck(getSettings()); boolean expResult = false; instance.setUpdateToVersion(updateToVersion); boolean result = instance.shouldUpdate(lastChecked, now, properties, currentVersion); assertEquals(expResult, result); - + updateToVersion = "1.2.5"; currentVersion = "1.2.5"; lastChecked = dateToMilliseconds("2014-10-01"); @@ -109,7 +109,7 @@ public class EngineVersionCheckTest extends BaseTest { instance.setUpdateToVersion(updateToVersion); result = instance.shouldUpdate(lastChecked, now, properties, currentVersion); assertEquals(expResult, result); - + updateToVersion = "1.2.6"; currentVersion = "1.2.5"; lastChecked = dateToMilliseconds("2014-12-01"); @@ -118,7 +118,7 @@ public class EngineVersionCheckTest extends BaseTest { instance.setUpdateToVersion(updateToVersion); result = instance.shouldUpdate(lastChecked, now, properties, currentVersion); assertEquals(expResult, result); - + updateToVersion = "1.2.5"; currentVersion = "1.2.6"; lastChecked = dateToMilliseconds("2014-12-01"); @@ -127,7 +127,7 @@ public class EngineVersionCheckTest extends BaseTest { instance.setUpdateToVersion(updateToVersion); result = instance.shouldUpdate(lastChecked, now, properties, currentVersion); assertEquals(expResult, result); - + updateToVersion = ""; currentVersion = "1.2.5"; lastChecked = dateToMilliseconds("2014-12-01"); @@ -136,7 +136,7 @@ public class EngineVersionCheckTest extends BaseTest { instance.setUpdateToVersion(updateToVersion); result = instance.shouldUpdate(lastChecked, now, properties, currentVersion); assertEquals(expResult, result); - + updateToVersion = ""; currentVersion = "1.2.5"; lastChecked = dateToMilliseconds("2014-12-01"); @@ -152,7 +152,7 @@ public class EngineVersionCheckTest extends BaseTest { */ @Test public void testGetCurrentReleaseVersion() { - EngineVersionCheck instance = new EngineVersionCheck(); + EngineVersionCheck instance = new EngineVersionCheck(getSettings()); DependencyVersion minExpResult = new DependencyVersion("1.2.6"); String release = instance.getCurrentReleaseVersion(); DependencyVersion result = new DependencyVersion(release); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/NvdCveUpdaterIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/NvdCveUpdaterIT.java index 2d398d02d..cf987ad39 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/NvdCveUpdaterIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/NvdCveUpdaterIT.java @@ -17,14 +17,13 @@ */ package org.owasp.dependencycheck.data.update; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; import org.junit.Test; import org.owasp.dependencycheck.BaseDBTestCase; import org.owasp.dependencycheck.data.update.exception.UpdateException; import org.owasp.dependencycheck.data.update.nvd.UpdateableNvdCve; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import org.owasp.dependencycheck.Engine; /** * @@ -32,31 +31,14 @@ import static org.junit.Assert.fail; */ public class NvdCveUpdaterIT extends BaseDBTestCase { - public NvdCveUpdater getUpdater() { - NvdCveUpdater instance = new NvdCveUpdater(); - instance.initializeExecutorServices(); - return instance; - } - - /** - * Test of update method. - */ - @Test - public void testUpdate() { - try { - NvdCveUpdater instance = getUpdater(); - instance.update(); - } catch (UpdateException ex) { - fail(ex.getMessage()); - } - } - /** * Test of updatesNeeded method. */ @Test public void testUpdatesNeeded() throws Exception { - NvdCveUpdater instance = getUpdater(); + NvdCveUpdater instance = new NvdCveUpdater(); + instance.setSettings(getSettings()); + instance.initializeExecutorServices(); UpdateableNvdCve result = instance.getUpdatesNeeded(); assertNotNull(result); } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/nvd/DownloadTaskTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/nvd/DownloadTaskTest.java index 347cec97b..11cac7bef 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/nvd/DownloadTaskTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/nvd/DownloadTaskTest.java @@ -44,11 +44,11 @@ public class DownloadTaskTest extends BaseTest { NvdCveInfo cve = new NvdCveInfo(); cve.setId("modified"); cve.setNeedsUpdate(true); - cve.setUrl(Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL)); - cve.setOldSchemaVersionUrl(Settings.getString(Settings.KEYS.CVE_MODIFIED_12_URL)); + cve.setUrl(getSettings().getString(Settings.KEYS.CVE_MODIFIED_20_URL)); + cve.setOldSchemaVersionUrl(getSettings().getString(Settings.KEYS.CVE_MODIFIED_12_URL)); ExecutorService processExecutor = null; CveDB cveDB = null; - DownloadTask instance = new DownloadTask(cve, processExecutor, cveDB, Settings.getInstance()); + DownloadTask instance = new DownloadTask(cve, processExecutor, cveDB, getSettings()); Future result = instance.call(); assertNull(result); } @@ -62,6 +62,5 @@ public class DownloadTaskTest extends BaseTest { assertTrue(DownloadTask.isXml(f)); f = getResourceAsFile(this, "file.tar.gz"); assertFalse(DownloadTask.isXml(f)); - } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java index 762d7bed4..01ed98315 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/dependency/DependencyTest.java @@ -177,7 +177,7 @@ public class DependencyTest extends BaseTest { public void testSetIdentifiers() { Set identifiers = new HashSet<>(); Dependency instance = new Dependency(); - instance.setIdentifiers(identifiers); + instance.addIdentifiers(identifiers); assertNotNull(instance.getIdentifiers()); } @@ -203,57 +203,11 @@ public class DependencyTest extends BaseTest { @Test public void testGetEvidence() { Dependency instance = new Dependency(); - EvidenceCollection result = instance.getEvidence(); + Set result = instance.getEvidence(EvidenceType.VENDOR); assertNotNull(result); - } - - /** - * Test of getEvidenceUsed method, of class Dependency. - */ - @Test - public void testGetEvidenceUsed() { - Dependency instance = new Dependency(); - String expResult = "used"; - - instance.getProductEvidence().addEvidence("used", "used", "used", Confidence.HIGH); - instance.getProductEvidence().addEvidence("not", "not", "not", Confidence.MEDIUM); - for (Evidence e : instance.getProductEvidence().iterator(Confidence.HIGH)) { - e.getValue(); - } - - EvidenceCollection result = instance.getEvidenceUsed(); - - assertEquals(1, result.size()); - assertTrue(result.containsUsedString(expResult)); - } - - /** - * Test of getVendorEvidence method, of class Dependency. - */ - @Test - public void testGetVendorEvidence() { - Dependency instance = new Dependency(); - EvidenceCollection result = instance.getVendorEvidence(); + result = instance.getEvidence(EvidenceType.PRODUCT); assertNotNull(result); - } - - /** - * Test of getProductEvidence method, of class Dependency. - */ - @Test - public void testGetProductEvidence() { - Dependency instance = new Dependency(); - EvidenceCollection result = instance.getProductEvidence(); - assertNotNull(result); - } - - /** - * Test of getVersionEvidence method, of class Dependency. - */ - @Test - public void testGetVersionEvidence() { - Dependency instance = new Dependency(); - EvidenceCollection result = instance.getVersionEvidence(); + result = instance.getEvidence(EvidenceType.VERSION); assertNotNull(result); } @@ -265,10 +219,8 @@ public class DependencyTest extends BaseTest { Dependency instance = new Dependency(); MavenArtifact mavenArtifact = new MavenArtifact("group", "artifact", "version", "url"); instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH); - assertTrue(instance.getEvidence().contains(Confidence.HIGH)); - assertFalse(instance.getEvidence().getEvidence("pom", "groupid").isEmpty()); - assertFalse(instance.getEvidence().getEvidence("pom", "artifactid").isEmpty()); - assertFalse(instance.getEvidence().getEvidence("pom", "version").isEmpty()); + assertTrue(instance.contains(EvidenceType.VENDOR, Confidence.HIGH)); + assertTrue(instance.size() > 1); assertFalse(instance.getIdentifiers().isEmpty()); } @@ -280,10 +232,8 @@ public class DependencyTest extends BaseTest { Dependency instance = new Dependency(); MavenArtifact mavenArtifact = new MavenArtifact(null, null, null, null); instance.addAsEvidence("pom", mavenArtifact, Confidence.HIGH); - assertFalse(instance.getEvidence().contains(Confidence.HIGH)); - assertTrue(instance.getEvidence().getEvidence("pom", "groupid").isEmpty()); - assertTrue(instance.getEvidence().getEvidence("pom", "artifactid").isEmpty()); - assertTrue(instance.getEvidence().getEvidence("pom", "version").isEmpty()); + assertFalse(instance.getEvidence(EvidenceType.VENDOR).contains(Confidence.HIGH)); + assertTrue(instance.size() == 0); assertTrue(instance.getIdentifiers().isEmpty()); } } diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIT.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIT.java index 68620672b..a718fd3d1 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIT.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIT.java @@ -58,7 +58,7 @@ public class ReportGeneratorIT extends BaseDBTestCase { File writeTo = new File("target/test-reports/Report.xml"); File suppressionFile = BaseTest.getResourceAsFile(this, "incorrectSuppressions.xml"); - Settings.setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile.getAbsolutePath()); + getSettings().setString(Settings.KEYS.SUPPRESSION_FILE, suppressionFile.getAbsolutePath()); //File struts = new File(this.getClass().getClassLoader().getResource("struts2-core-2.1.2.jar").getPath()); File struts = BaseTest.getResourceAsFile(this, "struts2-core-2.1.2.jar"); @@ -67,17 +67,14 @@ public class ReportGeneratorIT extends BaseDBTestCase { //File jetty = new File(this.getClass().getClassLoader().getResource("org.mortbay.jetty.jar").getPath()); File jetty = BaseTest.getResourceAsFile(this, "org.mortbay.jetty.jar"); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Engine engine = new Engine(); - - engine.scan(struts); - engine.scan(axis); - engine.scan(jetty); - engine.analyzeDependencies(); - engine.writeReports("Test Report", "org.owasp", "dependency-check-core", "1.4.7", writeTo, "XML"); - - engine.cleanup(); - + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + try (Engine engine = new Engine(getSettings())) { + engine.scan(struts); + engine.scan(axis); + engine.scan(jetty); + engine.analyzeDependencies(); + engine.writeReports("Test Report", "org.owasp", "dependency-check-core", "1.4.7", writeTo, "XML"); + } InputStream xsdStream = ReportGenerator.class.getClassLoader().getResourceAsStream("schema/dependency-check.1.6.xsd"); StreamSource xsdSource = new StreamSource(xsdStream); StreamSource xmlSource = new StreamSource(writeTo); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/xml/hints/HintParserTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/xml/hints/HintParserTest.java index 7c6432cc4..6d63e970d 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/xml/hints/HintParserTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/xml/hints/HintParserTest.java @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.xml.hints; import java.io.File; import java.io.InputStream; +import java.util.List; import org.junit.Test; import static org.junit.Assert.assertEquals; import org.owasp.dependencycheck.BaseTest; @@ -36,27 +37,29 @@ public class HintParserTest extends BaseTest { public void testParseHints_File() throws Exception { File file = BaseTest.getResourceAsFile(this, "hints.xml"); HintParser instance = new HintParser(); - Hints results = instance.parseHints(file); - assertEquals("Two duplicating hints should have been read", 2, results.getVendorDuplicatingHintRules().size()); - assertEquals("Two hint rules should have been read", 2, results.getHintRules().size()); + instance.parseHints(file); + List hintRules = instance.getHintRules(); + List vendorRules = instance.getVendorDuplicatingHintRules(); + assertEquals("Two duplicating hints should have been read", 2, vendorRules.size()); + assertEquals("Two hint rules should have been read", 2, hintRules.size()); - assertEquals("One add product should have been read", 1, results.getHintRules().get(0).getAddProduct().size()); - assertEquals("One add vendor should have been read", 1, results.getHintRules().get(0).getAddVendor().size()); - assertEquals("Two file name should have been read", 2, results.getHintRules().get(1).getFilenames().size()); + assertEquals("One add product should have been read", 1, hintRules.get(0).getAddProduct().size()); + assertEquals("One add vendor should have been read", 1, hintRules.get(0).getAddVendor().size()); + assertEquals("Two file name should have been read", 2, hintRules.get(1).getFilenames().size()); - assertEquals("add product name not found", "add product name", results.getHintRules().get(0).getAddProduct().get(0).getName()); - assertEquals("add vendor name not found", "add vendor name", results.getHintRules().get(0).getAddVendor().get(0).getName()); - assertEquals("given product name not found", "given product name", results.getHintRules().get(0).getGivenProduct().get(0).getName()); - assertEquals("given vendor name not found", "given vendor name", results.getHintRules().get(0).getGivenVendor().get(0).getName()); + assertEquals("add product name not found", "add product name", hintRules.get(0).getAddProduct().get(0).getName()); + assertEquals("add vendor name not found", "add vendor name", hintRules.get(0).getAddVendor().get(0).getName()); + assertEquals("given product name not found", "given product name", hintRules.get(0).getGivenProduct().get(0).getName()); + assertEquals("given vendor name not found", "given vendor name", hintRules.get(0).getGivenVendor().get(0).getName()); - assertEquals("spring file name not found", "spring", results.getHintRules().get(1).getFilenames().get(0).getValue()); - assertEquals("file name 1 should not be case sensitive", false, results.getHintRules().get(1).getFilenames().get(0).isCaseSensitive()); - assertEquals("file name 1 should not be a regex", false, results.getHintRules().get(1).getFilenames().get(0).isRegex()); - assertEquals("file name 2 should be case sensitive", true, results.getHintRules().get(1).getFilenames().get(1).isCaseSensitive()); - assertEquals("file name 2 should be a regex", true, results.getHintRules().get(1).getFilenames().get(1).isRegex()); - - assertEquals("sun duplicating vendor", "sun", results.getVendorDuplicatingHintRules().get(0).getValue()); - assertEquals("sun duplicates vendor oracle", "oracle", results.getVendorDuplicatingHintRules().get(0).getDuplicate()); + assertEquals("spring file name not found", "spring", hintRules.get(1).getFilenames().get(0).getValue()); + assertEquals("file name 1 should not be case sensitive", false, hintRules.get(1).getFilenames().get(0).isCaseSensitive()); + assertEquals("file name 1 should not be a regex", false, hintRules.get(1).getFilenames().get(0).isRegex()); + assertEquals("file name 2 should be case sensitive", true, hintRules.get(1).getFilenames().get(1).isCaseSensitive()); + assertEquals("file name 2 should be a regex", true, hintRules.get(1).getFilenames().get(1).isRegex()); + + assertEquals("sun duplicating vendor", "sun", vendorRules.get(0).getValue()); + assertEquals("sun duplicates vendor oracle", "oracle", vendorRules.get(0).getDuplicate()); } /** @@ -66,45 +69,47 @@ public class HintParserTest extends BaseTest { public void testParseHints_InputStream() throws Exception { InputStream ins = BaseTest.getResourceAsStream(this, "hints_12.xml"); HintParser instance = new HintParser(); - Hints results = instance.parseHints(ins); - assertEquals("Zero duplicating hints should have been read", 0, results.getVendorDuplicatingHintRules().size()); - assertEquals("Two hint rules should have been read", 2, results.getHintRules().size()); + instance.parseHints(ins); + List hintRules = instance.getHintRules(); + List vendorRules = instance.getVendorDuplicatingHintRules(); + assertEquals("Zero duplicating hints should have been read", 0, vendorRules.size()); + assertEquals("Two hint rules should have been read", 2, hintRules.size()); - assertEquals("One given product should have been read in hint 0", 1, results.getHintRules().get(0).getGivenProduct().size()); - assertEquals("One given vendor should have been read in hint 0", 1, results.getHintRules().get(0).getGivenVendor().size()); - assertEquals("One given version should have been read in hint 0", 1, results.getHintRules().get(0).getGivenVersion().size()); - - assertEquals("One add product should have been read in hint 0", 1, results.getHintRules().get(0).getAddProduct().size()); - assertEquals("One add vendor should have been read in hint 0", 1, results.getHintRules().get(0).getAddVendor().size()); - assertEquals("One add version should have been read in hint 0", 1, results.getHintRules().get(0).getAddVersion().size()); - assertEquals("Zero remove product should have been read in hint 0", 0, results.getHintRules().get(0).getRemoveProduct().size()); - assertEquals("Zero remove vendor should have been read in hint 0", 0, results.getHintRules().get(0).getRemoveVendor().size()); - assertEquals("Zero remove version should have been read in hint 0", 0, results.getHintRules().get(0).getRemoveVersion().size()); - - assertEquals("Zero given product should have been read in hint 1", 0, results.getHintRules().get(1).getGivenProduct().size()); - assertEquals("Zero given vendor should have been read in hint 1", 0, results.getHintRules().get(1).getGivenVendor().size()); - assertEquals("One given version should have been read in hint 1", 1, results.getHintRules().get(1).getGivenVersion().size()); - - assertEquals("One remove product should have been read in hint 1", 1, results.getHintRules().get(1).getRemoveProduct().size()); - assertEquals("One remove vendor should have been read in hint 1", 1, results.getHintRules().get(1).getRemoveVendor().size()); - assertEquals("One remove version should have been read in hint 1", 1, results.getHintRules().get(1).getRemoveVersion().size()); - assertEquals("Zero add product should have been read in hint 1", 0, results.getHintRules().get(1).getAddProduct().size()); - assertEquals("Zero add vendor should have been read in hint 1", 0, results.getHintRules().get(1).getAddVendor().size()); - assertEquals("Zero add version should have been read in hint 1", 0, results.getHintRules().get(1).getAddVersion().size()); + assertEquals("One given product should have been read in hint 0", 1, hintRules.get(0).getGivenProduct().size()); + assertEquals("One given vendor should have been read in hint 0", 1, hintRules.get(0).getGivenVendor().size()); + assertEquals("One given version should have been read in hint 0", 1, hintRules.get(0).getGivenVersion().size()); - assertEquals("add product name not found in hint 0", "add product name", results.getHintRules().get(0).getAddProduct().get(0).getName()); - assertEquals("add vendor name not found in hint 0", "add vendor name", results.getHintRules().get(0).getAddVendor().get(0).getName()); - assertEquals("add version name not found in hint 0", "add version name", results.getHintRules().get(0).getAddVersion().get(0).getName()); - - assertEquals("given product name not found in hint 0", "given product name", results.getHintRules().get(0).getGivenProduct().get(0).getName()); - assertEquals("given vendor name not found in hint 0", "given vendor name", results.getHintRules().get(0).getGivenVendor().get(0).getName()); - assertEquals("given version name not found in hint 0", "given version name", results.getHintRules().get(0).getGivenVersion().get(0).getName()); + assertEquals("One add product should have been read in hint 0", 1, hintRules.get(0).getAddProduct().size()); + assertEquals("One add vendor should have been read in hint 0", 1, hintRules.get(0).getAddVendor().size()); + assertEquals("One add version should have been read in hint 0", 1, hintRules.get(0).getAddVersion().size()); + assertEquals("Zero remove product should have been read in hint 0", 0, hintRules.get(0).getRemoveProduct().size()); + assertEquals("Zero remove vendor should have been read in hint 0", 0, hintRules.get(0).getRemoveVendor().size()); + assertEquals("Zero remove version should have been read in hint 0", 0, hintRules.get(0).getRemoveVersion().size()); - assertEquals("given version name not found in hint 1", "given version name", results.getHintRules().get(1).getGivenVersion().get(0).getName()); + assertEquals("Zero given product should have been read in hint 1", 0, hintRules.get(1).getGivenProduct().size()); + assertEquals("Zero given vendor should have been read in hint 1", 0, hintRules.get(1).getGivenVendor().size()); + assertEquals("One given version should have been read in hint 1", 1, hintRules.get(1).getGivenVersion().size()); + + assertEquals("One remove product should have been read in hint 1", 1, hintRules.get(1).getRemoveProduct().size()); + assertEquals("One remove vendor should have been read in hint 1", 1, hintRules.get(1).getRemoveVendor().size()); + assertEquals("One remove version should have been read in hint 1", 1, hintRules.get(1).getRemoveVersion().size()); + assertEquals("Zero add product should have been read in hint 1", 0, hintRules.get(1).getAddProduct().size()); + assertEquals("Zero add vendor should have been read in hint 1", 0, hintRules.get(1).getAddVendor().size()); + assertEquals("Zero add version should have been read in hint 1", 0, hintRules.get(1).getAddVersion().size()); + + assertEquals("add product name not found in hint 0", "add product name", hintRules.get(0).getAddProduct().get(0).getName()); + assertEquals("add vendor name not found in hint 0", "add vendor name", hintRules.get(0).getAddVendor().get(0).getName()); + assertEquals("add version name not found in hint 0", "add version name", hintRules.get(0).getAddVersion().get(0).getName()); + + assertEquals("given product name not found in hint 0", "given product name", hintRules.get(0).getGivenProduct().get(0).getName()); + assertEquals("given vendor name not found in hint 0", "given vendor name", hintRules.get(0).getGivenVendor().get(0).getName()); + assertEquals("given version name not found in hint 0", "given version name", hintRules.get(0).getGivenVersion().get(0).getName()); + + assertEquals("given version name not found in hint 1", "given version name", hintRules.get(1).getGivenVersion().get(0).getName()); + + assertEquals("add product name not found in hint 1", "remove product name", hintRules.get(1).getRemoveProduct().get(0).getName()); + assertEquals("add vendor name not found in hint 1", "remove vendor name", hintRules.get(1).getRemoveVendor().get(0).getName()); + assertEquals("add version name not found in hint 1", "remove version name", hintRules.get(1).getRemoveVersion().get(0).getName()); - assertEquals("add product name not found in hint 1", "remove product name", results.getHintRules().get(1).getRemoveProduct().get(0).getName()); - assertEquals("add vendor name not found in hint 1", "remove vendor name", results.getHintRules().get(1).getRemoveVendor().get(0).getName()); - assertEquals("add version name not found in hint 1", "remove version name", results.getHintRules().get(1).getRemoveVersion().get(0).getName()); - } } diff --git a/dependency-check-core/src/test/resources/dependencycheck.properties b/dependency-check-core/src/test/resources/dependencycheck.properties index 1bdd122ea..d48120fa1 100644 --- a/dependency-check-core/src/test/resources/dependencycheck.properties +++ b/dependency-check-core/src/test/resources/dependencycheck.properties @@ -18,7 +18,7 @@ data.directory=[JAR]/data #if the filename has a %s it will be replaced with the current expected version data.file_name=dc.h2.db data.version=3.0 -data.connection_string=jdbc:h2:file:%s;MV_STORE=FALSE;AUTOCOMMIT=ON;LOCK_MODE=0;FILE_LOCK=NO +data.connection_string=jdbc:h2:file:%s;MV_STORE=FALSE;AUTOCOMMIT=ON; #data.connection_string=jdbc:mysql://localhost:3306/dependencycheck # user name and password for the database connection. The inherent case is to use H2. diff --git a/dependency-check-maven/pom.xml b/dependency-check-maven/pom.xml index 814e1410f..40efcfffa 100644 --- a/dependency-check-maven/pom.xml +++ b/dependency-check-maven/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT dependency-check-maven maven-plugin @@ -231,12 +231,10 @@ Copyright (c) 2013 Jeremy Long. All Rights Reserved. - 4 - + ${project.build.directory}/it target/local-repo diff --git a/dependency-check-maven/src/it/617-hierarchical-cross-deps/invoker.properties b/dependency-check-maven/src/it/617-hierarchical-cross-deps/invoker.properties index 317af6f63..2954f90b7 100644 --- a/dependency-check-maven/src/it/617-hierarchical-cross-deps/invoker.properties +++ b/dependency-check-maven/src/it/617-hierarchical-cross-deps/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2014 Jeremy Long. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -e +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X diff --git a/dependency-check-maven/src/it/618-aggregator-purge/invoker.properties b/dependency-check-maven/src/it/618-aggregator-purge/invoker.properties index 854d0f155..46c174d8e 100644 --- a/dependency-check-maven/src/it/618-aggregator-purge/invoker.properties +++ b/dependency-check-maven/src/it/618-aggregator-purge/invoker.properties @@ -16,5 +16,5 @@ # Copyright (c) 2014 Jeremy Long. All Rights Reserved. # -invoker.goals.1 = ${project.groupId}:${project.artifactId}:${project.version}:update-only -DdataDirectory=./data -Dcve.startyear=2017 -invoker.goals.2 = ${project.groupId}:${project.artifactId}:${project.version}:purge -DdataDirectory=./data +invoker.goals.1 = ${project.groupId}:${project.artifactId}:${project.version}:update-only -DdataDirectory=./data -Dcve.startyear=2017 -X +invoker.goals.2 = ${project.groupId}:${project.artifactId}:${project.version}:purge -DdataDirectory=./data -X diff --git a/dependency-check-maven/src/it/618-aggregator-purge/postbuild.groovy b/dependency-check-maven/src/it/618-aggregator-purge/postbuild.groovy index bf3b3246e..82a6a5f04 100644 --- a/dependency-check-maven/src/it/618-aggregator-purge/postbuild.groovy +++ b/dependency-check-maven/src/it/618-aggregator-purge/postbuild.groovy @@ -21,7 +21,6 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; -// Analyse number of "Checking for updates" String log = FileUtils.readFileToString(new File(basedir, "build.log"), Charset.defaultCharset().name()); if (!StringUtils.contains(log, "Database file purged; local copy of the NVD has been removed")) { System.out.println("The database was not purged."); diff --git a/dependency-check-maven/src/it/618-aggregator-update-only/invoker.properties b/dependency-check-maven/src/it/618-aggregator-update-only/invoker.properties index 07d487144..55391a3fa 100644 --- a/dependency-check-maven/src/it/618-aggregator-update-only/invoker.properties +++ b/dependency-check-maven/src/it/618-aggregator-update-only/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2014 Jeremy Long. All Rights Reserved. # -invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:update-only \ No newline at end of file +invoker.goals = ${project.groupId}:${project.artifactId}:${project.version}:update-only -X \ No newline at end of file diff --git a/dependency-check-maven/src/it/618-aggregator-update-only/postbuild.groovy b/dependency-check-maven/src/it/618-aggregator-update-only/postbuild.groovy index a3aa62c8e..51a922015 100644 --- a/dependency-check-maven/src/it/618-aggregator-update-only/postbuild.groovy +++ b/dependency-check-maven/src/it/618-aggregator-update-only/postbuild.groovy @@ -22,7 +22,7 @@ import org.apache.commons.lang.StringUtils; // Analyse number of "Checking for updates" String log = FileUtils.readFileToString(new File(basedir, "build.log"), Charset.defaultCharset().name()); -int count = StringUtils.countMatches(log, "Checking for updates"); +int count = StringUtils.countMatches(log, "[INFO] Checking for updates"); if (count > 1){ System.out.println(String.format("The update should be unique, it is %s", count)); return false; diff --git a/dependency-check-maven/src/it/629-jackson-dataformat/invoker.properties b/dependency-check-maven/src/it/629-jackson-dataformat/invoker.properties index 3fc1810a0..693fb2637 100644 --- a/dependency-check-maven/src/it/629-jackson-dataformat/invoker.properties +++ b/dependency-check-maven/src/it/629-jackson-dataformat/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2014 Jeremy Long. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -e -Dformat=ALL +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X -Dformat=ALL diff --git a/dependency-check-maven/src/it/690-threadsafety/invoker.properties b/dependency-check-maven/src/it/690-threadsafety/invoker.properties index 697b15bf3..fe31f5769 100644 --- a/dependency-check-maven/src/it/690-threadsafety/invoker.properties +++ b/dependency-check-maven/src/it/690-threadsafety/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2014 Jeremy Long. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X -T 1 +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X -T 10 diff --git a/dependency-check-maven/src/it/729-system-scope-resolved/invoker.properties b/dependency-check-maven/src/it/729-system-scope-resolved/invoker.properties index 135fb86f1..aea97cbe8 100644 --- a/dependency-check-maven/src/it/729-system-scope-resolved/invoker.properties +++ b/dependency-check-maven/src/it/729-system-scope-resolved/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2017 Jeremy Long. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -e -Dformat=JSON +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X -Dformat=JSON diff --git a/dependency-check-maven/src/it/729-system-scope-skipped/invoker.properties b/dependency-check-maven/src/it/729-system-scope-skipped/invoker.properties index b41bc60f9..dcd17db7b 100644 --- a/dependency-check-maven/src/it/729-system-scope-skipped/invoker.properties +++ b/dependency-check-maven/src/it/729-system-scope-skipped/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2014 Jeremy Long. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -DskipSystemScope=true -Dformat=JSON +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -DskipSystemScope=true -Dformat=JSON -X diff --git a/dependency-check-maven/src/it/730-multiple-suppression-files-configs/invoker.properties b/dependency-check-maven/src/it/730-multiple-suppression-files-configs/invoker.properties index 9c2542d23..0cb61e623 100644 --- a/dependency-check-maven/src/it/730-multiple-suppression-files-configs/invoker.properties +++ b/dependency-check-maven/src/it/730-multiple-suppression-files-configs/invoker.properties @@ -15,4 +15,4 @@ # # Copyright (c) 2017 The OWASP Foundation. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X diff --git a/dependency-check-maven/src/it/730-multiple-suppression-files/invoker.properties b/dependency-check-maven/src/it/730-multiple-suppression-files/invoker.properties index 9c2542d23..0cb61e623 100644 --- a/dependency-check-maven/src/it/730-multiple-suppression-files/invoker.properties +++ b/dependency-check-maven/src/it/730-multiple-suppression-files/invoker.properties @@ -15,4 +15,4 @@ # # Copyright (c) 2017 The OWASP Foundation. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X diff --git a/dependency-check-maven/src/it/815-broken-suppression-aggregate/invoker.properties b/dependency-check-maven/src/it/815-broken-suppression-aggregate/invoker.properties index 8d65ccd95..60fe2e74c 100644 --- a/dependency-check-maven/src/it/815-broken-suppression-aggregate/invoker.properties +++ b/dependency-check-maven/src/it/815-broken-suppression-aggregate/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2017 The OWASP Foundation. All Rights Reserved. # invoker.buildResult = failure -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:aggregate +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:aggregate -X diff --git a/dependency-check-maven/src/it/846-site-plugin/invoker.properties b/dependency-check-maven/src/it/846-site-plugin/invoker.properties index aaf4883e4..5d0dc2b52 100644 --- a/dependency-check-maven/src/it/846-site-plugin/invoker.properties +++ b/dependency-check-maven/src/it/846-site-plugin/invoker.properties @@ -15,4 +15,4 @@ # # Copyright (c) 2017 The OWASP Foundation. All Rights Reserved. # -invoker.goals = site -Dodcversion=${project.version} +invoker.goals = site -Dodcversion=${project.version} -X diff --git a/dependency-check-maven/src/it/false-positives/invoker.properties b/dependency-check-maven/src/it/false-positives/invoker.properties index 3fc1810a0..693fb2637 100644 --- a/dependency-check-maven/src/it/false-positives/invoker.properties +++ b/dependency-check-maven/src/it/false-positives/invoker.properties @@ -16,4 +16,4 @@ # Copyright (c) 2014 Jeremy Long. All Rights Reserved. # -invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -e -Dformat=ALL +invoker.goals = install ${project.groupId}:${project.artifactId}:${project.version}:check -X -Dformat=ALL diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java index b64e0b489..303437b85 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/AggregateMojo.java @@ -34,7 +34,6 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.exception.ExceptionCollection; import org.owasp.dependencycheck.exception.ReportException; -import org.owasp.dependencycheck.utils.Settings; /** * Maven Plugin that checks project dependencies and the dependencies of all @@ -46,7 +45,7 @@ import org.owasp.dependencycheck.utils.Settings; name = "aggregate", defaultPhase = LifecyclePhase.VERIFY, aggregator = true, - threadSafe = false, + threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresOnline = true ) @@ -150,8 +149,8 @@ public class AggregateMojo extends BaseDependencyCheckMojo { if (exCol != null && this.isFailOnError()) { throw new MojoExecutionException("One or more exceptions occurred during dependency-check analysis", exCol); } - engine.cleanup(); - Settings.cleanup(); + engine.close(); + getSettings().cleanup(); } /** diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java index a55f77618..a20e81a80 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojo.java @@ -84,27 +84,35 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * A flag indicating whether or not the Maven site is being generated. */ private boolean generatingSite = false; + /** + * The configured settings. + */ + private Settings settings = null; // // /** * Sets whether or not the external report format should be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "metaFileName", defaultValue = "dependency-check.ser", required = true) private String dataFileName; /** * Sets whether or not the external report format should be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "failOnError", defaultValue = "true", required = true) private boolean failOnError; /** * The Maven Project Object. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "project", required = true, readonly = true) private MavenProject project; /** * List of Maven project of the current build */ + @SuppressWarnings("CanBeFinal") @Parameter(readonly = true, required = true, property = "reactorProjects") private List reactorProjects; /** @@ -112,18 +120,21 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * artifacts (handles both Maven 3.0 Sonatype and Maven 3.1+ eclipse Aether * implementations). */ + @SuppressWarnings("CanBeFinal") @Component private ArtifactResolver artifactResolver; /** * The Maven Session. */ + @SuppressWarnings("CanBeFinal") @Parameter(defaultValue = "${session}", readonly = true, required = true) private MavenSession session; /** * Remote repositories which will be searched for artifacts. */ + @SuppressWarnings("CanBeFinal") @Parameter(defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true) private List remoteRepositories; @@ -136,6 +147,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * The output directory. This generally maps to "target". */ + @SuppressWarnings("CanBeFinal") @Parameter(defaultValue = "${project.build.directory}", required = true) private File outputDirectory; /** @@ -162,11 +174,13 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not * recommended that this be turned to false. Default is true. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "autoUpdate") private Boolean autoUpdate; /** * Sets whether Experimental analyzers are enabled. Default is false. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "enableExperimental") private Boolean enableExperimental; /** @@ -188,33 +202,39 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * The Maven settings. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "mavenSettings", defaultValue = "${settings}", required = false) private org.apache.maven.settings.Settings mavenSettings; /** * The maven settings proxy id. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "mavenSettingsProxyId", required = false) private String mavenSettingsProxyId; /** * The Connection Timeout. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "connectionTimeout", defaultValue = "", required = false) private String connectionTimeout; /** * The paths to the suppression files. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "suppressionFiles", required = false) private String[] suppressionFiles; /** * The paths to the suppression file. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "suppressionFile", required = false) private String suppressionFile; /** * The path to the hints file. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "hintsFile", defaultValue = "", required = false) private String hintsFile; @@ -228,18 +248,21 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * Whether or not the Jar Analyzer is enabled. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "jarAnalyzerEnabled", required = false) private Boolean jarAnalyzerEnabled; /** * Whether or not the Archive Analyzer is enabled. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "archiveAnalyzerEnabled", required = false) private Boolean archiveAnalyzerEnabled; /** * Sets whether the Python Distribution Analyzer will be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "pyDistributionAnalyzerEnabled", required = false) private Boolean pyDistributionAnalyzerEnabled; /** @@ -250,21 +273,25 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * Sets whether the Ruby Gemspec Analyzer will be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "rubygemsAnalyzerEnabled", required = false) private Boolean rubygemsAnalyzerEnabled; /** * Sets whether or not the openssl Analyzer should be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "opensslAnalyzerEnabled", required = false) private Boolean opensslAnalyzerEnabled; /** * Sets whether or not the CMake Analyzer should be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "cmakeAnalyzerEnabled", required = false) private Boolean cmakeAnalyzerEnabled; /** * Sets whether or not the autoconf Analyzer should be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "autoconfAnalyzerEnabled", required = false) private Boolean autoconfAnalyzerEnabled; /** @@ -275,11 +302,13 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * Sets whether or not the Node.js Analyzer should be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "nodeAnalyzerEnabled", required = false) private Boolean nodeAnalyzerEnabled; /** * Sets whether or not the Node Security Project Analyzer should be used. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "nspAnalyzerEnabled", required = false) private Boolean nspAnalyzerEnabled; @@ -292,18 +321,21 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * Whether or not the .NET Nuspec Analyzer is enabled. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "nuspecAnalyzerEnabled", required = false) private Boolean nuspecAnalyzerEnabled; /** * Whether or not the Central Analyzer is enabled. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "centralAnalyzerEnabled", required = false) private Boolean centralAnalyzerEnabled; /** * Whether or not the Nexus Analyzer is enabled. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "nexusAnalyzerEnabled", required = false) private Boolean nexusAnalyzerEnabled; @@ -316,18 +348,21 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * Sets the path for the bundle-audit binary. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "bundleAuditPath", defaultValue = "", required = false) private String bundleAuditPath; /** * Whether or not the CocoaPods Analyzer is enabled. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "cocoapodsAnalyzerEnabled", required = false) private Boolean cocoapodsAnalyzerEnabled; /** * Whether or not the Swift package Analyzer is enabled. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "swiftPackageManagerAnalyzerEnabled", required = false) private Boolean swiftPackageManagerAnalyzerEnabled; @@ -335,38 +370,45 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * The URL of a Nexus server's REST API end point * (http://domain/nexus/service/local). */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "nexusUrl", required = false) private String nexusUrl; /** * Whether or not the configured proxy is used to connect to Nexus. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "nexusUsesProxy", required = false) private Boolean nexusUsesProxy; /** * The database connection string. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "connectionString", defaultValue = "", required = false) private String connectionString; /** * The database driver name. An example would be org.h2.Driver. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "databaseDriverName", defaultValue = "", required = false) private String databaseDriverName; /** * The path to the database driver if it is not on the class path. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "databaseDriverPath", defaultValue = "", required = false) private String databaseDriverPath; /** * The server id in the settings.xml; used to retrieve encrypted passwords * from the settings.xml. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "serverId", defaultValue = "", required = false) private String serverId; /** * A reference to the settings.xml settings. */ + @SuppressWarnings("CanBeFinal") @Parameter(defaultValue = "${settings}", readonly = true, required = true) private org.apache.maven.settings.Settings settingsXml; /** @@ -388,6 +430,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * A comma-separated list of file extensions to add to analysis next to jar, * zip, .... */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "zipExtensions", required = false) private String zipExtensions; /** @@ -433,38 +476,45 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma /** * The data directory, hold DC SQL DB. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "dataDirectory", defaultValue = "", required = false) private String dataDirectory; /** * Data Mirror URL for CVE 1.2. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "cveUrl12Modified", defaultValue = "", required = false) private String cveUrl12Modified; /** * Data Mirror URL for CVE 2.0. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "cveUrl20Modified", defaultValue = "", required = false) private String cveUrl20Modified; /** * Base Data Mirror URL for CVE 1.2. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "cveUrl12Base", defaultValue = "", required = false) private String cveUrl12Base; /** * Data Mirror URL for CVE 2.0. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "cveUrl20Base", defaultValue = "", required = false) private String cveUrl20Base; /** * Optionally skip excessive CVE update checks for a designated duration in * hours. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "cveValidForHours", defaultValue = "", required = false) private Integer cveValidForHours; /** * The path to mono for .NET Assembly analysis on non-windows systems. */ + @SuppressWarnings("CanBeFinal") @Parameter(property = "pathToMono", defaultValue = "", required = false) private String pathToMono; @@ -936,7 +986,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma */ protected Engine initializeEngine() throws DatabaseException { populateSettings(); - return new Engine(); + return new Engine(settings); } /** @@ -945,11 +995,11 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * proxy url, port, and connection timeout. */ protected void populateSettings() { - Settings.initialize(); + settings = new Settings(); InputStream mojoProperties = null; try { mojoProperties = this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE); - Settings.mergeProperties(mojoProperties); + settings.mergeProperties(mojoProperties); } catch (IOException ex) { getLog().warn("Unable to load the dependency-check ant task.properties file."); if (getLog().isDebugEnabled()) { @@ -966,9 +1016,9 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma } } } - Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate); + settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_EXPERIMENTAL_ENABLED, enableExperimental); if (externalReport != null) { getLog().warn("The 'externalReport' option was set; this configuration option has been removed. " @@ -980,56 +1030,57 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma } final Proxy proxy = getMavenProxy(); if (proxy != null) { - Settings.setString(Settings.KEYS.PROXY_SERVER, proxy.getHost()); - Settings.setString(Settings.KEYS.PROXY_PORT, Integer.toString(proxy.getPort())); + settings.setString(Settings.KEYS.PROXY_SERVER, proxy.getHost()); + settings.setString(Settings.KEYS.PROXY_PORT, Integer.toString(proxy.getPort())); final String userName = proxy.getUsername(); final String password = proxy.getPassword(); - Settings.setStringIfNotNull(Settings.KEYS.PROXY_USERNAME, userName); - Settings.setStringIfNotNull(Settings.KEYS.PROXY_PASSWORD, password); - Settings.setStringIfNotNull(Settings.KEYS.PROXY_NON_PROXY_HOSTS, proxy.getNonProxyHosts()); + settings.setStringIfNotNull(Settings.KEYS.PROXY_USERNAME, userName); + settings.setStringIfNotNull(Settings.KEYS.PROXY_PASSWORD, password); + settings.setStringIfNotNull(Settings.KEYS.PROXY_NON_PROXY_HOSTS, proxy.getNonProxyHosts()); } final String[] suppressions = determineSuppressions(); - Settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressions); + settings.setArrayIfNotEmpty(Settings.KEYS.SUPPRESSION_FILE, suppressions); - Settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); - Settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile); + settings.setStringIfNotEmpty(Settings.KEYS.CONNECTION_TIMEOUT, connectionTimeout); + settings.setStringIfNotEmpty(Settings.KEYS.HINTS_FILE, hintsFile); //File Type Analyzer Settings - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled); - Settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions); - Settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_JAR_ENABLED, jarAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NUSPEC_ENABLED, nuspecAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CENTRAL_ENABLED, centralAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_ENABLED, nexusAnalyzerEnabled); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_NEXUS_URL, nexusUrl); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NEXUS_USES_PROXY, nexusUsesProxy); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED, assemblyAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, archiveAnalyzerEnabled); + settings.setStringIfNotEmpty(Settings.KEYS.ADDITIONAL_ZIP_EXTENSIONS, zipExtensions); + settings.setStringIfNotEmpty(Settings.KEYS.ANALYZER_ASSEMBLY_MONO_PATH, pathToMono); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, rubygemsAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, opensslAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CMAKE_ENABLED, cmakeAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, autoconfAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED, nspAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, bundleAuditAnalyzerEnabled); - Settings.setStringIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, bundleAuditPath); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED, cocoapodsAnalyzerEnabled); - Settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED, swiftPackageManagerAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED, pyDistributionAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED, pyPackageAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED, rubygemsAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_OPENSSL_ENABLED, opensslAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_CMAKE_ENABLED, cmakeAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_AUTOCONF_ENABLED, autoconfAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED, composerAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED, nodeAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_NSP_PACKAGE_ENABLED, nspAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED, bundleAuditAnalyzerEnabled); + settings.setStringIfNotNull(Settings.KEYS.ANALYZER_BUNDLE_AUDIT_PATH, bundleAuditPath); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_COCOAPODS_ENABLED, cocoapodsAnalyzerEnabled); + settings.setBooleanIfNotNull(Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED, swiftPackageManagerAnalyzerEnabled); //Database configuration - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); + settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_NAME, databaseDriverName); + settings.setStringIfNotEmpty(Settings.KEYS.DB_DRIVER_PATH, databaseDriverPath); + settings.setStringIfNotEmpty(Settings.KEYS.DB_CONNECTION_STRING, connectionString); if (databaseUser == null && databasePassword == null && serverId != null) { final Server server = settingsXml.getServer(serverId); if (server != null) { databaseUser = server.getUsername(); try { + //CSOFF: LineLength //The following fix was copied from: // https://github.com/bsorrentino/maven-confluence-plugin/blob/master/maven-confluence-reporting-plugin/src/main/java/org/bsc/maven/confluence/plugin/AbstractBaseConfluenceMojo.java // @@ -1037,6 +1088,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma // org.sonatype.plexus.components.sec.dispatcher.SecDispatcherException: // java.io.FileNotFoundException: ~/.settings-security.xml (No such file or directory) // + //CSON: LineLength if (securityDispatcher instanceof DefaultSecDispatcher) { ((DefaultSecDispatcher) securityDispatcher).setConfigurationFile("~/.m2/settings-security.xml"); } @@ -1065,15 +1117,15 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma } } - Settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); - Settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); - Settings.setStringIfNotEmpty(Settings.KEYS.DATA_DIRECTORY, dataDirectory); + settings.setStringIfNotEmpty(Settings.KEYS.DB_USER, databaseUser); + settings.setStringIfNotEmpty(Settings.KEYS.DB_PASSWORD, databasePassword); + settings.setStringIfNotEmpty(Settings.KEYS.DATA_DIRECTORY, dataDirectory); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base); - Settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base); - Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_12_URL, cveUrl12Modified); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_MODIFIED_20_URL, cveUrl20Modified); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_1_2, cveUrl12Base); + settings.setStringIfNotEmpty(Settings.KEYS.CVE_SCHEMA_2_0, cveUrl20Base); + settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); artifactScopeExcluded = new ArtifactScopeExcluded(skipTestScope, skipProvidedScope, skipSystemScope, skipRuntimeScope); artifactTypeExcluded = new ArtifactTypeExcluded(skipArtifactType); @@ -1166,6 +1218,15 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma return artifactScopeExcluded; } + /** + * Returns the configured settings. + * + * @return the configured settings + */ + protected Settings getSettings() { + return settings; + } + // /** * Checks to see if a vulnerability has been identified with a CVSS score @@ -1175,7 +1236,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * @throws MojoFailureException thrown if a CVSS score is found that is * higher then the threshold set */ - protected void checkForFailure(List dependencies) throws MojoFailureException { + protected void checkForFailure(Dependency[] dependencies) throws MojoFailureException { final StringBuilder ids = new StringBuilder(); for (Dependency d : dependencies) { boolean addName = true; @@ -1212,7 +1273,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma * @param mp the Maven project for which the summary is shown * @param dependencies a list of dependency objects */ - protected void showSummary(MavenProject mp, List dependencies) { + protected void showSummary(MavenProject mp, Dependency[] dependencies) { if (showSummary) { final StringBuilder summary = new StringBuilder(); for (Dependency d : dependencies) { diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java index 04d36feac..a91f9b45d 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/CheckMojo.java @@ -30,7 +30,6 @@ import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.exception.ExceptionCollection; import org.owasp.dependencycheck.exception.ReportException; -import org.owasp.dependencycheck.utils.Settings; /** * Maven Plugin that checks the project dependencies to see if they have any @@ -41,7 +40,7 @@ import org.owasp.dependencycheck.utils.Settings; @Mojo( name = "check", defaultPhase = LifecyclePhase.VERIFY, - threadSafe = false, + threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresOnline = true ) @@ -99,7 +98,7 @@ public class CheckMojo extends BaseDependencyCheckMojo { } if (engine != null) { ExceptionCollection exCol = scanArtifacts(getProject(), engine); - if (engine.getDependencies().isEmpty()) { + if (engine.getDependencies().length == 0) { getLog().info("No dependencies were identified that could be analyzed by dependency-check"); } try { @@ -129,9 +128,9 @@ public class CheckMojo extends BaseDependencyCheckMojo { throw new MojoExecutionException("One or more exceptions occurred during dependency-check analysis", exCol); } } - engine.cleanup(); + engine.close(); } - Settings.cleanup(); + getSettings().cleanup(); } /** diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java index 42960fd5f..22b5f9e8d 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/PurgeMojo.java @@ -73,7 +73,7 @@ public class PurgeMojo extends BaseDependencyCheckMojo { populateSettings(); File db; try { - db = new File(Settings.getDataDirectory(), Settings.getString(Settings.KEYS.DB_FILE_NAME, "dc.h2.db")); + db = new File(getSettings().getDataDirectory(), getSettings().getString(Settings.KEYS.DB_FILE_NAME, "dc.h2.db")); if (db.exists()) { if (db.delete()) { getLog().info("Database file purged; local copy of the NVD has been removed"); @@ -98,7 +98,7 @@ public class PurgeMojo extends BaseDependencyCheckMojo { } getLog().error(msg); } - Settings.cleanup(); + getSettings().cleanup(); } } diff --git a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java index b212f6042..51b2ee77c 100644 --- a/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java +++ b/dependency-check-maven/src/main/java/org/owasp/dependencycheck/maven/UpdateMojo.java @@ -26,7 +26,6 @@ import org.apache.maven.plugins.annotations.ResolutionScope; import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.data.nvdcve.DatabaseException; import org.owasp.dependencycheck.data.update.exception.UpdateException; -import org.owasp.dependencycheck.utils.Settings; /** * Maven Plugin that checks the project dependencies to see if they have any @@ -82,8 +81,6 @@ public class UpdateMojo extends BaseDependencyCheckMojo { throw new MojoExecutionException(msg, ex); } getLog().error(msg); - } finally { - Settings.cleanup(); } } diff --git a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java index 6ac427185..ec6831730 100644 --- a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java +++ b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseDependencyCheckMojoTest.java @@ -90,20 +90,20 @@ public class BaseDependencyCheckMojoTest extends BaseTest { } }.getMockInstance(); - boolean autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, false); - Engine engine = new Engine(); - Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); + boolean autoUpdate = getSettings().getBoolean(Settings.KEYS.AUTO_UPDATE); + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, false); + try (Engine engine = new Engine(getSettings())) { + getSettings().setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); - assertTrue(engine.getDependencies().isEmpty()); - BaseDependencyCheckMojoImpl instance = new BaseDependencyCheckMojoImpl(); - try { //the mock above fails under some JDKs - instance.scanArtifacts(project, engine); - } catch (NullPointerException ex) { - Assume.assumeNoException(ex); + assertTrue(engine.getDependencies().length == 0); + BaseDependencyCheckMojoImpl instance = new BaseDependencyCheckMojoImpl(); + try { //the mock above fails under some JDKs + instance.scanArtifacts(project, engine); + } catch (NullPointerException ex) { + Assume.assumeNoException(ex); + } + assertFalse(engine.getDependencies().length == 0); } - assertFalse(engine.getDependencies().isEmpty()); - engine.cleanup(); } } diff --git a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseTest.java b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseTest.java index 4181facd0..7892f7aa9 100644 --- a/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseTest.java +++ b/dependency-check-maven/src/test/java/org/owasp/dependencycheck/maven/BaseTest.java @@ -17,8 +17,11 @@ */ package org.owasp.dependencycheck.maven; +import java.io.IOException; import java.io.InputStream; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.owasp.dependencycheck.utils.Settings; @@ -33,16 +36,36 @@ public class BaseTest { */ public static final String PROPERTIES_FILE = "mojo.properties"; - @BeforeClass - public static void setUpClass() throws Exception { - Settings.initialize(); + /** + * The configured settings. + */ + private Settings settings; + + /** + * Initialize the {@link Settings}. + */ + @Before + public void setUp() throws IOException { + settings = new Settings(); try (InputStream mojoProperties = BaseTest.class.getClassLoader().getResourceAsStream(BaseTest.PROPERTIES_FILE)) { - Settings.mergeProperties(mojoProperties); + settings.mergeProperties(mojoProperties); } } - @AfterClass - public static void tearDownClass() throws Exception { - Settings.cleanup(true); + /** + * Clean the {@link Settings}. + */ + @After + public void tearDown() { + settings.cleanup(true); + } + + /** + * Returns the settings for the test cases. + * + * @return + */ + protected Settings getSettings() { + return settings; } } diff --git a/dependency-check-plugin/pom.xml b/dependency-check-plugin/pom.xml index 33b393323..e172b21c4 100644 --- a/dependency-check-plugin/pom.xml +++ b/dependency-check-plugin/pom.xml @@ -21,7 +21,7 @@ Copyright (c) 2017 Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT org.owasp dependency-check-plugin diff --git a/dependency-check-plugin/src/main/resources/archetype-resources/src/main/java/__analyzerName__.java b/dependency-check-plugin/src/main/resources/archetype-resources/src/main/java/__analyzerName__.java index ec758c587..8916fb965 100644 --- a/dependency-check-plugin/src/main/resources/archetype-resources/src/main/java/__analyzerName__.java +++ b/dependency-check-plugin/src/main/resources/archetype-resources/src/main/java/__analyzerName__.java @@ -23,6 +23,7 @@ import org.owasp.dependencycheck.analyzer.FileTypeAnalyzer; import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.dependency.Dependency; import org.owasp.dependencycheck.exception.InitializationException; +import org.owasp.dependencycheck.utils.Settings; /** * An OWASP dependency-check plug-in example. If you are not implementing a @@ -66,7 +67,7 @@ public class ${analyzerName} implements Analyzer, FileTypeAnalyzer { @Override public void analyze(Dependency dependency, Engine engine) throws AnalysisException { if (enabled) { - throw new UnsupportedOperationException("Not implemented yet."); + //TODO implement analyze } } @@ -91,15 +92,26 @@ public class ${analyzerName} implements Analyzer, FileTypeAnalyzer { } /** - * The initialize method is called (once) prior to the analyze method being - * called on all of the dependencies. + * The initialize method is called just after instantiation of the object. * - * @throws InitializationException is thrown if an exception occurs - * initializing the analyzer. + * @param settings a reference to the configured settings */ @Override - public void initialize() throws InitializationException { + public void initialize(Settings settings) { + //TODO implement initialize + } + /** + * The prepare method is called once just prior to repeated calls to + * analyze. + * + * @param engine a reference to the engine + * @throws InitializationException thrown when the analyzer cannot be + * initialized + */ + @Override + public void prepare(Engine engine) throws InitializationException { + //TODO implement prepare } /** @@ -116,7 +128,7 @@ public class ${analyzerName} implements Analyzer, FileTypeAnalyzer { /** * Returns whether multiple instances of the same type of analyzer can run * in parallel. If the analyzer does not support parallel processing it is - * generally best to also mark the analyze(Dependency,Engine) as synchronized. + * generally best to also mark the analyze(Dependency,Engine) as synchronized. * * @return {@code true} if the analyzer supports parallel processing, * {@code false} else diff --git a/dependency-check-plugin/src/main/resources/archetype-resources/src/test/java/__analyzerName__Test.java b/dependency-check-plugin/src/main/resources/archetype-resources/src/test/java/__analyzerName__Test.java index a0247207f..8546837da 100644 --- a/dependency-check-plugin/src/main/resources/archetype-resources/src/test/java/__analyzerName__Test.java +++ b/dependency-check-plugin/src/main/resources/archetype-resources/src/test/java/__analyzerName__Test.java @@ -30,25 +30,27 @@ import org.owasp.dependencycheck.utils.Settings; */ public class ${analyzerName}Test { + Settings settings = null; + public ${analyzerName}Test() { } @BeforeClass public static void setUpClass() { - Settings.initialize(); } @AfterClass public static void tearDownClass() { - Settings.cleanup(); } @Before public void setUp() { + settings = new Settings(); } - + @After public void tearDown() { + settings.cleanup(); } /** @@ -68,12 +70,14 @@ public class ${analyzerName}Test { */ @Test public void testAnalyze() throws Exception { + //The engine is generally null for most analyzer test cases but can be instantiated if needed. + Engine engine = null; ${analyzerName} instance = new ${analyzerName}(); - instance.initialize(); + instance.initialize(settings); + instance.prepare(engine); + File file = new File(${analyzerName}.class.getClassLoader().getResource("test.file").toURI().getPath()); Dependency dependency = new Dependency(file); - //The engine is generally null for most analyzer test cases. - Engine engine = null; //TODO uncomment the following line and add assertions against the dependency. //instance.analyze(dependency, engine); @@ -107,7 +111,7 @@ public class ${analyzerName}Test { @Test public void testInitialize() throws Exception { ${analyzerName} instance = new ${analyzerName}(); - instance.initialize(); + instance.initialize(settings); } /** diff --git a/dependency-check-utils/pom.xml b/dependency-check-utils/pom.xml index 2c4763f55..846049af4 100644 --- a/dependency-check-utils/pom.xml +++ b/dependency-check-utils/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT dependency-check-utils @@ -94,10 +94,5 @@ Copyright (c) 2014 - Jeremy Long. All Rights Reserved. logback-classic test - - com.google.code.findbugs - annotations - provided - diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java index 14dc720a3..c2687caf1 100755 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Downloader.java @@ -62,9 +62,23 @@ public final class Downloader { private static final String GET = "GET"; /** - * Private constructor for utility class. + * The configured settings. */ - private Downloader() { + private final Settings settings; + + /** + * The URL connection facctory. + */ + private final URLConnectionFactory connFactory; + + /** + * Constructs a new downloader object. + * + * @param settings the configured settings + */ + public Downloader(Settings settings) { + this.settings = settings; + this.connFactory = new URLConnectionFactory(settings); } /** @@ -75,7 +89,7 @@ public final class Downloader { * @throws DownloadFailedException is thrown if there is an error * downloading the file */ - public static void fetchFile(URL url, File outputPath) throws DownloadFailedException { + public void fetchFile(URL url, File outputPath) throws DownloadFailedException { fetchFile(url, outputPath, true); } @@ -89,7 +103,7 @@ public final class Downloader { * @throws DownloadFailedException is thrown if there is an error * downloading the file */ - public static void fetchFile(URL url, File outputPath, boolean useProxy) throws DownloadFailedException { + public void fetchFile(URL url, File outputPath, boolean useProxy) throws DownloadFailedException { if ("file".equalsIgnoreCase(url.getProtocol())) { File file; try { @@ -113,7 +127,7 @@ public final class Downloader { HttpURLConnection conn = null; try { LOGGER.debug("Attempting download of {}", url.toString()); - conn = URLConnectionFactory.createHttpURLConnection(url, useProxy); + conn = connFactory.createHttpURLConnection(url, useProxy); conn.setRequestProperty("Accept-Encoding", "gzip, deflate"); conn.connect(); int status = conn.getResponseCode(); @@ -129,7 +143,7 @@ public final class Downloader { conn = null; } LOGGER.debug("Download is being redirected from {} to {}", url.toString(), location); - conn = URLConnectionFactory.createHttpURLConnection(new URL(location), useProxy); + conn = connFactory.createHttpURLConnection(new URL(location), useProxy); conn.setRequestProperty("Accept-Encoding", "gzip, deflate"); conn.connect(); status = conn.getResponseCode(); @@ -217,7 +231,7 @@ public final class Downloader { * @throws DownloadFailedException is thrown if an exception occurs making * the HTTP request */ - public static long getLastModified(URL url) throws DownloadFailedException { + public long getLastModified(URL url) throws DownloadFailedException { return getLastModified(url, false); } @@ -233,7 +247,7 @@ public final class Downloader { * @throws DownloadFailedException is thrown if an exception occurs making * the HTTP request */ - private static long getLastModified(URL url, boolean isRetry) throws DownloadFailedException { + private long getLastModified(URL url, boolean isRetry) throws DownloadFailedException { long timestamp = 0; //TODO add the FTP protocol? if ("file".equalsIgnoreCase(url.getProtocol())) { @@ -249,7 +263,7 @@ public final class Downloader { final String httpMethod = determineHttpMethod(); HttpURLConnection conn = null; try { - conn = URLConnectionFactory.createHttpURLConnection(url); + conn = connFactory.createHttpURLConnection(url); conn.setRequestMethod(httpMethod); conn.connect(); final int t = conn.getResponseCode(); @@ -269,8 +283,8 @@ public final class Downloader { } try { //retry - if (!isRetry && Settings.getBoolean(Settings.KEYS.DOWNLOADER_QUICK_QUERY_TIMESTAMP)) { - Settings.setBoolean(Settings.KEYS.DOWNLOADER_QUICK_QUERY_TIMESTAMP, false); + if (!isRetry && settings.getBoolean(Settings.KEYS.DOWNLOADER_QUICK_QUERY_TIMESTAMP)) { + settings.setBoolean(Settings.KEYS.DOWNLOADER_QUICK_QUERY_TIMESTAMP, false); return getLastModified(url, true); } } catch (InvalidSettingException ex1) { @@ -300,7 +314,7 @@ public final class Downloader { * @throws DownloadFailedException a wrapper exception that contains the * original exception as the cause */ - protected static synchronized void checkForCommonExceptionTypes(IOException ex) throws DownloadFailedException { + protected synchronized void checkForCommonExceptionTypes(IOException ex) throws DownloadFailedException { Throwable cause = ex; while (cause != null) { if (cause instanceof java.net.UnknownHostException) { @@ -328,7 +342,7 @@ public final class Downloader { * * @return the HTTP method to use */ - private static String determineHttpMethod() { + private String determineHttpMethod() { return isQuickQuery() ? HEAD : GET; } @@ -338,11 +352,11 @@ public final class Downloader { * * @return true if configured to use HEAD requests */ - private static boolean isQuickQuery() { + private boolean isQuickQuery() { boolean quickQuery; try { - quickQuery = Settings.getBoolean(Settings.KEYS.DOWNLOADER_QUICK_QUERY_TIMESTAMP, true); + quickQuery = settings.getBoolean(Settings.KEYS.DOWNLOADER_QUICK_QUERY_TIMESTAMP, true); } catch (InvalidSettingException e) { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Invalid settings : {}", e.getMessage(), e); diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStream.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStream.java index abd10d4e6..8840b7c8e 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStream.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStream.java @@ -25,12 +25,14 @@ import java.io.ObjectStreamClass; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.annotation.concurrent.NotThreadSafe; /** * An ObjectInputStream that will only deserialize expected classes. * * @author Jeremy Long */ +@NotThreadSafe public class ExpectedObjectInputStream extends ObjectInputStream { /** diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java index 50df24f14..88d8aa798 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java @@ -101,25 +101,6 @@ public final class FileUtils { return tempDir; } - /** - * Generates a new temporary file name that is guaranteed to be unique. - * - * @param prefix the prefix for the file name to generate - * @param extension the extension of the generated file name - * @return a temporary File - * @throws java.io.IOException thrown if the temporary folder could not be - * created - */ - public static File getTempFile(String prefix, String extension) throws IOException { - final File dir = Settings.getTempDirectory(); - final String tempFileName = String.format("%s%s.%s", prefix, UUID.randomUUID().toString(), extension); - final File tempFile = new File(dir, tempFileName); - if (tempFile.exists()) { - return getTempFile(prefix, extension); - } - return tempFile; - } - /** * Return the bit bucket for the OS. '/dev/null' for Unix and 'NUL' for * Windows diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/SSLSocketFactoryEx.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/SSLSocketFactoryEx.java index 4849de733..060193964 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/SSLSocketFactoryEx.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/SSLSocketFactoryEx.java @@ -42,15 +42,21 @@ public class SSLSocketFactoryEx extends SSLSocketFactory { * The protocols. */ private String[] protocols; + /** + * The configured settings. + */ + private final Settings settings; /** * Constructs a new SSLSocketFactory. * + * @param settings reference to the configured settings * @throws NoSuchAlgorithmException thrown when an algorithm is not * supported * @throws KeyManagementException thrown if initialization fails */ - public SSLSocketFactoryEx() throws NoSuchAlgorithmException, KeyManagementException { + public SSLSocketFactoryEx(Settings settings) throws NoSuchAlgorithmException, KeyManagementException { + this.settings = settings; initSSLSocketFactoryEx(null, null, null); } @@ -60,11 +66,14 @@ public class SSLSocketFactoryEx extends SSLSocketFactory { * @param km the key manager * @param tm the trust manager * @param random secure random + * @param settings reference to the configured settings * @throws NoSuchAlgorithmException thrown when an algorithm is not * supported * @throws KeyManagementException thrown if initialization fails */ - public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws NoSuchAlgorithmException, KeyManagementException { + public SSLSocketFactoryEx(KeyManager[] km, TrustManager[] tm, SecureRandom random, Settings settings) + throws NoSuchAlgorithmException, KeyManagementException { + this.settings = settings; initSSLSocketFactoryEx(km, tm, random); } @@ -72,11 +81,13 @@ public class SSLSocketFactoryEx extends SSLSocketFactory { * Constructs a new SSLSocketFactory. * * @param ctx the SSL context + * @param settings reference to the configured settings * @throws NoSuchAlgorithmException thrown when an algorithm is not * supported * @throws KeyManagementException thrown if initialization fails */ - public SSLSocketFactoryEx(SSLContext ctx) throws NoSuchAlgorithmException, KeyManagementException { + public SSLSocketFactoryEx(SSLContext ctx, Settings settings) throws NoSuchAlgorithmException, KeyManagementException { + this.settings = settings; initSSLSocketFactoryEx(ctx); } @@ -254,7 +265,7 @@ public class SSLSocketFactoryEx extends SSLSocketFactory { protected String[] getProtocolList() { SSLSocket socket = null; String[] availableProtocols = null; - final String[] preferredProtocols = Settings.getString( + final String[] preferredProtocols = settings.getString( Settings.KEYS.DOWNLOADER_TLS_PROTOCOL_LIST, "TLSv1,TLSv1.1,TLSv1.2,TLSv1.3") .split(","); diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java index f8bf932cc..a009dd1af 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/Settings.java @@ -32,6 +32,7 @@ import java.net.URLDecoder; import java.security.ProtectionDomain; import java.util.Enumeration; import java.util.Properties; +import java.util.UUID; import org.apache.commons.lang3.StringUtils; @@ -54,10 +55,6 @@ public final class Settings { * Array separator. */ private static final String ARRAY_SEP = ","; - /** - * Thread local settings. - */ - private static final ThreadLocal LOCAL_SETTINGS = new ThreadLocal<>(); /** * The properties. */ @@ -67,7 +64,7 @@ public final class Settings { * A reference to the temporary directory; used in case it needs to be * deleted during cleanup. */ - private static File tempDirectory = null; + private File tempDirectory = null; // /** @@ -449,12 +446,27 @@ public final class Settings { // /** - * Private constructor for the Settings class. This class loads the - * properties files. + * Initialize the settings object. + */ + public Settings() { + initialize(PROPERTIES_FILE); + } + + /** + * Initialize the settings object using the given properties file. * * @param propertiesFilePath the path to the base properties file to load */ - private Settings(String propertiesFilePath) { + public Settings(String propertiesFilePath) { + initialize(propertiesFilePath); + } + + /** + * Initializes the settings object from the given file. + * + * @param propertiesFilePath the path to the settings property file + */ + private void initialize(String propertiesFilePath) { props = new Properties(); try (InputStream in = FileUtils.getResourceAsStream(propertiesFilePath)) { props.load(in); @@ -468,54 +480,11 @@ public final class Settings { logProperties("Properties loaded", props); } - /** - *

    - * Initializes the thread local settings object. Note, to use the settings - * object you must call this method. However, you must also call - * Settings.cleanup() to properly release resources.

    - * - *

    - * Note - Only an end user interface such as the CLI, Maven Plugin, - * etc. should call initialize. When called `initialize` will over-write any - * configured settings (i.e. configured via the maven plugin) and the - * default values from dependency-check-core will be used. If you are - * running into issues with the settings not being initialized it is likely - * due to multi-threading and you should use the `Settings.setInstance` - * method instead. See the `TimestampRetriever` class within NvdCveUpdater - * as an example.

    - * - */ - public static void initialize() { - LOCAL_SETTINGS.set(new Settings(PROPERTIES_FILE)); - } - - /** - *

    - * Initializes the thread local settings object. Note, to use the settings - * object you must call this method. However, you must also call - * Settings.cleanup() to properly release resources.

    - * - *

    - * Note - Only an end user interface such as the CLI, Maven Plugin, - * etc. should call initialize. When called `initialize` will over-write any - * configured settings (i.e. configured via the maven plugin) and the - * default values from dependency-check-core will be used. If you are - * running into issues with the settings not being initialized it is likely - * due to multi-threading and you should use the `Settings.setInstance` - * method instead. See the `TimestampRetriever` class within NvdCveUpdater - * as an example.

    - * - * @param propertiesFilePath the path to the base properties file to load - */ - public static void initialize(String propertiesFilePath) { - LOCAL_SETTINGS.set(new Settings(propertiesFilePath)); - } - /** * Cleans up resources to prevent memory leaks. * */ - public static void cleanup() { + public void cleanup() { cleanup(true); } @@ -525,39 +494,11 @@ public final class Settings { * @param deleteTemporary flag indicating whether any temporary directories * generated should be removed */ - public static synchronized void cleanup(boolean deleteTemporary) { + public synchronized void cleanup(boolean deleteTemporary) { if (deleteTemporary && tempDirectory != null && tempDirectory.exists()) { FileUtils.delete(tempDirectory); tempDirectory = null; } - try { - LOCAL_SETTINGS.remove(); - } catch (Throwable ex) { - LOGGER.debug("Error cleaning up Settings", ex); - } - } - - /** - * Gets the underlying instance of the Settings object. - * - * @return the Settings object - */ - public static Settings getInstance() { - return LOCAL_SETTINGS.get(); - } - - /** - *

    - * Sets the instance of the Settings object to use in this thread.

    - *

    - * Note - if using this method to enable multi-threading one must - * call `Settings.cleanup(false)`. See the `TimestampRetriever` class within - * NvdCveUpdater as an example.

    - * - * @param instance the instance of the settings object to use in this thread - */ - public static void setInstance(Settings instance) { - LOCAL_SETTINGS.set(instance); } /** @@ -567,7 +508,7 @@ public final class Settings { * @param header the header to print with the log message * @param properties the properties to log */ - private static void logProperties(String header, Properties properties) { + private void logProperties(String header, Properties properties) { if (LOGGER.isDebugEnabled()) { final StringWriter sw = new StringWriter(); try (PrintWriter pw = new PrintWriter(sw)) { @@ -597,8 +538,8 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setString(String key, String value) { - LOCAL_SETTINGS.get().props.setProperty(key, value); + public void setString(String key, String value) { + props.setProperty(key, value); LOGGER.debug("Setting: {}='{}'", key, value); } @@ -608,7 +549,7 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setStringIfNotNull(String key, String value) { + public void setStringIfNotNull(String key, String value) { if (null != value) { setString(key, value); } @@ -620,7 +561,7 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setStringIfNotEmpty(String key, String value) { + public void setStringIfNotEmpty(String key, String value) { if (null != value && !value.isEmpty()) { setString(key, value); } @@ -632,7 +573,7 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setArrayIfNotEmpty(String key, String[] value) { + public void setArrayIfNotEmpty(String key, String[] value) { if (null != value && value.length > 0) { setString(key, StringUtils.join(value, ARRAY_SEP)); } @@ -644,7 +585,7 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setBoolean(String key, boolean value) { + public void setBoolean(String key, boolean value) { setString(key, Boolean.toString(value)); } @@ -654,7 +595,7 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setBooleanIfNotNull(String key, Boolean value) { + public void setBooleanIfNotNull(String key, Boolean value) { if (null != value) { setBoolean(key, value); } @@ -666,8 +607,8 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setInt(String key, int value) { - LOCAL_SETTINGS.get().props.setProperty(key, String.valueOf(value)); + public void setInt(String key, int value) { + props.setProperty(key, String.valueOf(value)); LOGGER.debug("Setting: {}='{}'", key, value); } @@ -677,7 +618,7 @@ public final class Settings { * @param key the key for the property * @param value the value for the property */ - public static void setIntIfNotNull(String key, Integer value) { + public void setIntIfNotNull(String key, Integer value) { if (null != value) { setInt(key, value); } @@ -695,7 +636,7 @@ public final class Settings { * @throws IOException is thrown when there is an exception loading/merging * the properties */ - public static void mergeProperties(File filePath) throws FileNotFoundException, IOException { + public void mergeProperties(File filePath) throws FileNotFoundException, IOException { try (FileInputStream fis = new FileInputStream(filePath)) { mergeProperties(fis); } @@ -713,7 +654,7 @@ public final class Settings { * @throws IOException is thrown when there is an exception loading/merging * the properties */ - public static void mergeProperties(String filePath) throws FileNotFoundException, IOException { + public void mergeProperties(String filePath) throws FileNotFoundException, IOException { try (FileInputStream fis = new FileInputStream(filePath)) { mergeProperties(fis); } @@ -729,9 +670,9 @@ public final class Settings { * @throws IOException is thrown when there is an exception loading/merging * the properties */ - public static void mergeProperties(InputStream stream) throws IOException { - LOCAL_SETTINGS.get().props.load(stream); - logProperties("Properties updated via merge", LOCAL_SETTINGS.get().props); + public void mergeProperties(InputStream stream) throws IOException { + props.load(stream); + logProperties("Properties updated via merge", props); } /** @@ -743,7 +684,7 @@ public final class Settings { * @param key the key to lookup within the properties file * @return the property from the properties file converted to a File object */ - public static File getFile(String key) { + public File getFile(String key) { final String file = getString(key); if (file == null) { return null; @@ -765,7 +706,7 @@ public final class Settings { * @param key the key to lookup within the properties file * @return the property from the properties file converted to a File object */ - protected static File getDataFile(String key) { + protected File getDataFile(String key) { final String file = getString(key); LOGGER.debug("Settings.getDataFile() - file: '{}'", file); if (file == null) { @@ -788,7 +729,7 @@ public final class Settings { * * @return a File object */ - private static File getJarPath() { + private File getJarPath() { String decodedPath = "."; String jarPath = ""; final ProtectionDomain domain = Settings.class.getProtectionDomain(); @@ -819,8 +760,8 @@ public final class Settings { * @param defaultValue the default value for the requested property * @return the property from the properties file */ - public static String getString(String key, String defaultValue) { - return System.getProperty(key, LOCAL_SETTINGS.get().props.getProperty(key, defaultValue)); + public String getString(String key, String defaultValue) { + return System.getProperty(key, props.getProperty(key, defaultValue)); } /** @@ -830,9 +771,9 @@ public final class Settings { * @throws java.io.IOException thrown if the temporary directory does not * exist and cannot be created */ - public static synchronized File getTempDirectory() throws IOException { + public synchronized File getTempDirectory() throws IOException { if (tempDirectory == null) { - final File baseTemp = new File(Settings.getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir"))); + final File baseTemp = new File(getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir"))); tempDirectory = FileUtils.createTempDirectory(baseTemp); } return tempDirectory; @@ -847,19 +788,19 @@ public final class Settings { * @param key the key to lookup within the properties file * @return the property from the properties file */ - public static String getString(String key) { - return System.getProperty(key, LOCAL_SETTINGS.get().props.getProperty(key)); + public String getString(String key) { + return System.getProperty(key, props.getProperty(key)); } /** * Returns a list with the given key. * - * If the propery is not set then {@code null} will be returned. + * If the property is not set then {@code null} will be returned. * - * @param key the key to get from this {@link Settings} singleton. + * @param key the key to get from this {@link Settings}. * @return the list or {@code null} if the key wasn't present. */ - public static String[] getArray(final String key) { + public String[] getArray(final String key) { final String string = getString(key); if (string != null) { return string.split(ARRAY_SEP); @@ -873,8 +814,8 @@ public final class Settings { * * @param key the property key to remove */ - public static void removeProperty(String key) { - LOCAL_SETTINGS.get().props.remove(key); + public void removeProperty(String key) { + props.remove(key); } /** @@ -888,9 +829,9 @@ public final class Settings { * @throws InvalidSettingException is thrown if there is an error retrieving * the setting */ - public static int getInt(String key) throws InvalidSettingException { + public int getInt(String key) throws InvalidSettingException { try { - return Integer.parseInt(Settings.getString(key)); + return Integer.parseInt(getString(key)); } catch (NumberFormatException ex) { throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex); } @@ -907,13 +848,13 @@ public final class Settings { * @return the property from the properties file or the defaultValue if the * property does not exist or cannot be converted to an integer */ - public static int getInt(String key, int defaultValue) { + public int getInt(String key, int defaultValue) { int value; try { - value = Integer.parseInt(Settings.getString(key)); + value = Integer.parseInt(getString(key)); } catch (NumberFormatException ex) { - if (!Settings.getString(key, "").isEmpty()) { - LOGGER.debug("Could not convert property '{}={}' to an int; using {} instead.", key, Settings.getString(key), defaultValue); + if (!getString(key, "").isEmpty()) { + LOGGER.debug("Could not convert property '{}={}' to an int; using {} instead.", key, getString(key), defaultValue); } value = defaultValue; } @@ -931,9 +872,9 @@ public final class Settings { * @throws InvalidSettingException is thrown if there is an error retrieving * the setting */ - public static long getLong(String key) throws InvalidSettingException { + public long getLong(String key) throws InvalidSettingException { try { - return Long.parseLong(Settings.getString(key)); + return Long.parseLong(getString(key)); } catch (NumberFormatException ex) { throw new InvalidSettingException("Could not convert property '" + key + "' to a long.", ex); } @@ -951,8 +892,8 @@ public final class Settings { * @throws InvalidSettingException is thrown if there is an error retrieving * the setting */ - public static boolean getBoolean(String key) throws InvalidSettingException { - return Boolean.parseBoolean(Settings.getString(key)); + public boolean getBoolean(String key) throws InvalidSettingException { + return Boolean.parseBoolean(getString(key)); } /** @@ -969,8 +910,8 @@ public final class Settings { * @throws InvalidSettingException is thrown if there is an error retrieving * the setting */ - public static boolean getBoolean(String key, boolean defaultValue) throws InvalidSettingException { - return Boolean.parseBoolean(Settings.getString(key, Boolean.toString(defaultValue))); + public boolean getBoolean(String key, boolean defaultValue) throws InvalidSettingException { + return Boolean.parseBoolean(getString(key, Boolean.toString(defaultValue))); } /** @@ -986,9 +927,9 @@ public final class Settings { * @throws IOException thrown the data directory cannot be created * @throws InvalidSettingException thrown if there is an invalid setting */ - public static String getConnectionString(String connectionStringKey, String dbFileNameKey) + public String getConnectionString(String connectionStringKey, String dbFileNameKey) throws IOException, InvalidSettingException { - final String connStr = Settings.getString(connectionStringKey); + final String connStr = getString(connectionStringKey); if (connStr == null) { final String msg = String.format("Invalid properties file; %s is missing.", connectionStringKey); throw new InvalidSettingException(msg); @@ -997,7 +938,7 @@ public final class Settings { final File directory = getDataDirectory(); String fileName = null; if (dbFileNameKey != null) { - fileName = Settings.getString(dbFileNameKey); + fileName = getString(dbFileNameKey); } if (fileName == null) { final String msg = String.format("Invalid properties file to get a file based connection string; '%s' must be defined.", @@ -1024,12 +965,31 @@ public final class Settings { * @return the data directory to store data files * @throws IOException is thrown if an IOException occurs of course... */ - public static File getDataDirectory() throws IOException { - final File path = Settings.getDataFile(Settings.KEYS.DATA_DIRECTORY); + public File getDataDirectory() throws IOException { + final File path = getDataFile(Settings.KEYS.DATA_DIRECTORY); if (path != null && (path.exists() || path.mkdirs())) { return path; } throw new IOException(String.format("Unable to create the data directory '%s'", (path == null) ? "unknown" : path.getAbsolutePath())); } + + /** + * Generates a new temporary file name that is guaranteed to be unique. + * + * @param prefix the prefix for the file name to generate + * @param extension the extension of the generated file name + * @return a temporary File + * @throws java.io.IOException thrown if the temporary folder could not be + * created + */ + public File getTempFile(String prefix, String extension) throws IOException { + final File dir = getTempDirectory(); + final String tempFileName = String.format("%s%s.%s", prefix, UUID.randomUUID().toString(), extension); + final File tempFile = new File(dir, tempFileName); + if (tempFile.exists()) { + return getTempFile(prefix, extension); + } + return tempFile; + } } diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java index e8557aad0..b0423ae0f 100644 --- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/URLConnectionFactory.java @@ -48,11 +48,18 @@ public final class URLConnectionFactory { * The logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(URLConnectionFactory.class); + /** + * The configured settings. + */ + private final Settings settings; /** * Private constructor for this factory. + * + * @param settings reference to the configured settings */ - private URLConnectionFactory() { + public URLConnectionFactory(Settings settings) { + this.settings = settings; } /** @@ -65,17 +72,17 @@ public final class URLConnectionFactory { * @throws URLConnectionFailureException thrown if there is an exception */ @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE", justification = "Just being extra safe") - public static HttpURLConnection createHttpURLConnection(URL url) throws URLConnectionFailureException { + public HttpURLConnection createHttpURLConnection(URL url) throws URLConnectionFailureException { HttpURLConnection conn = null; - final String proxyHost = Settings.getString(Settings.KEYS.PROXY_SERVER); + final String proxyHost = settings.getString(Settings.KEYS.PROXY_SERVER); try { if (proxyHost != null && !matchNonProxy(url)) { - final int proxyPort = Settings.getInt(Settings.KEYS.PROXY_PORT); + final int proxyPort = settings.getInt(Settings.KEYS.PROXY_PORT); final SocketAddress address = new InetSocketAddress(proxyHost, proxyPort); - final String username = Settings.getString(Settings.KEYS.PROXY_USERNAME); - final String password = Settings.getString(Settings.KEYS.PROXY_PASSWORD); + final String username = settings.getString(Settings.KEYS.PROXY_USERNAME); + final String password = settings.getString(Settings.KEYS.PROXY_PASSWORD); if (username != null && password != null) { final Authenticator auth = new Authenticator() { @@ -84,7 +91,7 @@ public final class URLConnectionFactory { if (proxyHost.equals(getRequestingHost()) || getRequestorType().equals(Authenticator.RequestorType.PROXY)) { LOGGER.debug("Using the configured proxy username and password"); try { - if (Settings.getBoolean(Settings.KEYS.PROXY_DISABLE_SCHEMAS, true)) { + if (settings.getBoolean(Settings.KEYS.PROXY_DISABLE_SCHEMAS, true)) { System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); } } catch (InvalidSettingException ex) { @@ -103,8 +110,8 @@ public final class URLConnectionFactory { } else { conn = (HttpURLConnection) url.openConnection(); } - final int timeout = Settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000); - conn.setConnectTimeout(timeout); + final int connectionTimeout = settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000); + conn.setConnectTimeout(connectionTimeout); conn.setInstanceFollowRedirects(true); } catch (IOException ex) { if (conn != null) { @@ -126,11 +133,11 @@ public final class URLConnectionFactory { * @param url the url to connect to * @return matching result. true: match nonProxy */ - private static boolean matchNonProxy(final URL url) { + private boolean matchNonProxy(final URL url) { final String host = url.getHost(); // code partially from org.apache.maven.plugins.site.AbstractDeployMojo#getProxyInfo - final String nonProxyHosts = Settings.getString(Settings.KEYS.PROXY_NON_PROXY_HOSTS); + final String nonProxyHosts = settings.getString(Settings.KEYS.PROXY_NON_PROXY_HOSTS); if (null != nonProxyHosts) { final String[] nonProxies = nonProxyHosts.split("(,)|(;)|(\\|)"); for (final String nonProxyHost : nonProxies) { @@ -172,14 +179,14 @@ public final class URLConnectionFactory { * @return a newly constructed HttpURLConnection * @throws URLConnectionFailureException thrown if there is an exception */ - public static HttpURLConnection createHttpURLConnection(URL url, boolean proxy) throws URLConnectionFailureException { + public HttpURLConnection createHttpURLConnection(URL url, boolean proxy) throws URLConnectionFailureException { if (proxy) { return createHttpURLConnection(url); } HttpURLConnection conn = null; try { conn = (HttpURLConnection) url.openConnection(); - final int timeout = Settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000); + final int timeout = settings.getInt(Settings.KEYS.CONNECTION_TIMEOUT, 10000); conn.setConnectTimeout(timeout); conn.setInstanceFollowRedirects(true); } catch (IOException ioe) { @@ -197,11 +204,11 @@ public final class URLConnectionFactory { * @param url the URL * @param conn the connection */ - private static void configureTLS(URL url, URLConnection conn) { + private void configureTLS(URL url, URLConnection conn) { if ("https".equals(url.getProtocol())) { try { final HttpsURLConnection secCon = (HttpsURLConnection) conn; - final SSLSocketFactoryEx factory = new SSLSocketFactoryEx(); + final SSLSocketFactoryEx factory = new SSLSocketFactoryEx(settings); secCon.setSSLSocketFactory(factory); } catch (NoSuchAlgorithmException ex) { LOGGER.debug("Unsupported algorithm in SSLSocketFactoryEx", ex); diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/BaseTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/BaseTest.java index a776c9f07..b2fdb4149 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/BaseTest.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/BaseTest.java @@ -15,8 +15,8 @@ */ package org.owasp.dependencycheck.utils; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Before; /** * @@ -24,13 +24,33 @@ import org.junit.BeforeClass; */ public class BaseTest { - @BeforeClass - public static void setUpClass() throws Exception { - Settings.initialize(); + /** + * The configured settings. + */ + private Settings settings; + + /** + * Initialize the {@link Settings}. + */ + @Before + public void setUp() { + settings = new Settings(); } - @AfterClass - public static void tearDownClass() throws Exception { - Settings.cleanup(true); + /** + * Clean the {@link Settings}. + */ + @After + public void tearDown() { + settings.cleanup(true); + } + + /** + * Returns the settings for the test cases. + * + * @return + */ + protected Settings getSettings() { + return settings; } } diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderIT.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderIT.java index a181249f6..1c8ce0733 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderIT.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderIT.java @@ -40,16 +40,18 @@ public class DownloaderIT extends BaseTest { // Settings.setString(Settings.KEYS.CONNECTION_TIMEOUT, "1000"); // Settings.setString(Settings.KEYS.PROXY_PORT, "8080"); // Settings.setString(Settings.KEYS.PROXY_SERVER, "127.0.0.1"); - URL url = new URL(Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL)); + URL url = new URL(getSettings().getString(Settings.KEYS.CVE_MODIFIED_20_URL)); File outputPath = new File("target/downloaded_cve.xml"); - Downloader.fetchFile(url, outputPath); + Downloader downloader = new Downloader(getSettings()); + downloader.fetchFile(url, outputPath); assertTrue(outputPath.isFile()); } @Test public void testGetLastModified() throws Exception { - URL url = new URL(Settings.getString(Settings.KEYS.CVE_MODIFIED_20_URL)); - long timestamp = Downloader.getLastModified(url); + URL url = new URL(getSettings().getString(Settings.KEYS.CVE_MODIFIED_20_URL)); + Downloader downloader = new Downloader(getSettings()); + long timestamp = downloader.getLastModified(url); assertTrue("timestamp equal to zero?", timestamp > 0); } } diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderTest.java index 7bc487fde..718b536cf 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderTest.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/DownloaderTest.java @@ -25,11 +25,12 @@ import org.junit.Test; * * @author Jeremy Long */ -public class DownloaderTest { +public class DownloaderTest extends BaseTest { @Test public void testGetLastModified_file() throws Exception { - long timestamp = Downloader.getLastModified(new File("target/test-classes/dependencycheck.properties").toURI().toURL()); + Downloader instance = new Downloader(getSettings()); + long timestamp = instance.getLastModified(new File("target/test-classes/dependencycheck.properties").toURI().toURL()); assertTrue("timestamp equal to zero?", timestamp > 0); } } diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStreamTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStreamTest.java index 0907da9bb..b7e0cc8bf 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStreamTest.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/ExpectedObjectInputStreamTest.java @@ -46,7 +46,6 @@ public class ExpectedObjectInputStreamTest { out.writeObject(data); out.flush(); byte[] buf = mem.toByteArray(); - out.close(); ByteArrayInputStream in = new ByteArrayInputStream(buf); ExpectedObjectInputStream instance = new ExpectedObjectInputStream(in, "java.util.ArrayList", "org.owasp.dependencycheck.utils.SimplePojo", "java.lang.Integer", "java.lang.Number"); instance.readObject(); diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java index f6fc832b7..f075573b0 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/FileUtilsTest.java @@ -50,7 +50,7 @@ public class FileUtilsTest extends BaseTest { @Test public void testDelete() throws Exception { - File file = File.createTempFile("tmp", "deleteme", Settings.getTempDirectory()); + File file = File.createTempFile("tmp", "deleteme", getSettings().getTempDirectory()); if (!file.exists()) { fail("Unable to create a temporary file."); } diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SettingsTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SettingsTest.java index a9087f9e7..32db4f4ba 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SettingsTest.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SettingsTest.java @@ -26,9 +26,7 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; -import org.junit.After; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; /** @@ -37,22 +35,6 @@ import org.junit.Test; */ public class SettingsTest extends BaseTest { - /** - * Initialize the {@link Settings} singleton. - */ - @Before - public void setUp() { - Settings.initialize(); - } - - /** - * Clean the {@link Settings} singleton. - */ - @After - public void tearDown() { - Settings.cleanup(); - } - /** * Test of getString method, of class Settings. */ @@ -60,7 +42,7 @@ public class SettingsTest extends BaseTest { public void testGetString() { String key = Settings.KEYS.CVE_MODIFIED_VALID_FOR_DAYS; String expResult = "7"; - String result = Settings.getString(key); + String result = getSettings().getString(key); Assert.assertTrue(result.endsWith(expResult)); } @@ -71,21 +53,23 @@ public class SettingsTest extends BaseTest { public void testGetDataFile() throws IOException { String key = Settings.KEYS.DATA_DIRECTORY; String expResult = "data"; - File result = Settings.getDataFile(key); + File result = getSettings().getDataFile(key); Assert.assertTrue(result.getAbsolutePath().endsWith(expResult)); } /** * Test of mergeProperties method, of class Settings. + * @throws java.io.IOException thrown when the test fails + * @throws java.net.URISyntaxException thrown when the test fails */ @Test public void testMergeProperties_String() throws IOException, URISyntaxException { String key = Settings.KEYS.PROXY_PORT; - String expResult = Settings.getString(key); + String expResult = getSettings().getString(key); File f = new File(this.getClass().getClassLoader().getResource("test.properties").toURI()); //InputStream in = this.getClass().getClassLoader().getResourceAsStream("test.properties"); - Settings.mergeProperties(f.getAbsolutePath()); - String result = Settings.getString(key); + getSettings().mergeProperties(f.getAbsolutePath()); + String result = getSettings().getString(key); Assert.assertTrue("setting didn't change?", (expResult == null && result != null) || !expResult.equals(result)); } @@ -96,8 +80,8 @@ public class SettingsTest extends BaseTest { public void testSetString() { String key = "newProperty"; String value = "someValue"; - Settings.setString(key, value); - String expResults = Settings.getString(key); + getSettings().setString(key, value); + String expResults = getSettings().getString(key); Assert.assertEquals(expResults, value); } @@ -108,9 +92,9 @@ public class SettingsTest extends BaseTest { public void testSetStringIfNotNull() { String key = "nullableProperty"; String value = "someValue"; - Settings.setString(key, value); - Settings.setStringIfNotNull(key, null); // NO-OP - String expResults = Settings.getString(key); + getSettings().setString(key, value); + getSettings().setStringIfNotNull(key, null); // NO-OP + String expResults = getSettings().getString(key); Assert.assertEquals(expResults, value); } @@ -121,9 +105,9 @@ public class SettingsTest extends BaseTest { public void testSetStringIfNotEmpty() { String key = "optionalProperty"; String value = "someValue"; - Settings.setString(key, value); - Settings.setStringIfNotEmpty(key, ""); // NO-OP - String expResults = Settings.getString(key); + getSettings().setString(key, value); + getSettings().setStringIfNotEmpty(key, ""); // NO-OP + String expResults = getSettings().getString(key); Assert.assertEquals(expResults, value); } @@ -135,9 +119,9 @@ public class SettingsTest extends BaseTest { String key = "key That Doesn't Exist"; String defaultValue = "blue bunny"; String expResult = "blue bunny"; - String result = Settings.getString(key); + String result = getSettings().getString(key); Assert.assertTrue(result == null); - result = Settings.getString(key, defaultValue); + result = getSettings().getString(key, defaultValue); Assert.assertEquals(expResult, result); } @@ -147,7 +131,7 @@ public class SettingsTest extends BaseTest { @Test public void testGetString_String() { String key = Settings.KEYS.CONNECTION_TIMEOUT; - String result = Settings.getString(key); + String result = getSettings().getString(key); Assert.assertTrue(result == null); } @@ -158,8 +142,8 @@ public class SettingsTest extends BaseTest { public void testGetInt() throws InvalidSettingException { String key = "SomeNumber"; int expResult = 85; - Settings.setString(key, "85"); - int result = Settings.getInt(key); + getSettings().setString(key, "85"); + int result = getSettings().getInt(key); Assert.assertEquals(expResult, result); } @@ -170,8 +154,8 @@ public class SettingsTest extends BaseTest { public void testGetIntDefault() throws InvalidSettingException { String key = "SomeKey"; int expResult = 85; - Settings.setString(key, "blue"); - int result = Settings.getInt(key, expResult); + getSettings().setString(key, "blue"); + int result = getSettings().getInt(key, expResult); Assert.assertEquals(expResult, result); } @@ -182,8 +166,8 @@ public class SettingsTest extends BaseTest { public void testGetLong() throws InvalidSettingException { String key = "SomeNumber"; long expResult = 300L; - Settings.setString(key, "300"); - long result = Settings.getLong(key); + getSettings().setString(key, "300"); + long result = getSettings().getLong(key); Assert.assertEquals(expResult, result); } @@ -193,14 +177,14 @@ public class SettingsTest extends BaseTest { @Test public void testGetBoolean() throws InvalidSettingException { String key = "SomeBoolean"; - Settings.setString(key, "false"); + getSettings().setString(key, "false"); boolean expResult = false; - boolean result = Settings.getBoolean(key); + boolean result = getSettings().getBoolean(key); Assert.assertEquals(expResult, result); key = "something that does not exist"; expResult = true; - result = Settings.getBoolean(key, true); + result = getSettings().getBoolean(key, true); Assert.assertEquals(expResult, result); } @@ -212,11 +196,11 @@ public class SettingsTest extends BaseTest { String key = "SomeKey"; String value = "value"; String dfault = "default"; - Settings.setString(key, value); - String ret = Settings.getString(key); + getSettings().setString(key, value); + String ret = getSettings().getString(key); Assert.assertEquals(value, ret); - Settings.removeProperty(key); - ret = Settings.getString(key, dfault); + getSettings().removeProperty(key); + ret = getSettings().getString(key, dfault); Assert.assertEquals(dfault, ret); } @@ -225,11 +209,11 @@ public class SettingsTest extends BaseTest { */ @Test public void testGetConnectionString() throws Exception { - String value = Settings.getConnectionString(Settings.KEYS.DB_CONNECTION_STRING, Settings.KEYS.DB_FILE_NAME); + String value = getSettings().getConnectionString(Settings.KEYS.DB_CONNECTION_STRING, Settings.KEYS.DB_FILE_NAME); Assert.assertNotNull(value); String msg = null; try { - value = Settings.getConnectionString("invalidKey", null); + value = getSettings().getConnectionString("invalidKey", null); } catch (InvalidSettingException e) { msg = e.getMessage(); } @@ -241,7 +225,7 @@ public class SettingsTest extends BaseTest { */ @Test public void testGetTempDirectory() throws Exception { - File tmp = Settings.getTempDirectory(); + File tmp = getSettings().getTempDirectory(); Assert.assertTrue(tmp.exists()); } @@ -253,10 +237,10 @@ public class SettingsTest extends BaseTest { public void testGetArrayFromADelimitedString() { // GIVEN a delimited string final String delimitedString = "value1,value2"; - Settings.setString("key", delimitedString); + getSettings().setString("key", delimitedString); // WHEN getting the array - final String[] array = Settings.getArray("key"); + final String[] array = getSettings().getArray("key"); // THEN the split array is returned assertThat("Expected the array to be non-null", array, notNullValue()); @@ -272,7 +256,7 @@ public class SettingsTest extends BaseTest { @Test public void testGetArrayWhereThePropertyIsNotSet() { // WHEN getting the array - final String[] array = Settings.getArray("key"); + final String[] array = getSettings().getArray("key"); // THEN null is returned assertThat("Expected the array to be null", array, nullValue()); @@ -288,10 +272,10 @@ public class SettingsTest extends BaseTest { final String[] array = {}; // WHEN setting the array - Settings.setArrayIfNotEmpty("key", array); + getSettings().setArrayIfNotEmpty("key", array); // THEN the property was not set - assertThat("Expected the property to not be set", Settings.getString("key"), nullValue()); + assertThat("Expected the property to not be set", getSettings().getString("key"), nullValue()); } /** @@ -304,10 +288,10 @@ public class SettingsTest extends BaseTest { final String[] array = null; // WHEN setting the array - Settings.setArrayIfNotEmpty("key", array); + getSettings().setArrayIfNotEmpty("key", array); // THEN the property was not set - assertThat("Expected the property to not be set", Settings.getString("key"), nullValue()); + assertThat("Expected the property to not be set", getSettings().getString("key"), nullValue()); } /** @@ -320,10 +304,10 @@ public class SettingsTest extends BaseTest { final String[] array = {"value1", "value2"}; // WHEN setting the array - Settings.setArrayIfNotEmpty("key", array); + getSettings().setArrayIfNotEmpty("key", array); // THEN the property is set - assertThat("Expected the property to be set", Settings.getString("key"), is("value1,value2")); + assertThat("Expected the property to be set", getSettings().getString("key"), is("value1,value2")); } /** @@ -336,9 +320,9 @@ public class SettingsTest extends BaseTest { final String[] array = {"value1"}; // WHEN setting the array - Settings.setArrayIfNotEmpty("key", array); + getSettings().setArrayIfNotEmpty("key", array); // THEN the property is set - assertThat("Expected the property to be set", Settings.getString("key"), is("value1")); + assertThat("Expected the property to be set", getSettings().getString("key"), is("value1")); } } diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SimplePojo.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SimplePojo.java index b3b5e2504..9926322f6 100644 --- a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SimplePojo.java +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/utils/SimplePojo.java @@ -23,6 +23,10 @@ import java.io.Serializable; * @author jeremy */ public class SimplePojo implements Serializable { + /** + * The serial version UID for serialization. + */ + private static final long serialVersionUID = 1L; public String s = "3"; public Integer i = 3; diff --git a/dependency-check-utils/src/test/resources/dependencycheck.properties b/dependency-check-utils/src/test/resources/dependencycheck.properties index b079d254b..e615f02db 100644 --- a/dependency-check-utils/src/test/resources/dependencycheck.properties +++ b/dependency-check-utils/src/test/resources/dependencycheck.properties @@ -17,7 +17,7 @@ engine.version.url=http://jeremylong.github.io/DependencyCheck/current.txt data.directory=[JAR]/data data.file_name=dc.h2.db data.version=3.0 -data.connection_string=jdbc:h2:file:%s;MV_STORE=FALSE;AUTOCOMMIT=ON;LOCK_MODE=0;FILE_LOCK=NO +data.connection_string=jdbc:h2:file:%s;MV_STORE=FALSE;AUTOCOMMIT=ON; #data.connection_string=jdbc:h2:file:%s;AUTO_SERVER=TRUE;AUTOCOMMIT=ON; #data.connection_string=jdbc:mysql://localhost:3306/dependencycheck diff --git a/pom.xml b/pom.xml index e8920873a..52a31e9df 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ Copyright (c) 2012 - Jeremy Long org.owasp dependency-check-parent - 2.1.2-SNAPSHOT + 3.0.0-SNAPSHOT pom diff --git a/src/main/config/checkstyle-checks.xml b/src/main/config/checkstyle-checks.xml index 3cb0cd41e..f237c7b39 100644 --- a/src/main/config/checkstyle-checks.xml +++ b/src/main/config/checkstyle-checks.xml @@ -197,7 +197,7 @@ - +