Merge branch 'stefanneuhaus-misc_performance_tweaking_and_cleanup'

This commit is contained in:
Jeremy Long
2017-03-04 14:29:47 -05:00
14 changed files with 259 additions and 163 deletions

View File

@@ -43,6 +43,7 @@ import java.util.regex.Pattern;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import org.apache.commons.compress.utils.IOUtils; import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.owasp.dependencycheck.Engine; import org.owasp.dependencycheck.Engine;
import org.owasp.dependencycheck.analyzer.exception.AnalysisException; import org.owasp.dependencycheck.analyzer.exception.AnalysisException;
@@ -1150,7 +1151,7 @@ public class JarAnalyzer extends AbstractFileTypeAnalyzer {
ClassNameInformation(String className) { ClassNameInformation(String className) {
name = className; name = className;
if (name.contains("/")) { if (name.contains("/")) {
final String[] tmp = className.toLowerCase().split("/"); final String[] tmp = StringUtils.split(className.toLowerCase(), '/');
int start = 0; int start = 0;
int end = 3; int end = 3;
if ("com".equals(tmp[0]) || "org".equals(tmp[0])) { if ("com".equals(tmp[0]) || "org".equals(tmp[0])) {

View File

@@ -20,6 +20,7 @@ package org.owasp.dependencycheck.data.cpe;
import java.io.Serializable; import java.io.Serializable;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import org.apache.commons.lang3.StringUtils;
/** /**
* A CPE entry containing the name, vendor, product, and version. * A CPE entry containing the name, vendor, product, and version.
@@ -143,7 +144,8 @@ public class IndexEntry implements Serializable {
*/ */
public void parseName(String cpeName) throws UnsupportedEncodingException { public void parseName(String cpeName) throws UnsupportedEncodingException {
if (cpeName != null && cpeName.length() > 7) { if (cpeName != null && cpeName.length() > 7) {
final String[] data = cpeName.substring(7).split(":"); final String cpeNameWithoutPrefix = cpeName.substring(7);
final String[] data = StringUtils.split(cpeNameWithoutPrefix, ':');
if (data.length >= 1) { if (data.length >= 1) {
vendor = URLDecoder.decode(data[0].replace("+", "%2B"), "UTF-8"); vendor = URLDecoder.decode(data[0].replace("+", "%2B"), "UTF-8");
if (data.length >= 2) { if (data.length >= 2) {

View File

@@ -23,8 +23,8 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -35,6 +35,7 @@ import java.util.MissingResourceException;
import java.util.Properties; 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 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;
@@ -47,12 +48,17 @@ 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.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*;
/** /**
* The database holding information about the NVD CVE data. * The database holding information about the NVD CVE data.
* This class is safe to be accessed from multiple threads in parallel, however
* internally only one connection will be used.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
public class CveDB { @ThreadSafe
public final class CveDB {
/** /**
* The logger. * The logger.
@@ -61,18 +67,54 @@ public class CveDB {
/** /**
* Database connection * Database connection
*/ */
private Connection conn; private Connection connection;
/** /**
* The bundle of statements used when accessing the database. * The bundle of statements used when accessing the database.
*/ */
private ResourceBundle statementBundle = null; private final ResourceBundle statementBundle;
/**
* Database properties object containing the 'properties' from the database
* table.
*/
private final DatabaseProperties databaseProperties;
/**
* Does the underlying connection support batch operations?
* Currently we do not support batch execution.
*/
private final boolean batchSupported = false;
/**
* The prepared statements.
*/
private final EnumMap<PreparedStatementCveDb, PreparedStatement> preparedStatements;
/** /**
* Creates a new CveDB object and opens the database connection. Note, the * The enum value names must match the keys of the statements in the
* connection must be closed by the caller by calling the close method. * statement bundles "dbStatements*.properties".
* ======= Does the underlying connection support batch operations?
*/ */
private boolean batchSupported; enum PreparedStatementCveDb {
CLEANUP_ORPHANS,
COUNT_CPE,
DELETE_REFERENCE,
DELETE_SOFTWARE,
DELETE_VULNERABILITY,
INSERT_CPE,
INSERT_PROPERTY,
INSERT_REFERENCE,
INSERT_SOFTWARE,
INSERT_VULNERABILITY,
MERGE_PROPERTY,
SELECT_CPE_ENTRIES,
SELECT_CPE_ID,
SELECT_CVE_FROM_SOFTWARE,
SELECT_PROPERTIES,
SELECT_REFERENCES,
SELECT_SOFTWARE,
SELECT_VENDOR_PRODUCT_LIST,
SELECT_VULNERABILITY,
SELECT_VULNERABILITY_ID,
UPDATE_PROPERTY,
UPDATE_VULNERABILITY
}
/** /**
* Creates a new CveDB object and opens the database connection. Note, the * Creates a new CveDB object and opens the database connection. Note, the
@@ -82,24 +124,28 @@ public class CveDB {
* database. * database.
*/ */
public CveDB() throws DatabaseException { public CveDB() throws DatabaseException {
super(); open();
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.
*
* @return the product name of the database if successful, {@code null} else
*/
private String determineDatabaseProductName() {
try { try {
open(); final String databaseProductName = getConnection().getMetaData().getDatabaseProductName();
try { LOGGER.debug("Database product: {}", databaseProductName);
final String databaseProductName = conn.getMetaData().getDatabaseProductName(); return databaseProductName;
LOGGER.debug("Database dialect: {}", databaseProductName); } catch (SQLException se) {
final Locale dbDialect = new Locale(databaseProductName); LOGGER.warn("Problem determining database product!", se);
statementBundle = ResourceBundle.getBundle("data/dbStatements", dbDialect); return null;
if ("mysql".equalsIgnoreCase(databaseProductName)) {
batchSupported = false;
}
} catch (SQLException se) {
LOGGER.warn("Problem loading database specific dialect!", se);
statementBundle = ResourceBundle.getBundle("data/dbStatements");
}
databaseProperties = new DatabaseProperties(this);
} catch (DatabaseException ex) {
throw ex;
} }
} }
@@ -108,8 +154,8 @@ public class CveDB {
* *
* @return the database connection * @return the database connection
*/ */
protected Connection getConnection() { private Connection getConnection() {
return conn; return connection;
} }
/** /**
@@ -119,9 +165,9 @@ public class CveDB {
* @throws DatabaseException thrown if there is an error opening the * @throws DatabaseException thrown if there is an error opening the
* database connection * database connection
*/ */
public final synchronized void open() throws DatabaseException { public synchronized void open() throws DatabaseException {
if (!isOpen()) { if (!isOpen()) {
conn = ConnectionFactory.getConnection(); connection = ConnectionFactory.getConnection();
} }
} }
@@ -130,9 +176,10 @@ public class CveDB {
* is done being used. * is done being used.
*/ */
public synchronized void close() { public synchronized void close() {
if (conn != null) { if (isOpen()) {
closeStatements();
try { try {
conn.close(); getConnection().close();
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.error("There was an error attempting to close the CveDB, see the log for more details."); LOGGER.error("There was an error attempting to close the CveDB, see the log for more details.");
LOGGER.debug("", ex); LOGGER.debug("", ex);
@@ -140,7 +187,7 @@ public class CveDB {
LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details."); LOGGER.error("There was an exception attempting to close the CveDB, see the log for more details.");
LOGGER.debug("", ex); LOGGER.debug("", ex);
} }
conn = null; connection = null;
} }
} }
@@ -149,8 +196,57 @@ public class CveDB {
* *
* @return whether the database connection is open or closed * @return whether the database connection is open or closed
*/ */
public synchronized boolean isOpen() { private boolean isOpen() {
return conn != null; return getConnection() != null;
}
/**
* Prepares all statements to be used and returns them.
*
* @return the prepared statements
* @throws DatabaseException thrown if there is an error preparing the statements
*/
private EnumMap<PreparedStatementCveDb, PreparedStatement> prepareStatements()
throws DatabaseException {
final EnumMap<PreparedStatementCveDb, PreparedStatement> result = new EnumMap<PreparedStatementCveDb, PreparedStatement>(PreparedStatementCveDb.class);
for (PreparedStatementCveDb key : values()) {
final String statementString = statementBundle.getString(key.name());
final PreparedStatement preparedStatement;
try {
if (key == INSERT_VULNERABILITY || key == INSERT_CPE) {
preparedStatement = getConnection().prepareStatement(statementString, new String[]{"id"});
} else {
preparedStatement = getConnection().prepareStatement(statementString);
}
} catch (SQLException exception) {
throw new DatabaseException(exception);
}
result.put(key, preparedStatement);
}
return result;
}
/**
* Closes all prepared statements.
*/
private void closeStatements() {
for (PreparedStatement preparedStatement : preparedStatements.values()) {
DBUtils.closeStatement(preparedStatement);
}
}
/**
* Returns the specified prepared statement.
*
* @param key the prepared statement from {@link PreparedStatementCveDb} to return
* @return the prepared statement
* @throws SQLException thrown if a SQL Exception occurs
*/
private PreparedStatement getPreparedStatement(PreparedStatementCveDb key) throws SQLException {
final PreparedStatement preparedStatement = preparedStatements.get(key);
preparedStatement.clearParameters();
return preparedStatement;
} }
/** /**
@@ -160,8 +256,8 @@ public class CveDB {
*/ */
public synchronized void commit() throws SQLException { public synchronized void commit() throws SQLException {
//temporary remove this as autocommit is on. //temporary remove this as autocommit is on.
//if (conn != null) { //if (isOpen()) {
// conn.commit(); // getConnection().commit();
//} //}
} }
@@ -177,11 +273,6 @@ public class CveDB {
close(); close();
super.finalize(); super.finalize();
} }
/**
* Database properties object containing the 'properties' from the database
* table.
*/
private DatabaseProperties databaseProperties;
/** /**
* Get the value of databaseProperties. * Get the value of databaseProperties.
@@ -205,9 +296,8 @@ public class CveDB {
public synchronized Set<VulnerableSoftware> getCPEs(String vendor, String product) { public synchronized Set<VulnerableSoftware> getCPEs(String vendor, String product) {
final Set<VulnerableSoftware> cpe = new HashSet<VulnerableSoftware>(); final Set<VulnerableSoftware> cpe = new HashSet<VulnerableSoftware>();
ResultSet rs = null; ResultSet rs = null;
PreparedStatement ps = null;
try { try {
ps = getConnection().prepareStatement(statementBundle.getString("SELECT_CPE_ENTRIES")); final PreparedStatement ps = getPreparedStatement(SELECT_CPE_ENTRIES);
ps.setString(1, vendor); ps.setString(1, vendor);
ps.setString(2, product); ps.setString(2, product);
rs = ps.executeQuery(); rs = ps.executeQuery();
@@ -222,7 +312,6 @@ public class CveDB {
LOGGER.debug("", ex); LOGGER.debug("", ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
DBUtils.closeStatement(ps);
} }
return cpe; return cpe;
} }
@@ -237,9 +326,8 @@ public class CveDB {
public synchronized Set<Pair<String, String>> getVendorProductList() throws DatabaseException { public synchronized Set<Pair<String, String>> getVendorProductList() throws DatabaseException {
final Set<Pair<String, String>> data = new HashSet<Pair<String, String>>(); final Set<Pair<String, String>> data = new HashSet<Pair<String, String>>();
ResultSet rs = null; ResultSet rs = null;
PreparedStatement ps = null;
try { try {
ps = getConnection().prepareStatement(statementBundle.getString("SELECT_VENDOR_PRODUCT_LIST")); final PreparedStatement ps = getPreparedStatement(SELECT_VENDOR_PRODUCT_LIST);
rs = ps.executeQuery(); rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
data.add(new Pair<String, String>(rs.getString(1), rs.getString(2))); data.add(new Pair<String, String>(rs.getString(1), rs.getString(2)));
@@ -249,7 +337,6 @@ public class CveDB {
throw new DatabaseException(msg, ex); throw new DatabaseException(msg, ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
DBUtils.closeStatement(ps);
} }
return data; return data;
} }
@@ -261,10 +348,9 @@ public class CveDB {
*/ */
public synchronized Properties getProperties() { public synchronized Properties getProperties() {
final Properties prop = new Properties(); final Properties prop = new Properties();
PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
ps = getConnection().prepareStatement(statementBundle.getString("SELECT_PROPERTIES")); final PreparedStatement ps = getPreparedStatement(SELECT_PROPERTIES);
rs = ps.executeQuery(); rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
prop.setProperty(rs.getString(1), rs.getString(2)); prop.setProperty(rs.getString(1), rs.getString(2));
@@ -273,7 +359,6 @@ public class CveDB {
LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details."); LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
LOGGER.debug("", ex); LOGGER.debug("", ex);
} finally { } finally {
DBUtils.closeStatement(ps);
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
} }
return prop; return prop;
@@ -288,31 +373,20 @@ public class CveDB {
public synchronized void saveProperty(String key, String value) { public synchronized void saveProperty(String key, String value) {
try { try {
try { try {
final PreparedStatement mergeProperty = getConnection().prepareStatement(statementBundle.getString("MERGE_PROPERTY")); final PreparedStatement mergeProperty = getPreparedStatement(MERGE_PROPERTY);
try { mergeProperty.setString(1, key);
mergeProperty.setString(1, key); mergeProperty.setString(2, value);
mergeProperty.setString(2, value); mergeProperty.executeUpdate();
mergeProperty.executeUpdate();
} finally {
DBUtils.closeStatement(mergeProperty);
}
} catch (MissingResourceException mre) { } catch (MissingResourceException mre) {
// No Merge statement, so doing an Update/Insert... // No Merge statement, so doing an Update/Insert...
PreparedStatement updateProperty = null; final PreparedStatement updateProperty = getPreparedStatement(UPDATE_PROPERTY);
PreparedStatement insertProperty = null; updateProperty.setString(1, value);
try { updateProperty.setString(2, key);
updateProperty = getConnection().prepareStatement(statementBundle.getString("UPDATE_PROPERTY")); if (updateProperty.executeUpdate() == 0) {
updateProperty.setString(1, value); final PreparedStatement insertProperty = getPreparedStatement(INSERT_PROPERTY);
updateProperty.setString(2, key); insertProperty.setString(1, key);
if (updateProperty.executeUpdate() == 0) { insertProperty.setString(2, value);
insertProperty = getConnection().prepareStatement(statementBundle.getString("INSERT_PROPERTY")); insertProperty.executeUpdate();
insertProperty.setString(1, key);
insertProperty.setString(2, value);
insertProperty.executeUpdate();
}
} finally {
DBUtils.closeStatement(updateProperty);
DBUtils.closeStatement(insertProperty);
} }
} }
} catch (SQLException ex) { } catch (SQLException ex) {
@@ -338,10 +412,9 @@ public class CveDB {
final DependencyVersion detectedVersion = parseDependencyVersion(cpe); final DependencyVersion detectedVersion = parseDependencyVersion(cpe);
final List<Vulnerability> vulnerabilities = new ArrayList<Vulnerability>(); final List<Vulnerability> vulnerabilities = new ArrayList<Vulnerability>();
PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
ps = getConnection().prepareStatement(statementBundle.getString("SELECT_CVE_FROM_SOFTWARE")); final PreparedStatement ps = getPreparedStatement(SELECT_CVE_FROM_SOFTWARE);
ps.setString(1, cpe.getVendor()); ps.setString(1, cpe.getVendor());
ps.setString(2, cpe.getProduct()); ps.setString(2, cpe.getProduct());
rs = ps.executeQuery(); rs = ps.executeQuery();
@@ -377,7 +450,6 @@ public class CveDB {
throw new DatabaseException("Exception retrieving vulnerability for " + cpeStr, ex); throw new DatabaseException("Exception retrieving vulnerability for " + cpeStr, ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
DBUtils.closeStatement(ps);
} }
return vulnerabilities; return vulnerabilities;
} }
@@ -390,16 +462,13 @@ public class CveDB {
* @throws DatabaseException if an exception occurs * @throws DatabaseException if an exception occurs
*/ */
public synchronized Vulnerability getVulnerability(String cve) throws DatabaseException { public synchronized Vulnerability getVulnerability(String cve) throws DatabaseException {
PreparedStatement psV = null;
PreparedStatement psR = null;
PreparedStatement psS = null;
ResultSet rsV = null; ResultSet rsV = null;
ResultSet rsR = null; ResultSet rsR = null;
ResultSet rsS = null; ResultSet rsS = null;
Vulnerability vuln = null; Vulnerability vuln = null;
try { try {
psV = getConnection().prepareStatement(statementBundle.getString("SELECT_VULNERABILITY")); final PreparedStatement psV = getPreparedStatement(SELECT_VULNERABILITY);
psV.setString(1, cve); psV.setString(1, cve);
rsV = psV.executeQuery(); rsV = psV.executeQuery();
if (rsV.next()) { if (rsV.next()) {
@@ -423,13 +492,14 @@ public class CveDB {
vuln.setCvssIntegrityImpact(rsV.getString(9)); vuln.setCvssIntegrityImpact(rsV.getString(9));
vuln.setCvssAvailabilityImpact(rsV.getString(10)); vuln.setCvssAvailabilityImpact(rsV.getString(10));
psR = getConnection().prepareStatement(statementBundle.getString("SELECT_REFERENCES")); final PreparedStatement psR = getPreparedStatement(SELECT_REFERENCES);
psR.setInt(1, cveId); psR.setInt(1, cveId);
rsR = psR.executeQuery(); rsR = psR.executeQuery();
while (rsR.next()) { while (rsR.next()) {
vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3)); vuln.addReference(rsR.getString(1), rsR.getString(2), rsR.getString(3));
} }
psS = getConnection().prepareStatement(statementBundle.getString("SELECT_SOFTWARE"));
final PreparedStatement psS = getPreparedStatement(SELECT_SOFTWARE);
psS.setInt(1, cveId); psS.setInt(1, cveId);
rsS = psS.executeQuery(); rsS = psS.executeQuery();
while (rsS.next()) { while (rsS.next()) {
@@ -448,9 +518,6 @@ public class CveDB {
DBUtils.closeResultSet(rsV); DBUtils.closeResultSet(rsV);
DBUtils.closeResultSet(rsR); DBUtils.closeResultSet(rsR);
DBUtils.closeResultSet(rsS); DBUtils.closeResultSet(rsS);
DBUtils.closeStatement(psV);
DBUtils.closeStatement(psR);
DBUtils.closeStatement(psS);
} }
return vuln; return vuln;
} }
@@ -463,52 +530,31 @@ public class CveDB {
* @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 {
PreparedStatement selectVulnerabilityId = null;
PreparedStatement deleteVulnerability = null;
PreparedStatement deleteReferences = null;
PreparedStatement deleteSoftware = null;
PreparedStatement updateVulnerability = null;
PreparedStatement insertVulnerability = null;
PreparedStatement insertReference = null;
PreparedStatement selectCpeId = null;
PreparedStatement insertCpe = null;
PreparedStatement insertSoftware = null;
try { try {
selectVulnerabilityId = getConnection().prepareStatement(statementBundle.getString("SELECT_VULNERABILITY_ID"));
deleteVulnerability = getConnection().prepareStatement(statementBundle.getString("DELETE_VULNERABILITY"));
deleteReferences = getConnection().prepareStatement(statementBundle.getString("DELETE_REFERENCE"));
deleteSoftware = getConnection().prepareStatement(statementBundle.getString("DELETE_SOFTWARE"));
updateVulnerability = getConnection().prepareStatement(statementBundle.getString("UPDATE_VULNERABILITY"));
final String[] ids = {"id"};
insertVulnerability = getConnection().prepareStatement(statementBundle.getString("INSERT_VULNERABILITY"),
//Statement.RETURN_GENERATED_KEYS);
ids);
insertReference = getConnection().prepareStatement(statementBundle.getString("INSERT_REFERENCE"));
selectCpeId = getConnection().prepareStatement(statementBundle.getString("SELECT_CPE_ID"));
insertCpe = getConnection().prepareStatement(statementBundle.getString("INSERT_CPE"),
//Statement.RETURN_GENERATED_KEYS);
ids);
insertSoftware = getConnection().prepareStatement(statementBundle.getString("INSERT_SOFTWARE"));
int vulnerabilityId = 0; int vulnerabilityId = 0;
final PreparedStatement selectVulnerabilityId = getPreparedStatement(SELECT_VULNERABILITY_ID);
selectVulnerabilityId.setString(1, vuln.getName()); selectVulnerabilityId.setString(1, vuln.getName());
ResultSet rs = selectVulnerabilityId.executeQuery(); ResultSet rs = selectVulnerabilityId.executeQuery();
if (rs.next()) { if (rs.next()) {
vulnerabilityId = rs.getInt(1); vulnerabilityId = rs.getInt(1);
// first delete any existing vulnerability info. We don't know what was updated. yes, slower but atm easier. // first delete any existing vulnerability info. We don't know what was updated. yes, slower but atm easier.
deleteReferences.setInt(1, vulnerabilityId); final PreparedStatement deleteReference = getPreparedStatement(DELETE_REFERENCE);
deleteReferences.execute(); deleteReference.setInt(1, vulnerabilityId);
deleteReference.execute();
final PreparedStatement deleteSoftware = getPreparedStatement(DELETE_SOFTWARE);
deleteSoftware.setInt(1, vulnerabilityId); deleteSoftware.setInt(1, vulnerabilityId);
deleteSoftware.execute(); deleteSoftware.execute();
} }
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
rs = null;
if (vulnerabilityId != 0) { if (vulnerabilityId != 0) {
if (vuln.getDescription().contains("** REJECT **")) { if (vuln.getDescription().contains("** REJECT **")) {
final PreparedStatement deleteVulnerability = getPreparedStatement(DELETE_VULNERABILITY);
deleteVulnerability.setInt(1, vulnerabilityId); deleteVulnerability.setInt(1, vulnerabilityId);
deleteVulnerability.executeUpdate(); deleteVulnerability.executeUpdate();
} else { } else {
final PreparedStatement updateVulnerability = getPreparedStatement(UPDATE_VULNERABILITY);
updateVulnerability.setString(1, vuln.getDescription()); updateVulnerability.setString(1, vuln.getDescription());
updateVulnerability.setString(2, vuln.getCwe()); updateVulnerability.setString(2, vuln.getCwe());
updateVulnerability.setFloat(3, vuln.getCvssScore()); updateVulnerability.setFloat(3, vuln.getCvssScore());
@@ -522,6 +568,7 @@ public class CveDB {
updateVulnerability.executeUpdate(); updateVulnerability.executeUpdate();
} }
} else { } else {
final PreparedStatement insertVulnerability = getPreparedStatement(INSERT_VULNERABILITY);
insertVulnerability.setString(1, vuln.getName()); insertVulnerability.setString(1, vuln.getName());
insertVulnerability.setString(2, vuln.getDescription()); insertVulnerability.setString(2, vuln.getDescription());
insertVulnerability.setString(3, vuln.getCwe()); insertVulnerability.setString(3, vuln.getCwe());
@@ -542,10 +589,13 @@ public class CveDB {
throw new DatabaseException(msg, ex); throw new DatabaseException(msg, ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
rs = null;
} }
} }
final PreparedStatement insertReference = getPreparedStatement(INSERT_REFERENCE);
if (batchSupported) {
insertReference.clearBatch();
}
for (Reference r : vuln.getReferences()) { for (Reference r : vuln.getReferences()) {
insertReference.setInt(1, vulnerabilityId); insertReference.setInt(1, vulnerabilityId);
insertReference.setString(2, r.getName()); insertReference.setString(2, r.getName());
@@ -563,8 +613,13 @@ public class CveDB {
insertReference.executeBatch(); insertReference.executeBatch();
} }
final PreparedStatement insertSoftware = getPreparedStatement(INSERT_SOFTWARE);
if (batchSupported) {
insertSoftware.clearBatch();
}
for (VulnerableSoftware s : vuln.getVulnerableSoftware()) { for (VulnerableSoftware s : vuln.getVulnerableSoftware()) {
int cpeProductId = 0; int cpeProductId = 0;
final PreparedStatement selectCpeId = getPreparedStatement(SELECT_CPE_ID);
selectCpeId.setString(1, s.getName()); selectCpeId.setString(1, s.getName());
try { try {
rs = selectCpeId.executeQuery(); rs = selectCpeId.executeQuery();
@@ -575,10 +630,10 @@ public class CveDB {
throw new DatabaseException("Unable to get primary key for new cpe: " + s.getName(), ex); throw new DatabaseException("Unable to get primary key for new cpe: " + s.getName(), ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
rs = null;
} }
if (cpeProductId == 0) { if (cpeProductId == 0) {
final PreparedStatement insertCpe = getPreparedStatement(INSERT_CPE);
insertCpe.setString(1, s.getName()); insertCpe.setString(1, s.getName());
insertCpe.setString(2, s.getVendor()); insertCpe.setString(2, s.getVendor());
insertCpe.setString(3, s.getProduct()); insertCpe.setString(3, s.getProduct());
@@ -605,7 +660,7 @@ public class CveDB {
} catch (SQLException ex) { } catch (SQLException ex) {
if (ex.getMessage().contains("Duplicate entry")) { if (ex.getMessage().contains("Duplicate entry")) {
final String msg = String.format("Duplicate software key identified in '%s:%s'", vuln.getName(), s.getName()); final String msg = String.format("Duplicate software key identified in '%s:%s'", vuln.getName(), s.getName());
LOGGER.debug(msg, ex); LOGGER.info(msg, ex);
} else { } else {
throw ex; throw ex;
} }
@@ -619,17 +674,6 @@ public class CveDB {
final String msg = String.format("Error updating '%s'", vuln.getName()); final String msg = String.format("Error updating '%s'", vuln.getName());
LOGGER.debug(msg, ex); LOGGER.debug(msg, ex);
throw new DatabaseException(msg, ex); throw new DatabaseException(msg, ex);
} finally {
DBUtils.closeStatement(selectVulnerabilityId);
DBUtils.closeStatement(deleteReferences);
DBUtils.closeStatement(deleteSoftware);
DBUtils.closeStatement(updateVulnerability);
DBUtils.closeStatement(deleteVulnerability);
DBUtils.closeStatement(insertVulnerability);
DBUtils.closeStatement(insertReference);
DBUtils.closeStatement(selectCpeId);
DBUtils.closeStatement(insertCpe);
DBUtils.closeStatement(insertSoftware);
} }
} }
@@ -639,17 +683,16 @@ public class CveDB {
* @return <code>true</code> if data exists; otherwise <code>false</code> * @return <code>true</code> if data exists; otherwise <code>false</code>
*/ */
public synchronized boolean dataExists() { public synchronized boolean dataExists() {
Statement cs = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
cs = conn.createStatement(); final PreparedStatement cs = getPreparedStatement(COUNT_CPE);
rs = cs.executeQuery("SELECT COUNT(*) records FROM cpeEntry"); rs = cs.executeQuery();
if (rs.next()) { if (rs.next()) {
if (rs.getInt(1) > 0) { if (rs.getInt(1) > 0) {
return true; return true;
} }
} }
} catch (SQLException ex) { } catch (Exception ex) {
String dd; String dd;
try { try {
dd = Settings.getDataDirectory().getAbsolutePath(); dd = Settings.getDataDirectory().getAbsolutePath();
@@ -664,7 +707,6 @@ public class CveDB {
LOGGER.debug("", ex); LOGGER.debug("", ex);
} finally { } finally {
DBUtils.closeResultSet(rs); DBUtils.closeResultSet(rs);
DBUtils.closeStatement(cs);
} }
return false; return false;
} }
@@ -675,17 +717,14 @@ public class CveDB {
* ensure orphan entries are removed. * ensure orphan entries are removed.
*/ */
public synchronized void cleanupDatabase() { public synchronized void cleanupDatabase() {
PreparedStatement ps = null;
try { try {
ps = getConnection().prepareStatement(statementBundle.getString("CLEANUP_ORPHANS")); final PreparedStatement ps = getPreparedStatement(CLEANUP_ORPHANS);
if (ps != null) { if (ps != null) {
ps.executeUpdate(); ps.executeUpdate();
} }
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details."); LOGGER.error("An unexpected SQL Exception occurred; please see the verbose log for more details.");
LOGGER.debug("", ex); LOGGER.debug("", ex);
} finally {
DBUtils.closeStatement(ps);
} }
} }
@@ -819,7 +858,6 @@ public class CveDB {
ps.executeUpdate(); ps.executeUpdate();
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.error("Unable to delete CPE dictionary entries", ex); LOGGER.error("Unable to delete CPE dictionary entries", ex);
} finally {
DBUtils.closeStatement(ps); DBUtils.closeStatement(ps);
} }
} }
@@ -844,7 +882,6 @@ public class CveDB {
ps.executeUpdate(); ps.executeUpdate();
} catch (SQLException ex) { } catch (SQLException ex) {
LOGGER.error("Unable to add CPE dictionary entry", ex); LOGGER.error("Unable to add CPE dictionary entry", ex);
} finally {
DBUtils.closeStatement(ps); DBUtils.closeStatement(ps);
} }
} }

View File

@@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
import java.util.TreeMap; import java.util.TreeMap;
import javax.annotation.concurrent.ThreadSafe;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.DateTimeFormatter;
@@ -31,9 +32,11 @@ import org.slf4j.LoggerFactory;
/** /**
* This is a wrapper around a set of properties that are stored in the database. * This is a wrapper around a set of properties that are stored in the database.
* This class is safe to be accessed from multiple threads in parallel.
* *
* @author Jeremy Long * @author Jeremy Long
*/ */
@ThreadSafe
public class DatabaseProperties { public class DatabaseProperties {
/** /**

View File

@@ -17,6 +17,7 @@
*/ */
package org.owasp.dependencycheck.data.update.cpe; package org.owasp.dependencycheck.data.update.cpe;
import org.apache.commons.lang3.StringUtils;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import org.owasp.dependencycheck.data.update.exception.InvalidDataException; import org.owasp.dependencycheck.data.update.exception.InvalidDataException;
@@ -36,7 +37,8 @@ public class Cpe {
*/ */
public Cpe(String value) throws UnsupportedEncodingException, InvalidDataException { public Cpe(String value) throws UnsupportedEncodingException, InvalidDataException {
this.value = value; this.value = value;
final String[] data = value.substring(7).split(":"); final String valueWithoutPrefix = value.substring(7);
final String[] data = StringUtils.split(valueWithoutPrefix, ':');
if (data.length >= 2) { if (data.length >= 2) {
vendor = URLDecoder.decode(data[0].replace("+", "%2B"), "UTF-8"); vendor = URLDecoder.decode(data[0].replace("+", "%2B"), "UTF-8");
product = URLDecoder.decode(data[1].replace("+", "%2B"), "UTF-8"); product = URLDecoder.decode(data[1].replace("+", "%2B"), "UTF-8");

View File

@@ -33,6 +33,8 @@ import org.xml.sax.SAXException;
import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import static org.owasp.dependencycheck.data.update.nvd.NvdCve20Handler.AttributeValues.*;
/** /**
* A SAX Handler that will parse the NVD CVE XML (schema version 2.0). * A SAX Handler that will parse the NVD CVE XML (schema version 2.0).
* *
@@ -48,6 +50,18 @@ public class NvdCve20Handler extends DefaultHandler {
* the current supported schema version. * the current supported schema version.
*/ */
private static final String CURRENT_SCHEMA_VERSION = "2.0"; private static final String CURRENT_SCHEMA_VERSION = "2.0";
/**
* a possible attribute value of the {@link AttributeValues#XML_LANG} attribute
*/
private static final String EN = "en";
/**
* the prefix of the node text of a CPE
*/
private static final String CPE_NODE_TEXT_PREFIX = "cpe:/a:";
/**
* the node text of an entry marked for deletion
*/
private static final String REJECT_NODE_TEXT = "** REJECT **";
/** /**
* the current element. * the current element.
*/ */
@@ -111,30 +125,30 @@ public class NvdCve20Handler extends DefaultHandler {
if (current.isEntryNode()) { if (current.isEntryNode()) {
hasApplicationCpe = false; hasApplicationCpe = false;
vulnerability = new Vulnerability(); vulnerability = new Vulnerability();
vulnerability.setName(attributes.getValue("id")); vulnerability.setName(attributes.getValue(ID));
} else if (current.isVulnProductNode()) { } else if (current.isVulnProductNode()) {
nodeText = new StringBuilder(100); nodeText = new StringBuilder(100);
} else if (current.isVulnReferencesNode()) { } else if (current.isVulnReferencesNode()) {
final String lang = attributes.getValue("xml:lang"); final String lang = attributes.getValue(XML_LANG);
if ("en".equals(lang)) { if (EN.equals(lang)) {
reference = new Reference(); reference = new Reference();
} else { } else {
reference = null; reference = null;
} }
} else if (reference != null && current.isVulnReferenceNode()) { } else if (reference != null && current.isVulnReferenceNode()) {
reference.setUrl(attributes.getValue("href")); reference.setUrl(attributes.getValue(HREF));
nodeText = new StringBuilder(130); nodeText = new StringBuilder(130);
} else if (reference != null && current.isVulnSourceNode()) { } else if (reference != null && current.isVulnSourceNode()) {
nodeText = new StringBuilder(30); nodeText = new StringBuilder(30);
} else if (current.isVulnSummaryNode()) { } else if (current.isVulnSummaryNode()) {
nodeText = new StringBuilder(500); nodeText = new StringBuilder(500);
} else if (current.isNVDNode()) { } else if (current.isNVDNode()) {
final String nvdVer = attributes.getValue("nvd_xml_version"); final String nvdVer = attributes.getValue(NVD_XML_VERSION);
if (!CURRENT_SCHEMA_VERSION.equals(nvdVer)) { if (!CURRENT_SCHEMA_VERSION.equals(nvdVer)) {
throw new SAXNotSupportedException("Schema version " + nvdVer + " is not supported"); throw new SAXNotSupportedException("Schema version " + nvdVer + " is not supported");
} }
} else if (current.isVulnCWENode()) { } else if (current.isVulnCWENode()) {
vulnerability.setCwe(attributes.getValue("id")); vulnerability.setCwe(attributes.getValue(ID));
} else if (current.isCVSSScoreNode()) { } else if (current.isCVSSScoreNode()) {
nodeText = new StringBuilder(5); nodeText = new StringBuilder(5);
} else if (current.isCVSSAccessVectorNode()) { } else if (current.isCVSSAccessVectorNode()) {
@@ -206,7 +220,7 @@ public class NvdCve20Handler extends DefaultHandler {
nodeText = null; nodeText = null;
} else if (current.isVulnProductNode()) { } else if (current.isVulnProductNode()) {
final String cpe = nodeText.toString(); final String cpe = nodeText.toString();
if (cpe.startsWith("cpe:/a:")) { if (cpe.startsWith(CPE_NODE_TEXT_PREFIX)) {
hasApplicationCpe = true; hasApplicationCpe = true;
vulnerability.addVulnerableSoftware(cpe); vulnerability.addVulnerableSoftware(cpe);
} }
@@ -222,7 +236,7 @@ public class NvdCve20Handler extends DefaultHandler {
nodeText = null; nodeText = null;
} else if (current.isVulnSummaryNode()) { } else if (current.isVulnSummaryNode()) {
vulnerability.setDescription(nodeText.toString()); vulnerability.setDescription(nodeText.toString());
if (nodeText.indexOf("** REJECT **") >= 0) { if (nodeText.indexOf(REJECT_NODE_TEXT) >= 0) {
hasApplicationCpe = true; //ensure we process this to delete the vuln hasApplicationCpe = true; //ensure we process this to delete the vuln
} }
nodeText = null; nodeText = null;
@@ -492,4 +506,27 @@ public class NvdCve20Handler extends DefaultHandler {
} }
} }
// </editor-fold> // </editor-fold>
/**
* A simple class to maintain information about the attribute values encountered while parsing the NVD CVE XML.
*/
protected static class AttributeValues {
/**
* An attribute in the NVD CVE Schema 2.0
*/
protected static final String ID = "id";
/**
* An attribute in the NVD CVE Schema 2.0
*/
protected static final String XML_LANG = "xml:lang";
/**
* An attribute in the NVD CVE Schema 2.0
*/
protected static final String HREF = "href";
/**
* An attribute in the NVD CVE Schema 2.0
*/
protected static final String NVD_XML_VERSION = "nvd_xml_version";
}
} }

View File

@@ -230,10 +230,9 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
* Adds an entry for vulnerable software. * Adds an entry for vulnerable software.
* *
* @param cpe string representation of a CPE entry * @param cpe string representation of a CPE entry
* @return if the add succeeded
*/ */
public boolean addVulnerableSoftware(String cpe) { public void addVulnerableSoftware(String cpe) {
return addVulnerableSoftware(cpe, null); addVulnerableSoftware(cpe, null);
} }
/** /**
@@ -242,28 +241,26 @@ public class Vulnerability implements Serializable, Comparable<Vulnerability> {
* @param cpe string representation of a cpe * @param cpe string representation of a cpe
* @param previousVersion the previous version (previousVersion - cpe would * @param previousVersion the previous version (previousVersion - cpe would
* be considered vulnerable) * be considered vulnerable)
* @return if the add succeeded
*/ */
public boolean addVulnerableSoftware(String cpe, String previousVersion) { public void addVulnerableSoftware(String cpe, String previousVersion) {
final VulnerableSoftware vs = new VulnerableSoftware(); final VulnerableSoftware vs = new VulnerableSoftware();
vs.setCpe(cpe); vs.setCpe(cpe);
if (previousVersion != null) { if (previousVersion != null) {
vs.setPreviousVersion(previousVersion); vs.setPreviousVersion(previousVersion);
} }
return updateVulnerableSoftware(vs); updateVulnerableSoftware(vs);
} }
/** /**
* Adds or updates a vulnerable software entry. * Adds or updates a vulnerable software entry.
* *
* @param vulnSoftware the vulnerable software * @param vulnSoftware the vulnerable software
* @return if the update succeeded
*/ */
public boolean updateVulnerableSoftware(VulnerableSoftware vulnSoftware) { public void updateVulnerableSoftware(VulnerableSoftware vulnSoftware) {
if (vulnerableSoftware.contains(vulnSoftware)) { if (vulnerableSoftware.contains(vulnSoftware)) {
vulnerableSoftware.remove(vulnSoftware); vulnerableSoftware.remove(vulnSoftware);
} }
return vulnerableSoftware.add(vulnSoftware); vulnerableSoftware.add(vulnSoftware);
} }
/** /**

View File

@@ -20,6 +20,7 @@ package org.owasp.dependencycheck.dependency;
import java.io.Serializable; import java.io.Serializable;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import org.apache.commons.lang3.StringUtils;
import org.owasp.dependencycheck.data.cpe.IndexEntry; import org.owasp.dependencycheck.data.cpe.IndexEntry;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -73,7 +74,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp
public void parseName(String cpeName) throws UnsupportedEncodingException { public void parseName(String cpeName) throws UnsupportedEncodingException {
this.name = cpeName; this.name = cpeName;
if (cpeName != null && cpeName.length() > 7) { if (cpeName != null && cpeName.length() > 7) {
final String[] data = cpeName.substring(7).split(":"); final String cpeNameWithoutPrefix = cpeName.substring(7);
final String[] data = StringUtils.split(cpeNameWithoutPrefix, ':');
if (data.length >= 1) { if (data.length >= 1) {
this.setVendor(urlDecode(data[0])); this.setVendor(urlDecode(data[0]));
} }
@@ -172,8 +174,8 @@ public class VulnerableSoftware extends IndexEntry implements Serializable, Comp
@Override @Override
public int compareTo(VulnerableSoftware vs) { public int compareTo(VulnerableSoftware vs) {
int result = 0; int result = 0;
final String[] left = this.name.split(":"); final String[] left = StringUtils.split(this.name, ':');
final String[] right = vs.getName().split(":"); final String[] right = StringUtils.split(vs.getName(), ':');
final int max = (left.length <= right.length) ? left.length : right.length; final int max = (left.length <= right.length) ? left.length : right.length;
if (max > 0) { if (max > 0) {
for (int i = 0; result == 0 && i < max; i++) { for (int i = 0; result == 0 && i < max; i++) {

View File

@@ -19,6 +19,7 @@ DELETE_REFERENCE=DELETE FROM reference WHERE cveid = ?
DELETE_SOFTWARE=DELETE FROM software WHERE cveid = ? DELETE_SOFTWARE=DELETE FROM software WHERE cveid = ?
DELETE_VULNERABILITY=DELETE FROM vulnerability WHERE id = ? DELETE_VULNERABILITY=DELETE FROM vulnerability WHERE id = ?
CLEANUP_ORPHANS=DELETE FROM cpeEntry WHERE id not in (SELECT CPEEntryId FROM software); CLEANUP_ORPHANS=DELETE FROM cpeEntry WHERE id not in (SELECT CPEEntryId FROM software);
COUNT_CPE=SELECT COUNT(*) records FROM cpeEntry
INSERT_REFERENCE=INSERT INTO reference (cveid, name, url, source) VALUES (?, ?, ?, ?) INSERT_REFERENCE=INSERT INTO reference (cveid, name, url, source) VALUES (?, ?, ?, ?)
INSERT_SOFTWARE=INSERT INTO software (cveid, cpeEntryId, previousVersion) VALUES (?, ?, ?) INSERT_SOFTWARE=INSERT INTO software (cveid, cpeEntryId, previousVersion) VALUES (?, ?, ?)
INSERT_CPE=INSERT INTO cpeEntry (cpe, vendor, product) VALUES (?, ?, ?) INSERT_CPE=INSERT INTO cpeEntry (cpe, vendor, product) VALUES (?, ?, ?)
@@ -38,6 +39,6 @@ INSERT_PROPERTY=INSERT INTO properties (id, value) VALUES (?, ?)
UPDATE_PROPERTY=UPDATE properties SET value = ? WHERE id = ? UPDATE_PROPERTY=UPDATE properties SET value = ? WHERE id = ?
DELETE_PROPERTY=DELETE FROM properties WHERE id = ? DELETE_PROPERTY=DELETE FROM properties WHERE id = ?
#the following two statements are unused and are only referenecd in dead code #the following two statements are unused and are only referenced in dead code
DELETE_UNUSED_DICT_CPE=DELETE FROM cpeEntry WHERE dictionaryEntry=true AND id NOT IN (SELECT cpeEntryId FROM software) DELETE_UNUSED_DICT_CPE=DELETE FROM cpeEntry WHERE dictionaryEntry=true AND id NOT IN (SELECT cpeEntryId FROM software)
ADD_DICT_CPE=MERGE INTO cpeEntry (cpe, vendor, product, dictionaryEntry) KEY(cpe) VALUES(?,?,?,true) ADD_DICT_CPE=MERGE INTO cpeEntry (cpe, vendor, product, dictionaryEntry) KEY(cpe) VALUES(?,?,?,true)

View File

@@ -1 +1,15 @@
# Copyright 2015 OWASP.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
CLEANUP_ORPHANS=DELETE FROM cpeEntry WHERE id not in (SELECT CPEEntryId FROM software) CLEANUP_ORPHANS=DELETE FROM cpeEntry WHERE id not in (SELECT CPEEntryId FROM software)