mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-04-24 01:08:48 +02:00
Adds configurable batch insert for References and Vulnerabilities
Applies batch inserts for reference and vulnerability tables, solves slow one-by-one insert process, for Vulnerabilities with several references/vulnerabilities associated. Feature is configurable through properties: database.batchinsert.enabled and database.batchinsert.maxsize.
This commit is contained in:
@@ -17,45 +17,31 @@
|
||||
*/
|
||||
package org.owasp.dependencycheck.data.nvdcve;
|
||||
|
||||
import org.apache.commons.collections.map.ReferenceMap;
|
||||
import org.owasp.dependencycheck.data.cwe.CweDB;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.utils.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.apache.commons.collections.map.ReferenceMap;
|
||||
import org.owasp.dependencycheck.data.cwe.CweDB;
|
||||
import org.owasp.dependencycheck.dependency.Reference;
|
||||
import org.owasp.dependencycheck.dependency.Vulnerability;
|
||||
import org.owasp.dependencycheck.dependency.VulnerableSoftware;
|
||||
import org.owasp.dependencycheck.utils.DBUtils;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersion;
|
||||
import org.owasp.dependencycheck.utils.DependencyVersionUtil;
|
||||
import org.owasp.dependencycheck.utils.Pair;
|
||||
import org.owasp.dependencycheck.utils.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
//CSOFF: AvoidStarImport
|
||||
import static org.owasp.dependencycheck.data.nvdcve.CveDB.PreparedStatementCveDb.*;
|
||||
//CSON: AvoidStarImport
|
||||
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.*;
|
||||
|
||||
//CSOFF: AvoidStarImport
|
||||
//CSON: AvoidStarImport
|
||||
|
||||
/**
|
||||
* The database holding information about the NVD CVE data. This class is safe
|
||||
@@ -71,7 +57,7 @@ public final class CveDB implements AutoCloseable {
|
||||
* The logger.
|
||||
*/
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(CveDB.class);
|
||||
public static final int MAX_BATCH_SIZE = 1000;
|
||||
|
||||
/**
|
||||
* The database connection factory.
|
||||
*/
|
||||
@@ -514,7 +500,7 @@ public final class CveDB implements AutoCloseable {
|
||||
* 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.
|
||||
*
|
||||
* <p>
|
||||
* It should be also called when DB is closed.
|
||||
*/
|
||||
private synchronized void clearCache() {
|
||||
@@ -672,6 +658,7 @@ public final class CveDB implements AutoCloseable {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
int vulnerabilityId = 0;
|
||||
long countVulnerabilities = 0;
|
||||
final PreparedStatement selectVulnerabilityId = getPreparedStatement(SELECT_VULNERABILITY_ID);
|
||||
selectVulnerabilityId.setString(1, vuln.getName());
|
||||
rs = selectVulnerabilityId.executeQuery();
|
||||
@@ -686,6 +673,7 @@ public final class CveDB implements AutoCloseable {
|
||||
deleteSoftware.setInt(1, vulnerabilityId);
|
||||
deleteSoftware.execute();
|
||||
}
|
||||
|
||||
DBUtils.closeResultSet(rs);
|
||||
|
||||
if (vulnerabilityId != 0) {
|
||||
@@ -739,9 +727,10 @@ public final class CveDB implements AutoCloseable {
|
||||
insertReference.setString(2, r.getName());
|
||||
insertReference.setString(3, r.getUrl());
|
||||
insertReference.setString(4, r.getSource());
|
||||
if(isBatchInsertEnabled()) {
|
||||
insertReference.addBatch();
|
||||
countReferences++;
|
||||
if (countReferences % MAX_BATCH_SIZE == 0) {
|
||||
if (countReferences % getBatchSize() == 0) {
|
||||
insertReference.executeBatch();
|
||||
insertReference = getPreparedStatement(INSERT_REFERENCE);
|
||||
LOGGER.info(getLogForBatchInserts(countReferences, "Completed %s batch inserts to references table: %s"));
|
||||
@@ -753,6 +742,9 @@ public final class CveDB implements AutoCloseable {
|
||||
insertReference.executeBatch();
|
||||
countReferences = 0;
|
||||
}
|
||||
} else {
|
||||
insertReference.execute();
|
||||
}
|
||||
}
|
||||
|
||||
PreparedStatement insertSoftware = getPreparedStatement(INSERT_SOFTWARE);
|
||||
@@ -792,9 +784,10 @@ public final class CveDB implements AutoCloseable {
|
||||
} else {
|
||||
insertSoftware.setString(3, vulnerableSoftware.getPreviousVersion());
|
||||
}
|
||||
if(isBatchInsertEnabled()) {
|
||||
insertSoftware.addBatch();
|
||||
countSoftware++;
|
||||
if (countSoftware % MAX_BATCH_SIZE == 0) {
|
||||
if (countSoftware % getBatchSize() == 0) {
|
||||
executeBatch(vuln, vulnerableSoftware, insertSoftware);
|
||||
insertSoftware = getPreparedStatement(INSERT_SOFTWARE);
|
||||
LOGGER.info(getLogForBatchInserts(countSoftware, "Completed %s batch inserts software table: %s"));
|
||||
@@ -806,7 +799,18 @@ public final class CveDB implements AutoCloseable {
|
||||
}
|
||||
executeBatch(vuln, vulnerableSoftware, insertSoftware);
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
insertSoftware.execute();
|
||||
} catch (SQLException ex) {
|
||||
if (ex.getMessage().contains("Duplicate entry")) {
|
||||
final String msg = String.format("Duplicate software key identified in '%s:%s'", vuln.getName(), vuln.getName());
|
||||
LOGGER.info(msg, ex);
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
final String msg = String.format("Error updating '%s'", vuln.getName());
|
||||
@@ -817,12 +821,33 @@ public final class CveDB implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private int getBatchSize() {
|
||||
int max;
|
||||
try {
|
||||
max = settings.getInt(Settings.KEYS.MAX_BATCH_SIZE);
|
||||
} catch (InvalidSettingException pE) {
|
||||
max = 1000;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private boolean isBatchInsertEnabled() {
|
||||
boolean batch = false;
|
||||
try {
|
||||
batch = settings.getBoolean(Settings.KEYS.ENABLE_BATCH_UPDATES);
|
||||
} catch (InvalidSettingException pE) {
|
||||
//If there's no configuration, default is to not perform batch inserts
|
||||
batch = false;
|
||||
}
|
||||
return batch;
|
||||
}
|
||||
|
||||
private String getLogForBatchInserts(int pCountReferences, String pFormat) {
|
||||
return String.format(pFormat, pCountReferences, new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes batch inserts of vulnerabilities when MAX_BATCH_SIZE is reached
|
||||
* Executes batch inserts of vulnerabilities when property database.batchinsert.maxsize is reached
|
||||
*
|
||||
* @param pVulnerability
|
||||
* @param pVulnerableSoftware
|
||||
@@ -1011,7 +1036,7 @@ public final class CveDB implements AutoCloseable {
|
||||
|
||||
/**
|
||||
* This method is only referenced in unused code.
|
||||
*
|
||||
* <p>
|
||||
* Deletes unused dictionary entries from the database.
|
||||
*/
|
||||
public synchronized void deleteUnusedCpe() {
|
||||
@@ -1030,7 +1055,7 @@ public final class CveDB implements AutoCloseable {
|
||||
/**
|
||||
* This method is only referenced in unused code and will likely break on
|
||||
* MySQL if ever used due to the MERGE statement.
|
||||
*
|
||||
* <p>
|
||||
* Merges CPE entries into the database.
|
||||
*
|
||||
* @param cpe the CPE identifier
|
||||
|
||||
@@ -50,7 +50,7 @@ cve.url.modified.validfordays=7
|
||||
# the number of hours to wait before checking if updates are available from the NVD.
|
||||
cve.check.validforhours=4
|
||||
#first year to pull data from the URLs below
|
||||
cve.startyear=2002
|
||||
cve.startyear=2017
|
||||
# the path to the modified nvd cve xml file.
|
||||
cve.url-1.2.modified=https://nvd.nist.gov/download/nvdcve-Modified.xml.gz
|
||||
#cve.url-1.2.modified=http://nvd.nist.gov/download/nvdcve-modified.xml
|
||||
@@ -127,3 +127,5 @@ analyzer.vulnerabilitysuppression.enabled=true
|
||||
updater.nvdcve.enabled=true
|
||||
updater.versioncheck.enabled=true
|
||||
analyzer.versionfilter.enabled=true
|
||||
database.batchinsert.enabled=true
|
||||
database.batchinsert.maxsize=1000
|
||||
|
||||
@@ -443,6 +443,16 @@ public final class Settings {
|
||||
*/
|
||||
public static final String UPDATE_VERSION_CHECK_ENABLED = "updater.versioncheck.enabled";
|
||||
|
||||
/**
|
||||
*
|
||||
* Adds capabilities to batch insert. Tested on PostgreSQL and H2.
|
||||
*/
|
||||
public static final String ENABLE_BATCH_UPDATES = "database.batchinsert.enabled";
|
||||
/**
|
||||
* Size of database batch inserts
|
||||
*/
|
||||
public static final String MAX_BATCH_SIZE = "database.batchinsert.maxsize";
|
||||
|
||||
/**
|
||||
* private constructor because this is a "utility" class containing
|
||||
* constants
|
||||
|
||||
Reference in New Issue
Block a user