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/EngineIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java index 5a78871c1..94f58dba6 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/EngineIntegrationTest.java @@ -71,9 +71,11 @@ public class EngineIntegrationTest 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/CveDBIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBIntegrationTest.java index 54ad59c88..32cf3a9f2 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBIntegrationTest.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; @@ -51,6 +52,9 @@ public class CveDBIntegrationTest extends BaseDBTestCase { instance.commit(); } catch (DatabaseException | SQLException ex) { fail(ex.getMessage()); + } finally { + instance.close(); + assertFalse(instance.isOpen()); } } @@ -64,6 +68,7 @@ public class CveDBIntegrationTest extends BaseDBTestCase { String product = "struts"; Set result = instance.getCPEs(vendor, product); assertTrue(result.size() > 5); + instance.close(); } /** @@ -74,6 +79,7 @@ public class CveDBIntegrationTest 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(); } /** @@ -110,6 +116,7 @@ public class CveDBIntegrationTest extends BaseDBTestCase { } } assertTrue("Expected " + expected + ", but was not identified", found); + instance.close(); } /** @@ -165,5 +172,6 @@ public class CveDBIntegrationTest 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/CveDBMySQLTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBMySQLTest.java index a27ef0191..6915ee6de 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBMySQLTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/CveDBMySQLTest.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; @@ -34,6 +35,23 @@ import org.owasp.dependencycheck.dependency.VulnerableSoftware; */ public class CveDBMySQLTest 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. */ @@ -48,6 +66,8 @@ public class CveDBMySQLTest 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(); } } @@ -64,6 +84,8 @@ public class CveDBMySQLTest 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/DatabasePropertiesIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/DatabasePropertiesIntegrationTest.java index 6c6af8bcf..2a64775ae 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/DatabasePropertiesIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/nvdcve/DatabasePropertiesIntegrationTest.java @@ -41,6 +41,7 @@ public class DatabasePropertiesIntegrationTest 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(); } /** @@ -59,6 +60,7 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase { instance = cveDB.reloadProperties(); long results = Long.parseLong(instance.getProperty("NVD CVE " + key)); assertEquals(expected, results); + cveDB.close(); } /** @@ -73,6 +75,7 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase { String expResult = "default"; String result = instance.getProperty(key, defaultValue); assertEquals(expResult, result); + cveDB.close(); } /** @@ -87,6 +90,7 @@ public class DatabasePropertiesIntegrationTest extends BaseDBTestCase { double version = Double.parseDouble(result); assertTrue(version >= 2.8); assertTrue(version <= 10); + cveDB.close(); } /** @@ -98,5 +102,6 @@ public class DatabasePropertiesIntegrationTest 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/NvdCveUpdaterIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/NvdCveUpdaterIntegrationTest.java index d2305a956..5ebe3351f 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/NvdCveUpdaterIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/data/update/NvdCveUpdaterIntegrationTest.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; @@ -28,7 +28,7 @@ import org.owasp.dependencycheck.data.update.nvd.UpdateableNvdCve; * * @author Jeremy Long */ -public class NvdCveUpdaterIntegrationTest extends BaseTest { +public class NvdCveUpdaterIntegrationTest extends BaseDBTestCase { public NvdCveUpdater getUpdater() { NvdCveUpdater instance = new NvdCveUpdater(); diff --git a/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIntegrationTest.java b/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIntegrationTest.java index f379d332d..f0a7ff01c 100644 --- a/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIntegrationTest.java +++ b/dependency-check-core/src/test/java/org/owasp/dependencycheck/reporting/ReportGeneratorIntegrationTest.java @@ -149,6 +149,7 @@ public class ReportGeneratorIntegrationTest 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