Compare commits

...

2 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

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"));