Compare commits

...

10 Commits

Author SHA1 Message Date
Šesták Vít
3bdc6988b3 Minor adjustments of caching for CveDB, based on code review 2017-05-24 16:00:37 +02:00
Šesták Vít
e86225c1e1 Added some caching for CveDB in order to speedup some scans 2017-05-24 09:24:40 +02:00
Jeremy Long
dccbd659ed Merge pull request #741 from sethjackson/patch-1
Update Windows usage.
2017-05-23 21:02:41 -04:00
Jeremy Long
0540474e0c Merge pull request #742 from sethjackson/patch-2
Fix wording.
2017-05-23 20:31:17 -04:00
Seth Jackson
2dbfba9ac5 Fix wording. 2017-05-23 16:01:11 -04:00
Seth Jackson
8eff628303 Update Windows usage.
So that the example invocation works in PowerShell and cmd.
2017-05-23 14:43:02 -04:00
Jeremy Long
519167bf0f Merge pull request #739 from jeremylong/issue696-cli
Fix Boolean Properties in CLI
2017-05-21 19:32:38 -04:00
Jeremy Long
e8e06d12c7 updated for code coverage 2017-05-21 07:25:42 -04:00
Jeremy Long
006224b52c Merge branch 'master' into issue696-cli 2017-05-20 07:38:12 -04:00
Jeremy Long
6007be1b5f updated to resolve issue #696 2017-05-20 07:37:46 -04:00
9 changed files with 269 additions and 45 deletions

View File

@@ -29,8 +29,8 @@ $ ./bin/dependency-check.sh --project Testing --out . --scan [path to jar files
``` ```
On Windows On Windows
``` ```
> bin/dependency-check.bat -h > .\bin\dependency-check.bat -h
> bin/dependency-check.bat --project Testing --out . --scan [path to jar files to be scanned] > .\bin\dependency-check.bat --project Testing --out . --scan [path to jar files to be scanned]
``` ```
On Mac with [Homebrew](http://brew.sh) On Mac with [Homebrew](http://brew.sh)
``` ```

View File

@@ -353,8 +353,7 @@ public class App {
* @throws InvalidSettingException thrown when a user defined properties * @throws InvalidSettingException thrown when a user defined properties
* file is unable to be loaded. * file is unable to be loaded.
*/ */
private void populateSettings(CliParser cli) throws InvalidSettingException { protected void populateSettings(CliParser cli) throws InvalidSettingException {
final boolean autoUpdate = cli.isAutoUpdate();
final String connectionTimeout = cli.getConnectionTimeout(); final String connectionTimeout = cli.getConnectionTimeout();
final String proxyServer = cli.getProxyServer(); final String proxyServer = cli.getProxyServer();
final String proxyPort = cli.getProxyPort(); final String proxyPort = cli.getProxyPort();
@@ -377,7 +376,8 @@ public class App {
final String cveBase12 = cli.getBaseCve12Url(); final String cveBase12 = cli.getBaseCve12Url();
final String cveBase20 = cli.getBaseCve20Url(); final String cveBase20 = cli.getBaseCve20Url();
final Integer cveValidForHours = cli.getCveValidForHours(); final Integer cveValidForHours = cli.getCveValidForHours();
final boolean experimentalEnabled = cli.isExperimentalEnabled(); final Boolean autoUpdate = cli.isAutoUpdate();
final Boolean experimentalEnabled = cli.isExperimentalEnabled();
if (propertiesFile != null) { if (propertiesFile != null) {
try { try {
@@ -390,7 +390,7 @@ public class App {
} }
// We have to wait until we've merged the properties before attempting to set whether we use // We have to wait until we've merged the properties before attempting to set whether we use
// the proxy for Nexus since it could be disabled in the properties, but not explicitly stated // the proxy for Nexus since it could be disabled in the properties, but not explicitly stated
// on the command line // on the command line. This is true of other boolean values set below not using the setBooleanIfNotNull.
final boolean nexusUsesProxy = cli.isNexusUsesProxy(); final boolean nexusUsesProxy = cli.isNexusUsesProxy();
if (dataDirectory != null) { if (dataDirectory != null) {
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory); Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDirectory);
@@ -404,7 +404,7 @@ public class App {
final File dataDir = new File(base, sub); final File dataDir = new File(base, sub);
Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath()); Settings.setString(Settings.KEYS.DATA_DIRECTORY, dataDir.getAbsolutePath());
} }
Settings.setBoolean(Settings.KEYS.AUTO_UPDATE, autoUpdate); Settings.setBooleanIfNotNull(Settings.KEYS.AUTO_UPDATE, autoUpdate);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer); Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_SERVER, proxyServer);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort); Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_PORT, proxyPort);
Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser); Settings.setStringIfNotEmpty(Settings.KEYS.PROXY_USERNAME, proxyUser);
@@ -415,7 +415,8 @@ public class App {
Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours); Settings.setIntIfNotNull(Settings.KEYS.CVE_CHECK_VALID_FOR_HOURS, cveValidForHours);
//File Type Analyzer Settings //File Type Analyzer Settings
Settings.setBoolean(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_JAR_ENABLED, !cli.isJarDisabled());
Settings.setBoolean(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, !cli.isArchiveDisabled()); 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_DISTRIBUTION_ENABLED, !cli.isPythonDistributionDisabled());

View File

@@ -567,6 +567,32 @@ public final class CliParser {
return value; return value;
} }
/**
* Utility method to determine if one of the disable options has been set.
* If not set, this method will check the currently configured settings for
* the current value to return.
*
* Example given `--disableArchive` on the command line would cause this
* method to return true for the disable archive setting.
*
* @param argument the command line argument
* @param setting the corresponding settings key
* @return true if the disable option was set, if not set the currently
* configured value will be returned
*/
private boolean hasDisableOption(String argument, String setting) {
if (line == null || !line.hasOption(argument)) {
try {
return !Settings.getBoolean(setting);
} catch (InvalidSettingException ise) {
LOGGER.warn("Invalid property setting '{}' defaulting to false", setting);
return false;
}
} else {
return true;
}
}
/** /**
* Returns true if the disableJar command line argument was specified. * Returns true if the disableJar command line argument was specified.
* *
@@ -574,7 +600,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isJarDisabled() { public boolean isJarDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_JAR); return hasDisableOption(ARGUMENT.DISABLE_JAR, Settings.KEYS.ANALYZER_JAR_ENABLED);
} }
/** /**
@@ -584,7 +610,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isArchiveDisabled() { public boolean isArchiveDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_ARCHIVE); return hasDisableOption(ARGUMENT.DISABLE_ARCHIVE, Settings.KEYS.ANALYZER_ARCHIVE_ENABLED);
} }
/** /**
@@ -594,7 +620,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isNuspecDisabled() { public boolean isNuspecDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NUSPEC); return hasDisableOption(ARGUMENT.DISABLE_NUSPEC, Settings.KEYS.ANALYZER_NUSPEC_ENABLED);
} }
/** /**
@@ -604,7 +630,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isAssemblyDisabled() { public boolean isAssemblyDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_ASSEMBLY); return hasDisableOption(ARGUMENT.DISABLE_ASSEMBLY, Settings.KEYS.ANALYZER_ASSEMBLY_ENABLED);
} }
/** /**
@@ -615,7 +641,7 @@ public final class CliParser {
* specified; otherwise false * specified; otherwise false
*/ */
public boolean isBundleAuditDisabled() { public boolean isBundleAuditDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_BUNDLE_AUDIT); return hasDisableOption(ARGUMENT.DISABLE_BUNDLE_AUDIT, Settings.KEYS.ANALYZER_BUNDLE_AUDIT_ENABLED);
} }
/** /**
@@ -625,7 +651,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isPythonDistributionDisabled() { public boolean isPythonDistributionDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_DIST); return hasDisableOption(ARGUMENT.DISABLE_PY_DIST, Settings.KEYS.ANALYZER_PYTHON_DISTRIBUTION_ENABLED);
} }
/** /**
@@ -635,7 +661,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isPythonPackageDisabled() { public boolean isPythonPackageDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_PY_PKG); return hasDisableOption(ARGUMENT.DISABLE_PY_PKG, Settings.KEYS.ANALYZER_PYTHON_PACKAGE_ENABLED);
} }
/** /**
@@ -645,7 +671,7 @@ public final class CliParser {
* argument was specified; otherwise false * argument was specified; otherwise false
*/ */
public boolean isRubyGemspecDisabled() { public boolean isRubyGemspecDisabled() {
return (null != line) && line.hasOption(ARGUMENT.DISABLE_RUBYGEMS); return hasDisableOption(ARGUMENT.DISABLE_RUBYGEMS, Settings.KEYS.ANALYZER_RUBY_GEMSPEC_ENABLED);
} }
/** /**
@@ -655,7 +681,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isCmakeDisabled() { public boolean isCmakeDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_CMAKE); return hasDisableOption(ARGUMENT.DISABLE_CMAKE, Settings.KEYS.ANALYZER_CMAKE_ENABLED);
} }
/** /**
@@ -665,7 +691,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isAutoconfDisabled() { public boolean isAutoconfDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_AUTOCONF); return hasDisableOption(ARGUMENT.DISABLE_AUTOCONF, Settings.KEYS.ANALYZER_AUTOCONF_ENABLED);
} }
/** /**
@@ -675,7 +701,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isComposerDisabled() { public boolean isComposerDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_COMPOSER); return hasDisableOption(ARGUMENT.DISABLE_COMPOSER, Settings.KEYS.ANALYZER_COMPOSER_LOCK_ENABLED);
} }
/** /**
@@ -685,7 +711,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isNexusDisabled() { public boolean isNexusDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NEXUS); return hasDisableOption(ARGUMENT.DISABLE_NEXUS, Settings.KEYS.ANALYZER_NEXUS_ENABLED);
} }
/** /**
@@ -695,7 +721,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isOpenSSLDisabled() { public boolean isOpenSSLDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_OPENSSL); return hasDisableOption(ARGUMENT.DISABLE_OPENSSL, Settings.KEYS.ANALYZER_OPENSSL_ENABLED);
} }
/** /**
@@ -705,7 +731,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isNodeJsDisabled() { public boolean isNodeJsDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_NODE_JS); return hasDisableOption(ARGUMENT.DISABLE_NODE_JS, Settings.KEYS.ANALYZER_NODE_PACKAGE_ENABLED);
} }
/** /**
@@ -716,7 +742,7 @@ public final class CliParser {
* specified; otherwise false * specified; otherwise false
*/ */
public boolean isCocoapodsAnalyzerDisabled() { public boolean isCocoapodsAnalyzerDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_COCOAPODS); return hasDisableOption(ARGUMENT.DISABLE_COCOAPODS, Settings.KEYS.ANALYZER_COCOAPODS_ENABLED);
} }
/** /**
@@ -727,7 +753,7 @@ public final class CliParser {
* argument was specified; otherwise false * argument was specified; otherwise false
*/ */
public boolean isSwiftPackageAnalyzerDisabled() { public boolean isSwiftPackageAnalyzerDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_SWIFT); return hasDisableOption(ARGUMENT.DISABLE_SWIFT, Settings.KEYS.ANALYZER_SWIFT_PACKAGE_MANAGER_ENABLED);
} }
/** /**
@@ -737,7 +763,7 @@ public final class CliParser {
* otherwise false * otherwise false
*/ */
public boolean isCentralDisabled() { public boolean isCentralDisabled() {
return (line != null) && line.hasOption(ARGUMENT.DISABLE_CENTRAL); return hasDisableOption(ARGUMENT.DISABLE_CENTRAL, Settings.KEYS.ANALYZER_CENTRAL_ENABLED);
} }
/** /**
@@ -1029,10 +1055,10 @@ public final class CliParser {
* disabled via the command line this will return false. * disabled via the command line this will return false.
* *
* @return <code>true</code> if auto-update is allowed; otherwise * @return <code>true</code> if auto-update is allowed; otherwise
* <code>false</code> * <code>null</code>
*/ */
public boolean isAutoUpdate() { public Boolean isAutoUpdate() {
return line != null && !line.hasOption(ARGUMENT.DISABLE_AUTO_UPDATE); return (line != null && line.hasOption(ARGUMENT.DISABLE_AUTO_UPDATE)) ? false : null;
} }
/** /**
@@ -1134,10 +1160,10 @@ public final class CliParser {
/** /**
* Returns true if the experimental analyzers are enabled. * Returns true if the experimental analyzers are enabled.
* *
* @return true if the experimental analyzers are enabled; otherwise false * @return true if the experimental analyzers are enabled; otherwise null
*/ */
public boolean isExperimentalEnabled() { public Boolean isExperimentalEnabled() {
return line.hasOption(ARGUMENT.EXPERIMENTAL); return (line != null && line.hasOption(ARGUMENT.EXPERIMENTAL)) ? true : null;
} }
/** /**

View File

@@ -13,18 +13,28 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* *
* Copyright (c) 2015 The OWASP Foundatio. All Rights Reserved. * Copyright (c) 2017 The OWASP Foundatio. All Rights Reserved.
*/ */
package org.owasp.dependencycheck; package org.owasp.dependencycheck;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.owasp.dependencycheck.utils.InvalidSettingException;
import org.owasp.dependencycheck.utils.Settings;
/** /**
* *
* @author jeremy * @author jeremy
*/ */
public class AppTest { public class AppTest {
/** /**
* Test of ensureCanonicalPath method, of class App. * Test of ensureCanonicalPath method, of class App.
*/ */
@@ -35,17 +45,83 @@ public class AppTest {
String result = instance.ensureCanonicalPath(file); String result = instance.ensureCanonicalPath(file);
assertFalse(result.contains("..")); assertFalse(result.contains(".."));
assertTrue(result.endsWith("*.jar")); assertTrue(result.endsWith("*.jar"));
}
/** file = "../some/skip/../path/file.txt";
* Test of ensureCanonicalPath method, of class App.
*/
@Test
public void testEnsureCanonicalPath2() {
String file = "../some/skip/../path/file.txt";
App instance = new App();
String expResult = "/some/path/file.txt"; String expResult = "/some/path/file.txt";
String result = instance.ensureCanonicalPath(file); result = instance.ensureCanonicalPath(file);
assertTrue("result=" + result, result.endsWith(expResult)); assertTrue("result=" + result, result.endsWith(expResult));
} }
@Test(expected = UnrecognizedOptionException.class)
public void testPopulateSettingsException() throws FileNotFoundException, ParseException, InvalidSettingException, URISyntaxException {
String[] args = {"-invalidPROPERTY"};
assertTrue(testBooleanProperties(args, null));
}
@Test
public void testPopulateSettings() throws FileNotFoundException, ParseException, InvalidSettingException, URISyntaxException {
File prop = new File(this.getClass().getClassLoader().getResource("sample.properties").toURI().getPath());
String[] args = {"-P", prop.getAbsolutePath()};
Map<String, Boolean> expected = new HashMap<>();
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.TRUE);
assertTrue(testBooleanProperties(args, expected));
String[] args2 = {"-n"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.TRUE);
assertTrue(testBooleanProperties(args2, expected));
String[] args3 = {"-h"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.TRUE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.TRUE);
assertTrue(testBooleanProperties(args3, expected));
String[] args4 = {"--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.TRUE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args4, expected));
String[] args5 = {"-P", prop.getAbsolutePath(), "--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args5, expected));
prop = new File(this.getClass().getClassLoader().getResource("sample2.properties").toURI().getPath());
String[] args6 = {"-P", prop.getAbsolutePath(), "--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.TRUE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args6, expected));
String[] args7 = {"-P", prop.getAbsolutePath(), "--noupdate"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args7, expected));
String[] args8 = {"-P", prop.getAbsolutePath(), "--noupdate", "--disableArchive"};
expected.put(Settings.KEYS.AUTO_UPDATE, Boolean.FALSE);
expected.put(Settings.KEYS.ANALYZER_ARCHIVE_ENABLED, Boolean.FALSE);
assertTrue(testBooleanProperties(args8, expected));
}
private boolean testBooleanProperties(String[] args, Map<String, Boolean> 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<String, Boolean> entry : expected.entrySet()) {
results &= Settings.getBoolean(entry.getKey()) == entry.getValue();
}
return results;
} finally {
Settings.cleanup();
}
}
} }

View File

@@ -0,0 +1,33 @@
autoupdate=false
analyzer.experimental.enabled=false
analyzer.jar.enabled=true
analyzer.archive.enabled=true
analyzer.node.package.enabled=true
analyzer.composer.lock.enabled=true
analyzer.python.distribution.enabled=true
analyzer.python.package.enabled=true
analyzer.ruby.gemspec.enabled=true
analyzer.autoconf.enabled=true
analyzer.cmake.enabled=true
analyzer.assembly.enabled=true
analyzer.nuspec.enabled=true
analyzer.openssl.enabled=true
analyzer.central.enabled=true
analyzer.nexus.enabled=false
analyzer.cocoapods.enabled=true
analyzer.swift.package.manager.enabled=true
#whether the nexus analyzer uses the proxy
analyzer.nexus.proxy=true
analyzer.cpe.enabled=true
analyzer.cpesuppression.enabled=true
analyzer.dependencybundling.enabled=true
analyzer.dependencymerging.enabled=true
analyzer.falsepositive.enabled=true
analyzer.filename.enabled=true
analyzer.hint.enabled=true
analyzer.nvdcve.enabled=true
analyzer.vulnerabilitysuppression.enabled=true
updater.nvdcve.enabled=true
updater.versioncheck.enabled=true
analyzer.versionfilter.enabled=true

View File

@@ -0,0 +1,33 @@
autoupdate=true
analyzer.experimental.enabled=true
analyzer.jar.enabled=false
analyzer.archive.enabled=false
analyzer.node.package.enabled=false
analyzer.composer.lock.enabled=false
analyzer.python.distribution.enabled=false
analyzer.python.package.enabled=false
analyzer.ruby.gemspec.enabled=false
analyzer.autoconf.enabled=false
analyzer.cmake.enabled=false
analyzer.assembly.enabled=false
analyzer.nuspec.enabled=false
analyzer.openssl.enabled=false
analyzer.central.enabled=false
analyzer.nexus.enabled=true
analyzer.cocoapods.enabled=false
analyzer.swift.package.manager.enabled=false
#whether the nexus analyzer uses the proxy
analyzer.nexus.proxy=false
analyzer.cpe.enabled=false
analyzer.cpesuppression.enabled=false
analyzer.dependencybundling.enabled=false
analyzer.dependencymerging.enabled=false
analyzer.falsepositive.enabled=false
analyzer.filename.enabled=false
analyzer.hint.enabled=false
analyzer.nvdcve.enabled=false
analyzer.vulnerabilitysuppression.enabled=false
updater.nvdcve.enabled=false
updater.versioncheck.enabled=false
analyzer.versionfilter.enabled=false

View File

@@ -24,6 +24,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@@ -36,6 +37,7 @@ import java.util.Properties;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.Set; import java.util.Set;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import org.apache.commons.collections.map.ReferenceMap;
import org.owasp.dependencycheck.data.cwe.CweDB; import org.owasp.dependencycheck.data.cwe.CweDB;
import org.owasp.dependencycheck.dependency.Reference; import org.owasp.dependencycheck.dependency.Reference;
import org.owasp.dependencycheck.dependency.Vulnerability; import org.owasp.dependencycheck.dependency.Vulnerability;
@@ -48,6 +50,8 @@ import org.owasp.dependencycheck.utils.Settings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
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.*; import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*;
/** /**
@@ -91,6 +95,9 @@ public final class CveDB implements AutoCloseable {
*/ */
private final EnumMap<PreparedStatementCveDb, PreparedStatement> preparedStatements = new EnumMap<>(PreparedStatementCveDb.class); private final EnumMap<PreparedStatementCveDb, PreparedStatement> preparedStatements = new EnumMap<>(PreparedStatementCveDb.class);
@SuppressWarnings("unchecked")
private final Map<String, List<Vulnerability>> vulnerabilitiesForCpeCache = Collections.synchronizedMap(new ReferenceMap(HARD, SOFT));
/** /**
* The enum value names must match the keys of the statements in the * The enum value names must match the keys of the statements in the
* statement bundles "dbStatements*.properties". * statement bundles "dbStatements*.properties".
@@ -269,6 +276,7 @@ public final class CveDB implements AutoCloseable {
instance.usageCount -= 1; instance.usageCount -= 1;
if (instance.usageCount <= 0 && instance.isOpen()) { if (instance.usageCount <= 0 && instance.isOpen()) {
instance.usageCount = 0; instance.usageCount = 0;
clearCache();
instance.closeStatements(); instance.closeStatements();
try { try {
instance.connection.close(); instance.connection.close();
@@ -474,6 +482,7 @@ public final class CveDB implements AutoCloseable {
* @param value the property value * @param value the property value
*/ */
public synchronized void saveProperty(String key, String value) { public synchronized void saveProperty(String key, String value) {
clearCache();
try { try {
try { try {
final PreparedStatement mergeProperty = getPreparedStatement(MERGE_PROPERTY); final PreparedStatement mergeProperty = getPreparedStatement(MERGE_PROPERTY);
@@ -498,6 +507,17 @@ public final class CveDB implements AutoCloseable {
} }
} }
/**
* Clears cache. Should be called whenever something is modified. While this is not the optimal cache eviction
* strategy, this is good enough for typical usage (update DB and then only read) and it is easier to maintain
* the code.
*
* It should be also called when DB is closed.
*/
private void clearCache() {
vulnerabilitiesForCpeCache.clear();
}
/** /**
* Retrieves the vulnerabilities associated with the specified CPE. * Retrieves the vulnerabilities associated with the specified CPE.
* *
@@ -506,6 +526,13 @@ public final class CveDB implements AutoCloseable {
* @throws DatabaseException thrown if there is an exception retrieving data * @throws DatabaseException thrown if there is an exception retrieving data
*/ */
public synchronized List<Vulnerability> getVulnerabilities(String cpeStr) throws DatabaseException { public synchronized List<Vulnerability> getVulnerabilities(String cpeStr) throws DatabaseException {
final List<Vulnerability> cachedVulnerabilities = vulnerabilitiesForCpeCache.get(cpeStr);
if (cachedVulnerabilities != null) {
LOGGER.debug("Cache hit for {}", cpeStr);
return cachedVulnerabilities;
} else {
LOGGER.debug("Cache miss for {}", cpeStr);
}
final VulnerableSoftware cpe = new VulnerableSoftware(); final VulnerableSoftware cpe = new VulnerableSoftware();
try { try {
cpe.parseName(cpeStr); cpe.parseName(cpeStr);
@@ -554,6 +581,7 @@ public final class CveDB implements AutoCloseable {
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
} }
vulnerabilitiesForCpeCache.put(cpeStr, vulnerabilities);
return vulnerabilities; return vulnerabilities;
} }
@@ -633,6 +661,7 @@ public final class CveDB implements AutoCloseable {
* @throws DatabaseException is thrown if the database * @throws DatabaseException is thrown if the database
*/ */
public synchronized void updateVulnerability(Vulnerability vuln) throws DatabaseException { public synchronized void updateVulnerability(Vulnerability vuln) throws DatabaseException {
clearCache();
try { try {
int vulnerabilityId = 0; int vulnerabilityId = 0;
final PreparedStatement selectVulnerabilityId = getPreparedStatement(SELECT_VULNERABILITY_ID); final PreparedStatement selectVulnerabilityId = getPreparedStatement(SELECT_VULNERABILITY_ID);
@@ -799,6 +828,7 @@ public final class CveDB implements AutoCloseable {
* ensure orphan entries are removed. * ensure orphan entries are removed.
*/ */
public synchronized void cleanupDatabase() { public synchronized void cleanupDatabase() {
clearCache();
try { try {
final PreparedStatement ps = getPreparedStatement(CLEANUP_ORPHANS); final PreparedStatement ps = getPreparedStatement(CLEANUP_ORPHANS);
if (ps != null) { if (ps != null) {
@@ -934,6 +964,7 @@ public final class CveDB implements AutoCloseable {
* Deletes unused dictionary entries from the database. * Deletes unused dictionary entries from the database.
*/ */
public synchronized void deleteUnusedCpe() { public synchronized void deleteUnusedCpe() {
clearCache();
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
ps = connection.prepareStatement(statementBundle.getString("DELETE_UNUSED_DICT_CPE")); ps = connection.prepareStatement(statementBundle.getString("DELETE_UNUSED_DICT_CPE"));
@@ -956,6 +987,7 @@ public final class CveDB implements AutoCloseable {
* @param product the CPE product * @param product the CPE product
*/ */
public synchronized void addCpe(String cpe, String vendor, String product) { public synchronized void addCpe(String cpe, String vendor, String product) {
clearCache();
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
ps = connection.prepareStatement(statementBundle.getString("ADD_DICT_CPE")); ps = connection.prepareStatement(statementBundle.getString("ADD_DICT_CPE"));

27
pom.xml
View File

@@ -288,7 +288,7 @@ Copyright (c) 2012 - Jeremy Long
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals> </goals>
<configuration> <configuration>
<!--destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile--> <destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
<propertyName>surefireArgLine</propertyName> <propertyName>surefireArgLine</propertyName>
</configuration> </configuration>
</execution> </execution>
@@ -299,12 +299,31 @@ Copyright (c) 2012 - Jeremy Long
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals> </goals>
<configuration> <configuration>
<!--destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile--> <destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile>
<propertyName>failsafeArgLine</propertyName> <propertyName>failsafeArgLine</propertyName>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>add-dynamic-properties</id>
<phase>pre-integration-test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
project.properties['invoker.mavenOpts']=project.properties.failsafeArgLine
</source>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
@@ -578,6 +597,10 @@ Copyright (c) 2012 - Jeremy Long
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.9</version> <version>0.7.9</version>
<configuration> <configuration>
<dataFileIncludes>
<dataFileInclude>target/coverage-reports/jacoco-ut.exec</dataFileInclude>
<dataFileInclude>target/coverage-reports/jacoco-it.exec</dataFileInclude>
</dataFileIncludes>
</configuration> </configuration>
<reportSets> <reportSets>
<reportSet> <reportSet>

View File

@@ -20,8 +20,8 @@ To setup a centralized database the following generalized steps can be used:
<ol><li>Create the database and tables using either <a href="https://github.com/jeremylong/DependencyCheck/blob/master/dependency-check-core/src/main/resources/data/initialize.sql">initialize.sql</a> <ol><li>Create the database and tables using either <a href="https://github.com/jeremylong/DependencyCheck/blob/master/dependency-check-core/src/main/resources/data/initialize.sql">initialize.sql</a>
or one of the other initialization scripts <a href="https://github.com/jeremylong/DependencyCheck/tree/master/dependency-check-core/src/main/resources/data">found here</a>.</li> or one of the other initialization scripts <a href="https://github.com/jeremylong/DependencyCheck/tree/master/dependency-check-core/src/main/resources/data">found here</a>.</li>
<li>The account that the clients will connect using must have select granted on the tables. <li>The account that the clients will connect using must have select granted on the tables.
<ul><li>Note, if the clients performing the scans should run with the noupdate setting. A single <ul><li>Note, the clients performing the scans should run with the noupdate setting. A single
instance of the dependency-check client should be setup with update enabled and the account instance of the dependency-check client should be setup with updates enabled and the account
used during the update process will need to be granted update rights on the tables. used during the update process will need to be granted update rights on the tables.
</li></ul> </li></ul>
</li><li>Dependency-check clients running scans will need to be configured to use the central database: </li><li>Dependency-check clients running scans will need to be configured to use the central database: