Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSVC integration #170

Merged
merged 4 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions nvip_data/mysql-database/newDB/db.init.xml
Original file line number Diff line number Diff line change
Expand Up @@ -930,5 +930,25 @@
</createTable>
</changeSet>

<changeSet id="createSSVCTable" author="dylan">
<createTable tableName="ssvc">
<column name="id" autoIncrement="true" type="SERIAL">
<constraints primaryKey="true" nullable="false" unique="true"/>
</column>
<column name="cve_id" type="VARCHAR(20)">
<constraints nullable="false"/>
</column>
<column name="automatable" type="TINYINT(1)">
<constraints nullable="false"/>
</column>
<column name="exploit_status" type="VARCHAR(50)">
<constraints nullable="false"/>
</column>
<column name="technical_impact" type="TINYINT(1)">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>


</databaseChangeLog>
39 changes: 39 additions & 0 deletions reconciler/src/main/java/edu/rit/se/nvip/DatabaseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -785,4 +787,41 @@ public Set<CompositeVulnerability> attachMitreVulns(Set<CompositeVulnerability>
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<CompositeVulnerability> 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);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public void main(Set<String> jobs) {

dbh.insertCvssBatch(recharacterized);
dbh.insertVdoBatch(recharacterized);
dbh.insertSSVCSet(recharacterized);
}
// PNE team no longer wants a finish message
//messenger.sendPNEFinishMessage();
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions reconciler/src/main/java/edu/rit/se/nvip/ReconcilerMain.java
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -63,6 +59,10 @@ public void main() {
break;
}
}
case "dev":
final Set<String> devJobs = new HashSet<>();
devJobs.add("CVE-2023-2825");
rc.main(devJobs);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.*;

Expand All @@ -50,6 +55,8 @@
public class CveCharacterizer {
private Logger logger = LogManager.getLogger(CveCharacterizer.class.getSimpleName());
private final Map<VDONounGroup, AbstractCveClassifier> 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
Expand All @@ -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 {

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -192,6 +201,7 @@ public void characterizeCveList(Set<CompositeVulnerability> cveSet, int limit) {
countNotChanged++;
continue;
}

// characterize CVE
Map<VDOLabel, Double> prediction = characterizeCveForVDO(vuln.getDescription(), true);
for (VDOLabel label : prediction.keySet()) {
Expand All @@ -206,6 +216,9 @@ public void characterizeCveList(Set<CompositeVulnerability> 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;
Expand All @@ -224,6 +237,39 @@ public void characterizeCveList(Set<CompositeVulnerability> 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<String, String> 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
Expand All @@ -240,4 +286,21 @@ private double getCvssScoreFromVdoLabels(Set<VDOLabel> 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<String, String> params)
throws UnsupportedEncodingException {
StringBuilder result = new StringBuilder("?");

for (Map.Entry<String, String> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public enum ReconciliationStatus {
// cvss scoring
private CvssScore cvssScore;

// ssvc scoring
private SSVC ssvc;

//list of related cwes
private final List<CWE> cweList = new ArrayList<>();
private ReconciliationStatus recStatus;
Expand Down Expand Up @@ -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;
}
Expand Down
26 changes: 26 additions & 0 deletions reconciler/src/main/java/edu/rit/se/nvip/model/SSVC.java
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<CompositeVulnerability> cveSet = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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



Expand Down
Loading