diff --git a/nvip_data/mysql-database/newDB/db.init.xml b/nvip_data/mysql-database/newDB/db.init.xml index a6176422e..04cde8ff5 100644 --- a/nvip_data/mysql-database/newDB/db.init.xml +++ b/nvip_data/mysql-database/newDB/db.init.xml @@ -930,5 +930,25 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reconciler/src/main/java/edu/rit/se/nvip/DatabaseHelper.java b/reconciler/src/main/java/edu/rit/se/nvip/DatabaseHelper.java index 27b9d6bec..cde3fa68e 100644 --- a/reconciler/src/main/java/edu/rit/se/nvip/DatabaseHelper.java +++ b/reconciler/src/main/java/edu/rit/se/nvip/DatabaseHelper.java @@ -68,6 +68,8 @@ public class DatabaseHelper { private static final String SELECT_MITRE_BY_DATE = "SELECT cve_id FROM mitredata WHERE last_modified >= DATE_SUB(NOW(), INTERVAL 2 MINUTE)"; private static final String INSERT_RUN_STATS = "INSERT INTO runhistory (run_date_time, total_cve_count, new_cve_count, updated_cve_count, not_in_nvd_count, not_in_mitre_count, not_in_both_count, avg_time_gap_nvd, avg_time_gap_mitre)" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + private static final String EXPLOIT_EXISTS = "SELECT id FROM exploit WHERE cve_id = ?"; + private static final String INSERT_SSVC = "INSERT INTO ssvc (cve_id, automatable, exploit_status, technical_impact) VALUES (?, ?, ?, ?)"; public static synchronized DatabaseHelper getInstance() { if (databaseHelper == null) { @@ -785,4 +787,41 @@ public Set attachMitreVulns(Set return out; } + public boolean exploitExists(String cveId) { + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(EXPLOIT_EXISTS)) { + pstmt.setString(1, cveId); + return pstmt.execute(); + } catch (SQLException ex) { + logger.error("Error while fetching exploit data"); + logger.error(ex); + return false; + } + } + + public void insertSSVCSet(Set vulns) { + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(INSERT_SSVC)) { + conn.setAutoCommit(false); + for (CompositeVulnerability vuln : vulns) { + // Get SSVC data + final SSVC ssvc = vuln.getSSVC(); + + // Skip vulns w/o data + if (!vuln.isRecharacterized() || ssvc == null) continue; + + // Insert data into statement + pstmt.setString(1, vuln.getCveId()); + pstmt.setBoolean(2, ssvc.isAutomatable()); + pstmt.setString(3, ssvc.getExploitStatus()); + pstmt.setBoolean(4, ssvc.getTechnicalImpact()); + + } + // Execute statement + pstmt.execute(); + conn.commit(); + } catch (SQLException ex) { + logger.error("Error while inserting vdo labels"); + logger.error(ex); + } + } + } diff --git a/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerController.java b/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerController.java index 680616695..e73064676 100644 --- a/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerController.java +++ b/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerController.java @@ -111,6 +111,7 @@ public void main(Set jobs) { dbh.insertCvssBatch(recharacterized); dbh.insertVdoBatch(recharacterized); + dbh.insertSSVCSet(recharacterized); } // PNE team no longer wants a finish message //messenger.sendPNEFinishMessage(); @@ -144,7 +145,7 @@ public CveCharacterizer call() { try { String[] trainingDataInfo = {ReconcilerEnvVars.getTrainingDataDir(), ReconcilerEnvVars.getTrainingData()}; logger.info("Setting NVIP_CVE_CHARACTERIZATION_LIMIT to {}", ReconcilerEnvVars.getCharacterizationLimit()); - return new CveCharacterizer(trainingDataInfo[0], trainingDataInfo[1], ReconcilerEnvVars.getCharacterizationApproach(), ReconcilerEnvVars.getCharacterizationMethod()); + return new CveCharacterizer(trainingDataInfo[0], trainingDataInfo[1], ReconcilerEnvVars.getCharacterizationApproach(), ReconcilerEnvVars.getCharacterizationMethod(), dbh); } catch (NullPointerException | NumberFormatException e) { logger.warn("Could not fetch NVIP_CVE_CHARACTERIZATION_TRAINING_DATA or NVIP_CVE_CHARACTERIZATION_TRAINING_DATA_DIR from env vars"); return null; diff --git a/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerMain.java b/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerMain.java index a40a33a5a..80641efa0 100644 --- a/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerMain.java +++ b/reconciler/src/main/java/edu/rit/se/nvip/ReconcilerMain.java @@ -1,14 +1,10 @@ package edu.rit.se.nvip; -import edu.rit.se.nvip.characterizer.CveCharacterizer; import edu.rit.se.nvip.messenger.Messenger; -import edu.rit.se.nvip.model.CompositeVulnerability; -import edu.rit.se.nvip.model.RawVulnerability; import edu.rit.se.nvip.utils.ReconcilerEnvVars; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.sql.Timestamp; import java.util.*; public class ReconcilerMain { @@ -63,6 +59,10 @@ public void main() { break; } } + case "dev": + final Set devJobs = new HashSet<>(); + devJobs.add("CVE-2023-2825"); + rc.main(devJobs); } } diff --git a/reconciler/src/main/java/edu/rit/se/nvip/characterizer/CveCharacterizer.java b/reconciler/src/main/java/edu/rit/se/nvip/characterizer/CveCharacterizer.java index a7ab6b254..3568e013b 100644 --- a/reconciler/src/main/java/edu/rit/se/nvip/characterizer/CveCharacterizer.java +++ b/reconciler/src/main/java/edu/rit/se/nvip/characterizer/CveCharacterizer.java @@ -22,6 +22,8 @@ * SOFTWARE. */ +import com.fasterxml.jackson.databind.ObjectMapper; +import edu.rit.se.nvip.DatabaseHelper; import edu.rit.se.nvip.automatedcvss.CvssScoreCalculator; import edu.rit.se.nvip.automatedcvss.PartialCvssVectorGenerator; import edu.rit.se.nvip.automatedcvss.preprocessor.CvePreProcessor; @@ -32,12 +34,15 @@ import edu.rit.se.nvip.characterizer.enums.VDONounGroup; import edu.rit.se.nvip.model.CompositeVulnerability; import edu.rit.se.nvip.model.CvssScore; +import edu.rit.se.nvip.model.SSVC; import edu.rit.se.nvip.model.VdoCharacteristic; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.File; +import java.io.*; +import java.net.URL; +import java.net.URLEncoder; import java.nio.file.Paths; import java.util.*; @@ -50,6 +55,8 @@ public class CveCharacterizer { private Logger logger = LogManager.getLogger(CveCharacterizer.class.getSimpleName()); private final Map nounGroupToClassifier = new HashMap<>(); + private final static ObjectMapper OM = new ObjectMapper(); + private final DatabaseHelper dbh; /** * these two vars are used to derive the CVSS vector from VDO labels and then @@ -73,10 +80,12 @@ public CveCharacterizer(CvePreProcessor cvePreProcessor, CveClassifierFactory cveClassifierFactory, CvssScoreCalculator cvssScoreCalculator, PartialCvssVectorGenerator partialCvssVectorGenerator, - String trainingDataPath, String trainingDataFiles, String approach, String method) { + String trainingDataPath, String trainingDataFiles, String approach, String method, + DatabaseHelper dbh) { this.cvssScoreCalculator = cvssScoreCalculator; this.partialCvssVectorGenerator = partialCvssVectorGenerator; this.cvePreProcessor = cvePreProcessor; + this.dbh = dbh; try { /** @@ -132,8 +141,8 @@ public CveCharacterizer(CvePreProcessor cvePreProcessor, */ //removed boolean loadSerializedModels as well as exploitability package - public CveCharacterizer(String trainingDataPath, String trainingDataFiles, String approach, String method) { - this(new CvePreProcessor(true), new CveClassifierFactory(), new CvssScoreCalculator(), new PartialCvssVectorGenerator(), trainingDataPath, trainingDataFiles, approach, method); + public CveCharacterizer(String trainingDataPath, String trainingDataFiles, String approach, String method, DatabaseHelper dbh) { + this(new CvePreProcessor(true), new CveClassifierFactory(), new CvssScoreCalculator(), new PartialCvssVectorGenerator(), trainingDataPath, trainingDataFiles, approach, method, dbh); } /** @@ -192,6 +201,7 @@ public void characterizeCveList(Set cveSet, int limit) { countNotChanged++; continue; } + // characterize CVE Map prediction = characterizeCveForVDO(vuln.getDescription(), true); for (VDOLabel label : prediction.keySet()) { @@ -206,6 +216,9 @@ public void characterizeCveList(Set cveSet, int limit) { vuln.addCvssScore(score); // logger.info("CVSS Score predicted for {}", vulnerability.getCveId()); + // Get SSVC + vuln.setSSVC(characterizeCveForSSVC(vuln)); + // log if (totCharacterized % 100 == 0 && totCharacterized > 0) { double percent = (totCharacterized + countBadDescription + countNotChanged) * 1.0 / cveSet.size() * 100; @@ -224,6 +237,39 @@ public void characterizeCveList(Set cveSet, int limit) { logger.info("{} CVEs did not have a good description, and {} CVEs had the same description (after reconciliation) and skipped!", countBadDescription, countNotChanged); } + // Query SSVC AI models for scoring + private SSVC characterizeCveForSSVC(CompositeVulnerability vuln) { + try { + // Create parameter dict + final Map params = new HashMap<>(); + params.put("cveId", vuln.getCveId()); + params.put("description", vuln.getDescription()); + params.put("exploitStatus", dbh.exploitExists(vuln.getCveId()) ? "POC" : "NONE"); + + // Create url object + final URL url = new URL("http://localhost:5000/ssvc" + getParamsString(params)); + +// // Setup connection and parameters +// final HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); +// conn.setRequestMethod("GET"); +// conn.setDoOutput(true); +// DataOutputStream out = new DataOutputStream(conn.getOutputStream()); +// out.writeBytes(getParamsString(params)); +// out.flush(); +// out.close(); + + // Build object from request response + return OM.readValue(url, SSVC.class); + +// // Extract values from response +// +// // Build SSVC object +// final SSVC ssvc = new SSVC(automatable, exploitExists, technicalImpact); + } catch (Exception e) { + return null; + } + } + /** * get VDO labels and return a double array that includes the * mean/minimum/maximum and standard deviation of the CVSS scores in NVD @@ -240,4 +286,21 @@ private double getCvssScoreFromVdoLabels(Set predictionsForVuln) { // return cvssScoreCalculator.getCvssScoreJython(cvssVec)[0]; // old way of doing it return cvssScoreCalculator.lookupCvssScore(cvssVec); // should return same number as the old way but doesn't rely on python } + + private static String getParamsString(Map params) + throws UnsupportedEncodingException { + StringBuilder result = new StringBuilder("?"); + + for (Map.Entry entry : params.entrySet()) { + result.append(URLEncoder.encode(entry.getKey(), "UTF-8")); + result.append("="); + result.append(URLEncoder.encode(entry.getValue(), "UTF-8")); + result.append("&"); + } + + String resultString = result.toString(); + return resultString.length() > 0 + ? resultString.substring(0, resultString.length() - 1) + : resultString; + } } diff --git a/reconciler/src/main/java/edu/rit/se/nvip/model/CompositeVulnerability.java b/reconciler/src/main/java/edu/rit/se/nvip/model/CompositeVulnerability.java index 361b721b4..0b7248e60 100644 --- a/reconciler/src/main/java/edu/rit/se/nvip/model/CompositeVulnerability.java +++ b/reconciler/src/main/java/edu/rit/se/nvip/model/CompositeVulnerability.java @@ -25,6 +25,9 @@ public enum ReconciliationStatus { // cvss scoring private CvssScore cvssScore; + // ssvc scoring + private SSVC ssvc; + //list of related cwes private final List cweList = new ArrayList<>(); private ReconciliationStatus recStatus; @@ -193,11 +196,15 @@ public CvssScore getCvssScoreInfo() { return cvssScore; } + public SSVC getSSVC() { return ssvc; } + public void addCvssScore(CvssScore cvss) { this.cvssScore = cvss; this.recharacterized = true; } + public void setSSVC(SSVC ssvc) { this.ssvc = ssvc; } + public boolean isRecharacterized() { return this.recharacterized; } diff --git a/reconciler/src/main/java/edu/rit/se/nvip/model/SSVC.java b/reconciler/src/main/java/edu/rit/se/nvip/model/SSVC.java new file mode 100644 index 000000000..7c3072f14 --- /dev/null +++ b/reconciler/src/main/java/edu/rit/se/nvip/model/SSVC.java @@ -0,0 +1,26 @@ +package edu.rit.se.nvip.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown=true) +public class SSVC { + private enum EXPLOIT_STATUS { + NONE, POC, ACTIVE + } + @JsonProperty("automatable") + private boolean automatable; + @JsonProperty("exploitStatus") + private EXPLOIT_STATUS exploitStatus; + + private boolean technicalImpact; + + public boolean isAutomatable() { return automatable; } + public String getExploitStatus() { return exploitStatus.toString(); } + public boolean getTechnicalImpact() { return technicalImpact; } + + @JsonProperty("technicalImpact") + public void setTechnicalImpact(String technicalImpact) { + this.technicalImpact = technicalImpact.equals("TOTAL"); + } +} diff --git a/reconciler/src/main/java/edu/rit/se/nvip/sandbox/characterizerRealTest.java b/reconciler/src/main/java/edu/rit/se/nvip/sandbox/characterizerRealTest.java index 3f734dff7..140255adb 100644 --- a/reconciler/src/main/java/edu/rit/se/nvip/sandbox/characterizerRealTest.java +++ b/reconciler/src/main/java/edu/rit/se/nvip/sandbox/characterizerRealTest.java @@ -38,7 +38,7 @@ public static void main(String[] args){ String[] trainingDataInfo = {ReconcilerEnvVars.getTrainingDataDir(), ReconcilerEnvVars.getTrainingData()}; CveCharacterizer characterizer = new CveCharacterizer(trainingDataInfo[0], trainingDataInfo[1], ReconcilerEnvVars.getCharacterizationApproach(), - ReconcilerEnvVars.getCharacterizationMethod()); + ReconcilerEnvVars.getCharacterizationMethod(), null); // TODO: Add/mock DBH Set cveSet = new HashSet<>(); diff --git a/reconciler/src/test/java/edu/rit/se/nvip/characterizer/CveCharacterizerTest.java b/reconciler/src/test/java/edu/rit/se/nvip/characterizer/CveCharacterizerTest.java index c7351eb4e..d01c568e8 100644 --- a/reconciler/src/test/java/edu/rit/se/nvip/characterizer/CveCharacterizerTest.java +++ b/reconciler/src/test/java/edu/rit/se/nvip/characterizer/CveCharacterizerTest.java @@ -90,7 +90,7 @@ public void testCveCharacterization() { when(mockPartialCvssVectorGenerator.getCVssVector(anySet())).thenReturn(new String[8]); //create characterizer with the mocks manually injected CveCharacterizer cveCharacterizer = new CveCharacterizer(mockPreProcessor, mockCveClassifierFactory, mockCvssScoreCalculator, mockPartialCvssVectorGenerator, - trainingDataInfo[0], trainingDataInfo[1], "ML", "NB"); + trainingDataInfo[0], trainingDataInfo[1], "ML", "NB", null); // TODO: Add/mock dbh