diff --git a/.github/contributing.md b/.github/contributing.md new file mode 100644 index 000000000..6528807ee --- /dev/null +++ b/.github/contributing.md @@ -0,0 +1,34 @@ +# Contributing to OWASP dependency-check + +## Reporting Bugs + +- Ensure you're running the latest version of dependency-check. +- Ensure the bug has not [already been reported](https://github.com/jeremylong/DependencyCheck/issues). +- If you're unable to find an open issue addressing the problem, please [submit a new issue](https://github.com/jeremylong/DependencyCheck/issues/new). + - Please fill out the appropriate section of the bug report template provided. Please delete any sections not needed in the template. + +## Reporting Vulnerabilities + +- If you believe you have found a vulnerability in dependency-check itself (not that dependency-check found a vulnerability); please email jeremy.long@owasp.org. + +## Asking Questions + +- Your question may be answered by taking a look at the [documentataion](https://jeremylong.github.io/DependencyCheck/). +- If you still have a question consider: + - posting to the [Google Group](https://groups.google.com/forum/#!forum/dependency-check) + - opening a [new issue](https://github.com/jeremylong/DependencyCheck/issues/new) + +## Enhancement Requests + +- Suggest changes by [submitting a new issue](https://github.com/jeremylong/DependencyCheck/issues/new) and begin coding. + +## Contributing Code + +- If you have written a new feature or have fixed a bug please open a new pull request with the patch. +- Ensure the PR description clearly describes the problem and solution. Include any related issue number(s) if applicable. +- Please ensure the PR passes the automated checks performed (travis-ci, codacy, etc.) +- Please consider adding test cases for any new functionality + +## Thank you for your contributions + +OWASP dependency-check team \ No newline at end of file diff --git a/.github/issue_template.md b/.github/issue_template.md index f2a77a5bc..c4150a43b 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -1,3 +1,5 @@ +Please delete any un-needed section from the following issue template: + ### Reporting Bugs/Errors When reporting errors, 99% of the time log file output is required. Please post the log file as a [gist](https://gist.github.com/) and provide a link in the new issue. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..fa049cb30 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ +## Fixes Issue # + +## Description of Change + +*Please add a description of the proposed change* + +## Have test cases been added to cover the new functionality? + +*yes/no* \ No newline at end of file diff --git a/README.md b/README.md index d7b1bfb58..87a68fc54 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Dependency-Check is Copyright (c) 2012-2016 Jeremy Long. All Rights Reserved. Permission to modify and redistribute is granted under the terms of the Apache 2.0 license. See the [LICENSE.txt](https://raw.githubusercontent.com/jeremylong/DependencyCheck/master/LICENSE.txt) file for the full license. -Dependency-Check makes use of several other open source libraries. Please see the [NOTICE.txt] [notices] file for more information. +Dependency-Check makes use of several other open source libraries. Please see the [NOTICE.txt][notices] file for more information. [wiki]: https://github.com/jeremylong/DependencyCheck/wiki 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 c68fefdb8..5cc1e8226 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 @@ -941,9 +941,7 @@ public class Check extends Update { } } DatabaseProperties prop = null; - CveDB cve; - try { - cve = CveDB.getInstance(); + try (CveDB cve = CveDB.getInstance()) { prop = cve.getDatabaseProperties(); } catch (DatabaseException ex) { //TODO shouldn't this be a fatal exception 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 de9f62c1e..ffab315f3 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 @@ -31,7 +31,6 @@ import org.owasp.dependencycheck.utils.Settings; import static org.junit.Assert.assertTrue; - /** * * @author Jeremy Long @@ -65,15 +64,11 @@ public class DependencyCheckTaskTest { @Test public void testAddFileSet() throws Exception { File report = new File("target/dependency-check-report.html"); - if (report.exists()) { - if (!report.delete()) { - throw new Exception("Unable to delete 'target/DependencyCheck-Report.html' prior to test."); - } + if (report.exists() && !report.delete()) { + throw new Exception("Unable to delete 'target/DependencyCheck-Report.html' prior to test."); } buildFileRule.executeTarget("test.fileset"); - assertTrue("DependencyCheck report was not generated", report.exists()); - } /** 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 506ae0cac..52c6fce77 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 @@ -282,9 +282,15 @@ public class App { exCol = ex; } final List dependencies = engine.getDependencies(); - final CveDB cve = CveDB.getInstance(); - final DatabaseProperties prop = cve.getDatabaseProperties(); + DatabaseProperties prop = null; + try (CveDB cve = CveDB.getInstance()) { + prop = cve.getDatabaseProperties(); + } catch (DatabaseException ex) { + //TODO shouldn't this be a fatal exception + LOGGER.debug("Unable to retrieve DB Properties", ex); + } final ReportGenerator report = new ReportGenerator(applicationName, dependencies, engine.getAnalyzers(), prop); + try { report.generateReports(reportDirectory, outputFormat); } catch (ReportException ex) { 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 14c9a4e2a..bfbc066c8 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 @@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileFilter; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -83,6 +84,10 @@ public class Engine implements FileFilter { * services. */ private ClassLoader serviceClassLoader = Thread.currentThread().getContextClassLoader(); + /** + * A reference to the database. + */ + private CveDB database = null; /** * The Logger for use throughout the class. */ @@ -126,10 +131,9 @@ public class Engine implements FileFilter { * Properly cleans up resources allocated during analysis. */ public void cleanup() { - try { - CveDB.getInstance().closeDatabase(); - } catch (DatabaseException ex) { - LOGGER.trace("Error closing the database", ex); + if (database != null) { + database.close(); + database = null; } ConnectionFactory.cleanup(); } @@ -483,31 +487,14 @@ public class Engine implements FileFilter { */ public void analyzeDependencies() throws ExceptionCollection { final List exceptions = Collections.synchronizedList(new ArrayList()); - boolean autoUpdate = true; - try { - 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 { - doUpdates(); - } catch (UpdateException ex) { - exceptions.add(ex); - LOGGER.warn("Unable to update Cached Web DataSource, using local " - + "data instead. Results may not include recent vulnerabilities."); - LOGGER.debug("Update Error", ex); - } - } + + initializeAndUpdateDatabase(exceptions); //need to ensure that data exists try { ensureDataExists(); } catch (NoDataException ex) { throwFatalExceptionCollection("Unable to continue dependency-check analysis.", ex, exceptions); - } catch (DatabaseException ex) { - throwFatalExceptionCollection("Unable to connect to the dependency-check database.", ex, exceptions); } LOGGER.debug("\n----------------------------------------------------\nBEGIN ANALYSIS\n----------------------------------------------------"); @@ -554,6 +541,47 @@ public class Engine implements FileFilter { } } + /** + * Performs any necessary updates and initializes the database. + * + * @param exceptions a collection to store non-fatal exceptions + * @throws ExceptionCollection thrown if fatal exceptions occur + */ + private void initializeAndUpdateDatabase(final List exceptions) throws ExceptionCollection { + boolean autoUpdate = true; + try { + 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(); + } catch (UpdateException ex) { + exceptions.add(ex); + LOGGER.warn("Unable to update Cached Web DataSource, using local " + + "data instead. Results may not include recent vulnerabilities."); + LOGGER.debug("Update Error", ex); + } catch (DatabaseException ex) { + throw new ExceptionCollection("Unable to connect to the database", ex); + } + } else { + try { + if (ConnectionFactory.isH2Connection() && !ConnectionFactory.h2DataFileExists()) { + throw new ExceptionCollection(new NoDataException("Autoupdate is disabled and the database does not exist"), true); + } else { + database = CveDB.getInstance(); + } + } catch (IOException ex) { + throw new ExceptionCollection(new DatabaseException("Autoupdate is disabled and unable to connect to the database"), true); + } catch (DatabaseException ex) { + throwFatalExceptionCollection("Unable to connect to the dependency-check database.", ex, exceptions); + } + } + } + /** * Executes executes the analyzer using multiple threads. * @@ -746,12 +774,9 @@ public class Engine implements FileFilter { * NoDataException is thrown. * * @throws NoDataException thrown if no data exists in the CPE Index - * @throws DatabaseException thrown if there is an exception opening the - * database */ - private void ensureDataExists() throws NoDataException, DatabaseException { - final CveDB cve = CveDB.getInstance(); - if (!cve.dataExists()) { + private void ensureDataExists() throws NoDataException { + if (database == null || !database.dataExists()) { throw new NoDataException("No documents exist"); } } 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 eafdb7b1e..2ee63b1f6 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 @@ -843,12 +843,10 @@ public class DependencyCheckScanAgent { */ private void generateExternalReports(Engine engine, File outDirectory) { DatabaseProperties prop = null; - CveDB cve; - try { - cve = CveDB.getInstance(); + try (CveDB cve = CveDB.getInstance()) { prop = cve.getDatabaseProperties(); } catch (DatabaseException ex) { - //TODO shouldn't this throw an exception or return? + //TODO shouldn't this be a fatal exception LOGGER.debug("Unable to retrieve DB Properties", ex); } final ReportGenerator r = new ReportGenerator(this.applicationName, engine.getDependencies(), engine.getAnalyzers(), prop); 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 455e6689c..14177fa9b 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 @@ -182,6 +182,10 @@ public class CPEAnalyzer extends AbstractAnalyzer { */ @Override public void closeAnalyzer() { + if (cve != null) { + cve.close(); + cve = null; + } if (cpe != null) { cpe.close(); cpe = null; 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 10525ee62..c3cdedf2f 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 @@ -68,6 +68,7 @@ public class NvdCveAnalyzer extends AbstractAnalyzer { */ @Override public void closeAnalyzer() { + cveDB.close(); cveDB = null; } 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 f8239eadf..e872dd050 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 @@ -132,7 +132,8 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { LOGGER.info("Launching: " + args + " from " + folder); return builder.start(); } catch (IOException ioe) { - throw new AnalysisException("bundle-audit failure", ioe); + throw new AnalysisException("bundle-audit initialization failure; this error can be ignored if you are not analyzing Ruby. " + + "Otherwise ensure that bundle-audit is installed and the path to bundle audit is correctly specified", ioe); } } @@ -159,7 +160,6 @@ public class RubyBundleAuditAnalyzer extends AbstractFileTypeAnalyzer { } catch (AnalysisException ae) { setEnabled(false); - cvedb = null; final String msg = String.format("Exception from bundle-audit process: %s. Disabling %s", ae.getCause(), ANALYZER_NAME); throw new InitializationException(msg, ae); } catch (IOException ex) { @@ -208,6 +208,17 @@ 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. * 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 b901231b4..4402fd341 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 @@ -241,13 +241,31 @@ public final class ConnectionFactory { * @throws IOException thrown if the data directory does not exist and * cannot be created */ - private static boolean h2DataFileExists() throws IOException { + 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); return file.exists(); } + /** + * 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() { + String connStr; + try { + connStr = Settings.getConnectionString( + Settings.KEYS.DB_CONNECTION_STRING, + Settings.KEYS.DB_FILE_NAME); + } catch (IOException ex) { + LOGGER.debug("Unable to get connectionn string", ex); + return false; + } + return connStr.startsWith("jdbc:h2:file:"); + } + /** * Creates the database structure (tables and indexes) to store the CVE * data. 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 0a966a24b..bb32ae907 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 @@ -58,12 +58,17 @@ import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb * @author Jeremy Long */ @ThreadSafe -public final class CveDB { +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. */ @@ -75,7 +80,7 @@ public final class CveDB { /** * The bundle of statements used when accessing the database. */ - private final ResourceBundle statementBundle; + private ResourceBundle statementBundle; /** * Database properties object containing the 'properties' from the database * table. @@ -84,7 +89,7 @@ public final class CveDB { /** * The prepared statements. */ - private final EnumMap preparedStatements; + private final EnumMap preparedStatements = new EnumMap<>(PreparedStatementCveDb.class); /** * The enum value names must match the keys of the statements in the @@ -191,6 +196,10 @@ public final class CveDB { if (instance == null) { instance = new CveDB(); } + if (!instance.isOpen()) { + instance.open(); + } + instance.usageCount += 1; return instance; } @@ -202,23 +211,17 @@ public final class CveDB { * database. */ private CveDB() throws DatabaseException { - openDatabase(); - final String databaseProductName = determineDatabaseProductName(); - statementBundle = databaseProductName != null - ? ResourceBundle.getBundle("data/dbStatements", new Locale(databaseProductName)) - : ResourceBundle.getBundle("data/dbStatements"); - preparedStatements = prepareStatements(); - databaseProperties = new DatabaseProperties(this); } /** * Tries to determine the product name of the database. * + * @param conn the database connection * @return the product name of the database if successful, {@code null} else */ - private synchronized String determineDatabaseProductName() { + private static String determineDatabaseProductName(Connection conn) { try { - final String databaseProductName = connection.getMetaData().getDatabaseProductName(); + final String databaseProductName = conn.getMetaData().getDatabaseProductName(); LOGGER.debug("Database product: {}", databaseProductName); return databaseProductName; } catch (SQLException se) { @@ -234,30 +237,43 @@ public final class CveDB { * @throws DatabaseException thrown if there is an error opening the * database connection */ - public synchronized void openDatabase() throws DatabaseException { - if (!isOpen()) { - connection = ConnectionFactory.getConnection(); + private synchronized void open() throws DatabaseException { + if (!instance.isOpen()) { + instance.connection = ConnectionFactory.getConnection(); + final String databaseProductName = determineDatabaseProductName(instance.connection); + instance.statementBundle = databaseProductName != null + ? ResourceBundle.getBundle("data/dbStatements", new Locale(databaseProductName)) + : ResourceBundle.getBundle("data/dbStatements"); + instance.prepareStatements(); + instance.databaseProperties = new DatabaseProperties(instance); } } /** - * Closes the DB4O database. Close should be called on this object when it - * is done being used. + * Closes the database connection. Close should be called on this object + * when it is done being used. */ - public synchronized void closeDatabase() { - if (isOpen()) { - 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); + @Override + public synchronized void close() { + if (instance != null) { + instance.usageCount -= 1; + if (instance.usageCount <= 0 && instance.isOpen()) { + instance.usageCount = 0; + 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); + } + instance.statementBundle = null; + instance.preparedStatements.clear(); + instance.databaseProperties = null; + instance.connection = null; } - connection = null; - instance = null; } } @@ -266,21 +282,17 @@ public final class CveDB { * * @return whether the database connection is open or closed */ - private boolean isOpen() { + protected synchronized boolean isOpen() { return connection != null; } /** - * Prepares all statements to be used and returns them. + * Prepares all statements to be used. * - * @return the prepared statements * @throws DatabaseException thrown if there is an error preparing the * statements */ - private EnumMap prepareStatements() - throws DatabaseException { - - final EnumMap result = new EnumMap<>(PreparedStatementCveDb.class); + private void prepareStatements() throws DatabaseException { for (PreparedStatementCveDb key : values()) { final String statementString = statementBundle.getString(key.name()); final PreparedStatement preparedStatement; @@ -293,15 +305,14 @@ public final class CveDB { } catch (SQLException exception) { throw new DatabaseException(exception); } - result.put(key, preparedStatement); + preparedStatements.put(key, preparedStatement); } - return result; } /** * Closes all prepared statements. */ - private void closeStatements() { + private synchronized void closeStatements() { for (PreparedStatement preparedStatement : preparedStatements.values()) { DBUtils.closeStatement(preparedStatement); } @@ -315,7 +326,7 @@ public final class CveDB { * @return the prepared statement * @throws SQLException thrown if a SQL Exception occurs */ - private PreparedStatement getPreparedStatement(PreparedStatementCveDb key) throws SQLException { + private synchronized PreparedStatement getPreparedStatement(PreparedStatementCveDb key) throws SQLException { final PreparedStatement preparedStatement = preparedStatements.get(key); preparedStatement.clearParameters(); return preparedStatement; @@ -342,7 +353,7 @@ public final class CveDB { @SuppressWarnings("FinalizeDeclaration") protected void finalize() throws Throwable { LOGGER.debug("Entering finalize"); - closeDatabase(); + close(); super.finalize(); } @@ -351,7 +362,7 @@ public final class CveDB { * * @return the value of databaseProperties */ - public DatabaseProperties getDatabaseProperties() { + public synchronized DatabaseProperties getDatabaseProperties() { return databaseProperties; } @@ -360,7 +371,7 @@ public final class CveDB { * * @return the database properties */ - protected DatabaseProperties reloadProperties() { + protected synchronized DatabaseProperties reloadProperties() { databaseProperties = new DatabaseProperties(this); return databaseProperties; } 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 ad25e6ae4..2bd2eb5ef 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 @@ -93,7 +93,7 @@ public class EngineVersionCheck implements CachedWebDataSource { */ @Override public void update() throws UpdateException { - try { + 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); @@ -105,7 +105,9 @@ public class EngineVersionCheck implements CachedWebDataSource { */ if (enabled && autoupdate && original != null && original.equals(current)) { LOGGER.debug("Begin Engine Version Check"); - final DatabaseProperties properties = CveDB.getInstance().getDatabaseProperties(); + + final DatabaseProperties properties = db.getDatabaseProperties(); + final long lastChecked = Long.parseLong(properties.getProperty(ENGINE_VERSION_CHECKED_ON, "0")); final long now = System.currentTimeMillis(); updateToVersion = properties.getProperty(CURRENT_ENGINE_RELEASE, ""); 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 80ebb1bf6..2ae21797c 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 @@ -104,17 +104,17 @@ public class NvdCveUpdater implements CachedWebDataSource { LOGGER.trace("invalid setting UPDATE_NVDCVE_ENABLED", ex); } + boolean autoUpdate = true; + try { + autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); + } catch (InvalidSettingException ex) { + LOGGER.debug("Invalid setting for auto-update; using true."); + } + if (!autoUpdate) { + return; + } + initializeExecutorServices(); try { - boolean autoUpdate = true; - try { - autoUpdate = Settings.getBoolean(Settings.KEYS.AUTO_UPDATE); - } catch (InvalidSettingException ex) { - LOGGER.debug("Invalid setting for auto-update; using true."); - } - if (!autoUpdate) { - return; - } - initializeExecutorServices(); cveDb = CveDB.getInstance(); dbProperties = cveDb.getDatabaseProperties(); @@ -139,6 +139,7 @@ public class NvdCveUpdater implements CachedWebDataSource { throw new UpdateException("Database Exception, unable to update the data to use the most current data.", ex); } finally { shutdownExecutorServices(); + cveDb.close(); } } @@ -201,8 +202,7 @@ public class NvdCveUpdater implements CachedWebDataSource { * @return true if the database contains data */ private boolean dataExists() { - try { - final CveDB cve = CveDB.getInstance(); + try (CveDB cve = CveDB.getInstance()) { return cve.dataExists(); } catch (DatabaseException ex) { return 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 6264b6ebf..bfcb5cb5a 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 @@ -25,9 +25,7 @@ import java.io.FileOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.commons.compress.utils.IOUtils; -import org.junit.AfterClass; import org.junit.Before; -import org.owasp.dependencycheck.data.nvdcve.CveDB; import org.owasp.dependencycheck.utils.Settings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,11 +47,6 @@ public abstract class BaseDBTestCase extends BaseTest { ensureDBExists(); } - @AfterClass - public static void tearDownClass() throws Exception { - CveDB.getInstance().closeDatabase(); - } - public static void ensureDBExists() throws Exception { File f = new File("./target/data/dc.h2.db"); if (f.exists() && f.isFile() && f.length() < 71680) { 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 774748e78..dd81bf828 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 @@ -72,9 +72,11 @@ public class EngineIT extends BaseDBTestCase { throw ex; } } - CveDB cveDB = CveDB.getInstance(); - DatabaseProperties dbProp = cveDB.getDatabaseProperties(); - ReportGenerator rg = new ReportGenerator("DependencyCheck", instance.getDependencies(), instance.getAnalyzers(), dbProp); + DatabaseProperties prop = null; + try (CveDB cve = CveDB.getInstance()) { + prop = cve.getDatabaseProperties(); + } + ReportGenerator rg = new ReportGenerator("DependencyCheck", instance.getDependencies(), instance.getAnalyzers(), prop); rg.generateReports("./target/", "ALL"); instance.cleanup(); } 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/RubyBundleAuditAnalyzerTest.java index ff5a4a9e1..ea986dfbf 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/RubyBundleAuditAnalyzerTest.java @@ -28,6 +28,7 @@ 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; @@ -82,8 +83,10 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { */ @After public void tearDown() throws Exception { - analyzer.close(); - analyzer = null; + if (analyzer != null) { + analyzer.close(); + analyzer = null; + } } /** @@ -166,6 +169,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { 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."); @@ -190,6 +194,7 @@ public class RubyBundleAuditAnalyzerTest extends BaseDBTestCase { 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.size() + " dependencies found."); 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 d73942304..e65f1b917 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 @@ -29,6 +29,7 @@ import java.util.Map.Entry; import java.util.Set; 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; @@ -66,6 +67,9 @@ public class CveDBIT extends BaseDBTestCase { instance.commit(); } catch (DatabaseException | SQLException ex) { fail(ex.getMessage()); + } finally { + instance.close(); + assertFalse(instance.isOpen()); } } @@ -79,6 +83,7 @@ public class CveDBIT extends BaseDBTestCase { String product = "struts"; Set result = instance.getCPEs(vendor, product); assertTrue(result.size() > 5); + instance.close(); } /** @@ -89,6 +94,7 @@ public class CveDBIT extends BaseDBTestCase { 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(); } /** @@ -125,6 +131,7 @@ public class CveDBIT extends BaseDBTestCase { } } assertTrue("Expected " + expected + ", but was not identified", found); + instance.close(); } /** @@ -180,5 +187,6 @@ 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 04bb6af5e..b38419a0d 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 @@ -19,6 +19,7 @@ package org.owasp.dependencycheck.data.nvdcve; import java.util.List; import java.util.Set; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -36,6 +37,23 @@ import static org.junit.Assert.fail; */ public class CveDBMySqlIT extends BaseTest { + /** + * 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) { + 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 { + instance.close(); + assertFalse(instance.isOpen()); + } + } + /** * Test of getCPEs method, of class CveDB. */ @@ -50,6 +68,8 @@ 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(); } } @@ -66,6 +86,8 @@ 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(); } } } 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 5243018c4..1d4b9ac1b 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 @@ -44,6 +44,7 @@ public class DatabasePropertiesIT extends BaseDBTestCase { assertNotNull(instance); //no exception means the call worked... whether or not it is empty depends on if the db is new //assertEquals(expResult, result); + cveDB.close(); } /** @@ -62,6 +63,7 @@ public class DatabasePropertiesIT extends BaseDBTestCase { instance = cveDB.reloadProperties(); long results = Long.parseLong(instance.getProperty("NVD CVE " + key)); assertEquals(expected, results); + cveDB.close(); } /** @@ -76,6 +78,7 @@ public class DatabasePropertiesIT extends BaseDBTestCase { String expResult = "default"; String result = instance.getProperty(key, defaultValue); assertEquals(expResult, result); + cveDB.close(); } /** @@ -90,6 +93,7 @@ public class DatabasePropertiesIT extends BaseDBTestCase { double version = Double.parseDouble(result); assertTrue(version >= 2.8); assertTrue(version <= 10); + cveDB.close(); } /** @@ -101,5 +105,6 @@ public class DatabasePropertiesIT extends BaseDBTestCase { DatabaseProperties instance = cveDB.getDatabaseProperties(); Properties result = instance.getProperties(); assertTrue(result.size() > 0); + cveDB.close(); } } 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 3211ff214..2d398d02d 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 @@ -20,7 +20,7 @@ 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.BaseTest; +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; @@ -30,7 +30,7 @@ import static org.junit.Assert.fail; * * @author Jeremy Long */ -public class NvdCveUpdaterIT extends BaseTest { +public class NvdCveUpdaterIT extends BaseDBTestCase { public NvdCveUpdater getUpdater() { NvdCveUpdater instance = new NvdCveUpdater(); 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 af638db7d..63d78ec6e 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 @@ -150,6 +150,7 @@ public class ReportGeneratorIT extends BaseDBTestCase { ReportGenerator generator = new ReportGenerator("Test Report", engine.getDependencies(), engine.getAnalyzers(), dbProp); generator.generateReport(templateName, writeTo); + cveDB.close(); engine.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 d2e95260c..76b74d9dd 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 @@ -1007,8 +1007,7 @@ public abstract class BaseDependencyCheckMojo extends AbstractMojo implements Ma */ protected void writeReports(Engine engine, MavenProject p, File outputDir) throws ReportException { DatabaseProperties prop = null; - try { - final CveDB cve = CveDB.getInstance(); + try (CveDB cve = CveDB.getInstance()) { prop = cve.getDatabaseProperties(); } catch (DatabaseException ex) { //TODO shouldn't this throw an exception? diff --git a/src/site/markdown/dependency-check-gradle/configuration-purge.md b/src/site/markdown/dependency-check-gradle/configuration-purge.md index 00b5bba6d..3e4c136ce 100644 --- a/src/site/markdown/dependency-check-gradle/configuration-purge.md +++ b/src/site/markdown/dependency-check-gradle/configuration-purge.md @@ -9,6 +9,11 @@ dependencyCheckPurge | Deletes the local copy of Configuration: dependencyCheckPurge ==================== +The following properties can be configured for the dependencyCheckPurge task: + +Property | Description | Default Value +---------------------|------------------------------------|------------------ +failOnError | Fails the build if an error occurs during the dependency-check analysis. | true #### Example ```groovy diff --git a/src/site/markdown/dependency-check-gradle/configuration-update.md b/src/site/markdown/dependency-check-gradle/configuration-update.md index e83f2dc03..f3ab37d35 100644 --- a/src/site/markdown/dependency-check-gradle/configuration-update.md +++ b/src/site/markdown/dependency-check-gradle/configuration-update.md @@ -14,6 +14,7 @@ The following properties can be configured for the dependencyCheckUpdate task: Property | Description | Default Value ---------------------|------------------------------------|------------------ cveValidForHours | Sets the number of hours to wait before checking for new updates from the NVD. | 4 +failOnError | Fails the build if an error occurs during the dependency-check analysis. | true #### Example ```groovy diff --git a/src/site/markdown/dependency-check-gradle/configuration.md b/src/site/markdown/dependency-check-gradle/configuration.md index 7e4183d74..f33730a01 100644 --- a/src/site/markdown/dependency-check-gradle/configuration.md +++ b/src/site/markdown/dependency-check-gradle/configuration.md @@ -15,6 +15,7 @@ Property | Description | Default Value ---------------------|------------------------------------|------------------ autoUpdate | Sets whether auto-updating of the NVD CVE/CPE data is enabled. It is not recommended that this be turned to false. | true cveValidForHours | Sets the number of hours to wait before checking for new updates from the NVD. | 4 +failOnError | Fails the build if an error occurs during the dependency-check analysis. | true failBuildOnCVSS | Specifies if the build should be failed if a CVSS score above a specified level is identified. The default is 11; since the CVSS scores are 0-10, by default the build will never fail. | 11 format | The report format to be generated (HTML, XML, VULN, ALL). | HTML outputDirectory | The location to write the report(s). This directory will be located in the build directory. | build/reports