Initial commit
This commit is contained in:
177
scripts/enrichCVE_MSRC.js
Normal file
177
scripts/enrichCVE_MSRC.js
Normal file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import axios from 'axios';
|
||||
import mysql from 'mysql2/promise';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config({ path: '.env.local' });
|
||||
|
||||
const DB = await mysql.createConnection({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
});
|
||||
|
||||
function formatDate(isoString) {
|
||||
if (!isoString) return null;
|
||||
const date = new Date(isoString);
|
||||
return date.toISOString().slice(0, 19).replace('T', ' ');
|
||||
}
|
||||
|
||||
function getLocalizedText(entry, fallback = '') {
|
||||
if (!entry || typeof entry !== 'object') return fallback;
|
||||
|
||||
// MSRC: sometimes the object directly has "Value"
|
||||
if ('Value' in entry && typeof entry.Value === 'string') {
|
||||
return entry.Value;
|
||||
}
|
||||
|
||||
// If it's localized (e.g., { "en-US": { Value: ... } })
|
||||
if (entry['en-US']?.Value) return entry['en-US'].Value;
|
||||
|
||||
// Fallback: first valid value
|
||||
for (const key in entry) {
|
||||
if (entry[key]?.Value) return entry[key].Value;
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
|
||||
function extractDescription(notes = [], fallback = '') {
|
||||
const note = notes.find(n =>
|
||||
n.Title === 'Description' &&
|
||||
n.Type === 2 &&
|
||||
typeof n.Value === 'string' &&
|
||||
n.Value.trim().length > 0
|
||||
);
|
||||
return note?.Value || fallback;
|
||||
}
|
||||
|
||||
function extractAffectedProducts(vuln) {
|
||||
const fixed = vuln.ProductStatuses?.Fixed ?? [];
|
||||
const known = vuln.ProductStatuses?.KnownAffected ?? [];
|
||||
const underInvestigation = vuln.ProductStatuses?.UnderInvestigation ?? [];
|
||||
return [...new Set([...fixed, ...known, ...underInvestigation])].join(', ');
|
||||
}
|
||||
|
||||
async function fetchCVRFDoc(cvrfId) {
|
||||
const url = `https://api.msrc.microsoft.com/cvrf/v3.0/cvrf/${cvrfId}`;
|
||||
try {
|
||||
const res = await axios.get(url, {
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
return res.data;
|
||||
} catch (err) {
|
||||
console.error(`❌ Failed to fetch CVRF for ${cvrfId}:`, err.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function enrichCVE(cve, data) {
|
||||
const [rows] = await DB.execute(
|
||||
`SELECT title, severity, description, cvss_score, cvss_vector FROM cves WHERE id = ?`,
|
||||
[cve]
|
||||
);
|
||||
|
||||
if (rows.length === 0) return;
|
||||
|
||||
const existing = rows[0];
|
||||
const updateFields = {
|
||||
title: existing.title || data.title,
|
||||
severity: existing.severity || data.severity,
|
||||
description: (existing.description?.length ?? 0) < 20 ? data.description : existing.description,
|
||||
cvss_score: existing.cvss_score ?? data.cvssScore,
|
||||
cvss_vector: existing.cvss_vector || data.cvssVector,
|
||||
};
|
||||
|
||||
await DB.execute(
|
||||
`UPDATE cves
|
||||
SET title = ?, severity = ?, description = ?, cvss_score = ?, cvss_vector = ?
|
||||
WHERE id = ?`,
|
||||
[updateFields.title, updateFields.severity, updateFields.description, updateFields.cvss_score, updateFields.cvss_vector, cve]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
async function storeVulnerability(v, published, modified) {
|
||||
const cve = v.CVE;
|
||||
console.log(`⏳ Processing ${cve}...`);
|
||||
|
||||
const title = getLocalizedText(v.Title, '');
|
||||
console.debug(`🧪 Raw Title for ${cve}:`, JSON.stringify(v.Title, null, 2));
|
||||
console.debug(`➡️ Parsed Title: "${title}"`);
|
||||
const description = extractDescription(v.Notes, title);
|
||||
const affectedProducts = extractAffectedProducts(v);
|
||||
const severity = v.Threats?.find(t => t.Type === 'Severity')?.Description?.Value || '';
|
||||
const exploitability = v.Threats?.find(t => t.Type === 'Exploitability')?.Description?.Value || '';
|
||||
const cvssSet = v.CVSSScoreSets?.[0];
|
||||
const cvssScore = cvssSet?.BaseScore || null;
|
||||
const cvssVector = cvssSet?.Vector || '';
|
||||
const cweList = Array.isArray(v.CWE) ? v.CWE : [];
|
||||
|
||||
await DB.execute(
|
||||
`INSERT INTO microsoft_cves (cve_id, title, severity, published_date, last_modified_date,
|
||||
affected_products, description, exploitability, cvss_score, cvss_vector)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
last_modified_date = VALUES(last_modified_date),
|
||||
affected_products = VALUES(affected_products),
|
||||
severity = VALUES(severity),
|
||||
description = VALUES(description),
|
||||
exploitability = VALUES(exploitability),
|
||||
cvss_score = VALUES(cvss_score),
|
||||
cvss_vector = VALUES(cvss_vector)`,
|
||||
[cve, title, severity, published, modified, affectedProducts, description, exploitability, cvssScore, cvssVector]
|
||||
);
|
||||
|
||||
for (const { ID: cweId, Value: cweName } of cweList) {
|
||||
if (!cweId) continue;
|
||||
|
||||
await DB.execute(
|
||||
`INSERT IGNORE INTO cwe_entries (cwe_id, cwe_name) VALUES (?, ?)`,
|
||||
[cweId, cweName || null]
|
||||
);
|
||||
|
||||
await DB.execute(
|
||||
`INSERT IGNORE INTO cve_cwe (cve_id, cwe_id) VALUES (?, ?)`,
|
||||
[cve, cweId]
|
||||
);
|
||||
}
|
||||
|
||||
await enrichCVE(cve, { title, severity, description, cvssScore, cvssVector });
|
||||
console.log(`✅ Updated ${cve} in master table`);
|
||||
}
|
||||
|
||||
function getPreviousMonthID() {
|
||||
const now = new Date();
|
||||
now.setMonth(now.getMonth() - 1);
|
||||
const year = now.getFullYear();
|
||||
const month = now.toLocaleString('en-US', { month: 'short' }); // e.g., "Mar"
|
||||
return `${year}-${month}`;
|
||||
}
|
||||
|
||||
async function enrichPreviousMonth() {
|
||||
const cvrfId = getPreviousMonthID();
|
||||
console.log(`📡 Fetching CVEs for ${cvrfId}...`);
|
||||
|
||||
const data = await fetchCVRFDoc(cvrfId);
|
||||
if (!data) return;
|
||||
|
||||
const published = formatDate(data.DocumentTracking?.InitialReleaseDate);
|
||||
const modified = formatDate(data.DocumentTracking?.CurrentReleaseDate);
|
||||
|
||||
const vulns = data.Vulnerability || [];
|
||||
console.log(`📋 Found ${vulns.length} vulnerabilities.`);
|
||||
|
||||
for (const v of vulns) {
|
||||
await storeVulnerability(v, published, modified);
|
||||
}
|
||||
|
||||
console.log(`🎉 Enrichment complete for ${cvrfId}`);
|
||||
await DB.end();
|
||||
}
|
||||
|
||||
console.log("🚀 Starting MSRC enrichment of CVEs...");
|
||||
await enrichPreviousMonth();
|
||||
Reference in New Issue
Block a user