Updated fetchcve scripts with enrichments.
All checks were successful
Build & Deploy Backend / build (push) Successful in 47s
Build & Deploy Backend / deploy (push) Successful in 31s

This commit is contained in:
2025-10-08 11:07:04 +08:00
parent c43b3a65c5
commit 2b5aaa1401
6 changed files with 341 additions and 9 deletions

View File

@@ -0,0 +1,169 @@
#!/usr/bin/env node
import mysql from 'mysql2/promise';
function log(msg) {
const now = new Date().toLocaleString('en-AU', {
day: '2-digit', month: 'short', year: 'numeric',
hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true
}).replace(/\b(AM|PM)\b/, m => m.toLowerCase());
console.log(`[${now}] ${msg}`);
}
async function analyzeDatabaseQuality() {
const db = await mysql.createConnection({
host: process.env.DB_HOST,
port: process.env.DB_PORT || 3306,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
log('🔍 Analyzing CVE Database Quality...\n');
log('━'.repeat(70));
// Total CVEs
const [total] = await db.query('SELECT COUNT(*) as count FROM cves');
log(`📊 Total CVEs: ${total[0].count.toLocaleString()}`);
// CVEs with CVSS v3 scores
const [v3] = await db.query('SELECT COUNT(*) as count FROM cves WHERE cvss_score_v3 IS NOT NULL');
const v3Pct = ((v3[0].count / total[0].count) * 100).toFixed(1);
log(`📈 CVEs with CVSS v3 scores: ${v3[0].count.toLocaleString()} (${v3Pct}%)`);
// CVEs with CVSS v2 scores
const [v2] = await db.query('SELECT COUNT(*) as count FROM cves WHERE cvss_score_v2 IS NOT NULL');
const v2Pct = ((v2[0].count / total[0].count) * 100).toFixed(1);
log(`📈 CVEs with CVSS v2 scores: ${v2[0].count.toLocaleString()} (${v2Pct}%)`);
// CVEs with CVSS v4 scores
const [v4] = await db.query('SELECT COUNT(*) as count FROM cves WHERE cvss_score_v4 IS NOT NULL');
const v4Pct = ((v4[0].count / total[0].count) * 100).toFixed(1);
log(`📈 CVEs with CVSS v4 scores: ${v4[0].count.toLocaleString()} (${v4Pct}%)`);
// CVEs with CWE IDs
const [cwe] = await db.query('SELECT COUNT(*) as count FROM cves WHERE cwe_ids IS NOT NULL AND cwe_ids != ""');
const cwePct = ((cwe[0].count / total[0].count) * 100).toFixed(1);
log(`🏷️ CVEs with CWE IDs: ${cwe[0].count.toLocaleString()} (${cwePct}%)`);
// CVEs with references
const [refs] = await db.query('SELECT COUNT(*) as count FROM cves WHERE `references` IS NOT NULL AND `references` != ""');
const refsPct = ((refs[0].count / total[0].count) * 100).toFixed(1);
log(`🔗 CVEs with references: ${refs[0].count.toLocaleString()} (${refsPct}%)`);
// KEV data
const [kev] = await db.query('SELECT COUNT(*) as count FROM kev_catalog');
log(`🛡️ Known Exploited Vulnerabilities (KEV): ${kev[0].count.toLocaleString()}`);
// Microsoft CVEs
const [msrc] = await db.query('SELECT COUNT(*) as count FROM microsoft_cves');
log(`🖥️ Microsoft CVEs: ${msrc[0].count.toLocaleString()}`);
// CPE matches
const [cpe] = await db.query('SELECT COUNT(*) as count FROM cpe_matches');
log(`💿 CPE matches (affected software): ${cpe[0].count.toLocaleString()}`);
log('\n━'.repeat(70));
log('📅 CVEs by Severity (CVSS v3):');
log('━'.repeat(70));
const [severity] = await db.query(`
SELECT
severity_v3,
COUNT(*) as count,
ROUND(AVG(cvss_score_v3), 1) as avg_score
FROM cves
WHERE severity_v3 IS NOT NULL
GROUP BY severity_v3
ORDER BY
CASE severity_v3
WHEN 'CRITICAL' THEN 1
WHEN 'HIGH' THEN 2
WHEN 'MEDIUM' THEN 3
WHEN 'LOW' THEN 4
ELSE 5
END
`);
severity.forEach(row => {
const pct = ((row.count / total[0].count) * 100).toFixed(1);
const icon = {
'CRITICAL': '🔴',
'HIGH': '🟠',
'MEDIUM': '🟡',
'LOW': '🟢'
}[row.severity_v3] || '⚪';
log(`${icon} ${(row.severity_v3 || 'UNKNOWN').padEnd(10)} ${row.count.toString().padStart(8)} (${pct.padStart(5)}%) - Avg: ${row.avg_score}`);
});
log('\n━'.repeat(70));
log('📅 Recent Activity (Last 30 Days):');
log('━'.repeat(70));
const [recent] = await db.query(`
SELECT COUNT(*) as count
FROM cves
WHERE last_modified_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
`);
log(`🆕 CVEs modified in last 30 days: ${recent[0].count.toLocaleString()}`);
const [recentPub] = await db.query(`
SELECT COUNT(*) as count
FROM cves
WHERE published_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)
`);
log(`📝 CVEs published in last 30 days: ${recentPub[0].count.toLocaleString()}`);
log('\n━'.repeat(70));
log('🎯 Data Quality Score:');
log('━'.repeat(70));
const qualityScore = (
(parseFloat(v3Pct) * 0.3) + // 30% weight on CVSS v3
(parseFloat(cwePct) * 0.2) + // 20% weight on CWE
(parseFloat(refsPct) * 0.2) + // 20% weight on references
((cpe[0].count > 0 ? 100 : 0) * 0.15) + // 15% weight on CPE existence
((kev[0].count > 0 ? 100 : 0) * 0.15) // 15% weight on KEV data
);
log(`Overall Quality: ${qualityScore.toFixed(1)}%`);
if (qualityScore >= 90) log('✅ Excellent - Highly enriched database');
else if (qualityScore >= 75) log('✅ Good - Well enriched database');
else if (qualityScore >= 60) log('⚠️ Fair - Some enrichment needed');
else log('❌ Poor - Significant enrichment needed');
log('\n━'.repeat(70));
log('💡 Recommendations:');
log('━'.repeat(70));
if (parseFloat(v3Pct) < 80) {
log('⚠️ Run CVE enrichment to get more CVSS v3 scores');
log(' Use: POST /api/admin/scripts/fetch-cve (runs fetchCVE_v2.js in enrichment mode)');
}
if (parseFloat(cwePct) < 70) {
log('⚠️ Low CWE coverage - consider running enrichment');
}
if (kev[0].count < 1000) {
log('⚠️ KEV data seems low - run: POST /api/admin/scripts/fetch-kev');
}
if (msrc[0].count < 10000) {
log('⚠️ Microsoft CVE data seems low - run: POST /api/admin/scripts/fetch-msrc');
}
if (recent[0].count < 100) {
log('⚠️ No recent updates detected - run daily sync to stay current');
log(' Use: POST /api/admin/scripts/fetch-cve');
}
await db.end();
log('\n✅ Analysis complete!\n');
}
analyzeDatabaseQuality().catch(err => {
console.error('❌ Error:', err.message);
process.exit(1);
});

View File

@@ -126,3 +126,102 @@
[08 Oct 2025, 10:22:57 am] 🛑 Reached earliest supported CVE publication date — halting backfill.
[08 Oct 2025, 10:22:57 am] ✅ CVE backfill complete!
[08 Oct 2025, 02:22:57 am] ✅ fetchCVE_withMORE.js finished with exit code: 0
[08 Oct 2025, 02:45:32 am] 🚀 🔍 CVE verification started - comparing with GitHub API.
[08 Oct 2025, 10:45:32 am] 🚀 Starting CVE verification using GitHub API...
[08 Oct 2025, 10:45:32 am] 📡 Fetching CVE statistics from GitHub API...
[08 Oct 2025, 10:45:37 am] ✅ Found 77106 CVE files in GitHub repository
[08 Oct 2025, 10:45:37 am] 🗄️ Counting CVEs in database...
[08 Oct 2025, 10:45:42 am] ✅ Found 301533 CVEs in database
[08 Oct 2025, 10:45:42 am]
📋 Comparison Report:
[08 Oct 2025, 10:45:42 am] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[08 Oct 2025, 10:45:42 am] GitHub Repository: 77,106 CVEs
[08 Oct 2025, 10:45:42 am] Your Database: 301,533 CVEs
[08 Oct 2025, 10:45:42 am] Difference: 224,427
[08 Oct 2025, 10:45:42 am] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[08 Oct 2025, 10:45:42 am]
Database Date Range:
[08 Oct 2025, 10:45:42 am] Earliest: 9/30/1988
[08 Oct 2025, 10:45:42 am] Latest: 10/7/2025
[08 Oct 2025, 10:45:42 am]
📅 Year-by-Year Breakdown:
[08 Oct 2025, 10:45:42 am] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[08 Oct 2025, 10:45:42 am] Year | GitHub | Database | Difference | % Complete
[08 Oct 2025, 10:45:42 am] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[08 Oct 2025, 10:45:42 am] 1988 | 0 | 3 | +3 | 0.0%
[08 Oct 2025, 10:45:42 am] 1989 | 0 | 2 | +2 | 0.0%
[08 Oct 2025, 10:45:42 am] 1990 | 0 | 11 | +11 | 0.0%
[08 Oct 2025, 10:45:42 am] 1991 | 0 | 15 | +15 | 0.0%
[08 Oct 2025, 10:45:42 am] 1992 | 0 | 14 | +14 | 0.0%
[08 Oct 2025, 10:45:42 am] 1993 | 0 | 14 | +14 | 0.0%
[08 Oct 2025, 10:45:42 am] 1994 | 0 | 26 | +26 | 0.0%
[08 Oct 2025, 10:45:42 am] 1995 | 0 | 24 | +24 | 0.0%
[08 Oct 2025, 10:45:42 am] 1996 | 0 | 116 | +116 | 0.0%
[08 Oct 2025, 10:45:42 am] 1997 | 0 | 220 | +220 | 0.0%
[08 Oct 2025, 10:45:42 am] 1998 | 0 | 380 | +380 | 0.0%
[08 Oct 2025, 10:45:42 am] 1999 | 1579 | 785 | -794 | 49.7%
[08 Oct 2025, 10:45:42 am] 2000 | 1242 | 1021 | -221 | 82.2%
[08 Oct 2025, 10:45:42 am] 2001 | 1556 | 1675 | +119 | 107.6%
[08 Oct 2025, 10:45:42 am] 2002 | 2393 | 2170 | -223 | 90.7%
[08 Oct 2025, 10:45:42 am] 2003 | 1555 | 1548 | -7 | 99.5%
[08 Oct 2025, 10:45:42 am] 2004 | 2707 | 2480 | -227 | 91.6%
[08 Oct 2025, 10:45:42 am] 2005 | 4769 | 5009 | +240 | 105.0%
[08 Oct 2025, 10:45:42 am] 2006 | 7143 | 6659 | -484 | 93.2%
[08 Oct 2025, 10:45:42 am] 2007 | 6580 | 6596 | +16 | 100.2%
[08 Oct 2025, 10:45:42 am] 2008 | 7177 | 5664 | -1513 | 78.9%
[08 Oct 2025, 10:45:42 am] 2009 | 5052 | 5778 | +726 | 114.4%
[08 Oct 2025, 10:45:42 am] 2010 | 5244 | 4667 | -577 | 89.0%
[08 Oct 2025, 10:45:42 am] 2011 | 4886 | 4172 | -714 | 85.4%
[08 Oct 2025, 10:45:42 am] 2012 | 5937 | 5351 | -586 | 90.1%
[08 Oct 2025, 10:45:42 am] 2013 | 6819 | 5324 | -1495 | 78.1%
[08 Oct 2025, 10:45:42 am] 2014 | 9000 | 8017 | -983 | 89.1%
[08 Oct 2025, 10:45:42 am] 2015 | 3467 | 6596 | +3129 | 190.3%
[08 Oct 2025, 10:45:42 am] 2016 | 0 | 6507 | +6507 | 0.0%
[08 Oct 2025, 10:45:42 am] 2017 | 0 | 18116 | +18116 | 0.0%
[08 Oct 2025, 10:45:42 am] 2018 | 0 | 18151 | +18151 | 0.0%
[08 Oct 2025, 10:45:42 am] 2019 | 0 | 18938 | +18938 | 0.0%
[08 Oct 2025, 10:45:42 am] 2020 | 0 | 19250 | +19250 | 0.0%
[08 Oct 2025, 10:45:42 am] 2021 | 0 | 21962 | +21962 | 0.0%
[08 Oct 2025, 10:45:42 am] 2022 | 0 | 26445 | +26445 | 0.0%
[08 Oct 2025, 10:45:42 am] 2023 | 0 | 30895 | +30895 | 0.0%
[08 Oct 2025, 10:45:42 am] 2024 | 0 | 40704 | +40704 | 0.0%
[08 Oct 2025, 10:45:42 am] 2025 | 0 | 26228 | +26228 | 0.0%
[08 Oct 2025, 10:45:42 am] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[08 Oct 2025, 10:45:42 am]
⚠️ Years with significant differences (>100):
[08 Oct 2025, 10:45:42 am] 1996: Extra 116 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 1997: Extra 220 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 1998: Extra 380 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 1999: Missing 794 CVEs (785/1579)
[08 Oct 2025, 10:45:42 am] 2000: Missing 221 CVEs (1021/1242)
[08 Oct 2025, 10:45:42 am] 2001: Extra 119 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2002: Missing 223 CVEs (2170/2393)
[08 Oct 2025, 10:45:42 am] 2004: Missing 227 CVEs (2480/2707)
[08 Oct 2025, 10:45:42 am] 2005: Extra 240 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2006: Missing 484 CVEs (6659/7143)
[08 Oct 2025, 10:45:42 am] 2008: Missing 1513 CVEs (5664/7177)
[08 Oct 2025, 10:45:42 am] 2009: Extra 726 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2010: Missing 577 CVEs (4667/5244)
[08 Oct 2025, 10:45:42 am] 2011: Missing 714 CVEs (4172/4886)
[08 Oct 2025, 10:45:42 am] 2012: Missing 586 CVEs (5351/5937)
[08 Oct 2025, 10:45:42 am] 2013: Missing 1495 CVEs (5324/6819)
[08 Oct 2025, 10:45:42 am] 2014: Missing 983 CVEs (8017/9000)
[08 Oct 2025, 10:45:42 am] 2015: Extra 3129 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2016: Extra 6507 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2017: Extra 18116 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2018: Extra 18151 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2019: Extra 18938 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2020: Extra 19250 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2021: Extra 21962 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2022: Extra 26445 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2023: Extra 30895 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2024: Extra 40704 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am] 2025: Extra 26228 CVEs (database has more than GitHub)
[08 Oct 2025, 10:45:42 am]
📊 Overall Completion: 391.06%
[08 Oct 2025, 10:45:42 am] 🎉 Your database has all CVEs from the official GitHub repository!
[08 Oct 2025, 10:45:42 am] (You may have older CVEs or modified entries not in the current repository)
[08 Oct 2025, 10:45:42 am]
✅ Verification complete!
[08 Oct 2025, 02:45:42 am] ✅ verifyCVECountAPI.js finished with exit code: 0

View File

@@ -110,18 +110,82 @@ async function fetchCVEPage(startIndex, startDate, endDate) {
async function processCVE(cveWrapper) {
const cve = cveWrapper.cve;
const cveId = cve.id;
const title = cve.titles?.find(t => t.lang === 'en')?.title || '';
const desc = cve.descriptions.find(d => d.lang === 'en')?.value ?? '';
const published = formatDate(cve.published);
const modified = formatDate(cve.lastModified);
const severity = cve.metrics?.cvssMetricV31?.[0]?.cvssData?.baseSeverity ?? null;
const score = cve.metrics?.cvssMetricV31?.[0]?.cvssData?.baseScore ?? null;
// CVSSv2
const metricV2 = cve.metrics?.cvssMetricV2?.[0];
const severityV2 = metricV2?.cvssData?.baseSeverity || null;
const scoreV2 = metricV2?.cvssData?.baseScore || null;
const vectorV2 = metricV2?.cvssData?.vectorString || '';
// CVSSv3
const metricV3 = cve.metrics?.cvssMetricV31?.[0];
const severityV3 = metricV3?.cvssData?.baseSeverity || null;
const scoreV3 = metricV3?.cvssData?.baseScore || null;
const vectorV3 = metricV3?.cvssData?.vectorString || '';
// CVSSv4
const metricV4 = cve.metrics?.cvssMetricV40?.[0] || cve.metrics?.cvssMetricV4?.[0];
const severityV4 = metricV4?.cvssData?.baseSeverity || null;
const scoreV4 = metricV4?.cvssData?.baseScore || null;
const vectorV4 = metricV4?.cvssData?.vectorString || '';
// CWE IDs
const cweIds = (cve.weaknesses || [])
.flatMap(w => w.description || [])
.filter(desc => desc.lang === 'en')
.map(desc => desc.value)
.join(',');
// References
const references = (cve.references || [])
.map(ref => ref.url)
.join(',');
// Tags
const cveTags = cve.cveMetadata?.cveTags || [];
const hasKev = cveTags.includes('Known_Exploited_Vulnerability');
const hasCertNotes = cveTags.includes('CERT-VN');
const hasCertAlerts = cveTags.includes('US-CERT-TA');
try {
await DB.execute(
`INSERT INTO cves (id, description, published_date, last_modified_date, severity, cvss_score)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE last_modified_date = VALUES(last_modified_date)`,
[cveId, desc, published, modified, severity, score]
`INSERT INTO cves (
id, title, description, published_date, last_modified_date,
severity_v2, cvss_score_v2, cvss_vector_v2,
severity_v3, cvss_score_v3, cvss_vector_v3,
severity_v4, cvss_score_v4, cvss_vector_v4,
cwe_ids, \`references\`, hasKev, hasCertNotes, hasCertAlerts, source
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
last_modified_date = VALUES(last_modified_date),
title = IFNULL(title, VALUES(title)),
severity_v2 = IFNULL(severity_v2, VALUES(severity_v2)),
cvss_score_v2 = IFNULL(cvss_score_v2, VALUES(cvss_score_v2)),
cvss_vector_v2 = IFNULL(cvss_vector_v2, VALUES(cvss_vector_v2)),
severity_v3 = IFNULL(severity_v3, VALUES(severity_v3)),
cvss_score_v3 = IFNULL(cvss_score_v3, VALUES(cvss_score_v3)),
cvss_vector_v3 = IFNULL(cvss_vector_v3, VALUES(cvss_vector_v3)),
severity_v4 = IFNULL(severity_v4, VALUES(severity_v4)),
cvss_score_v4 = IFNULL(cvss_score_v4, VALUES(cvss_score_v4)),
cvss_vector_v4 = IFNULL(cvss_vector_v4, VALUES(cvss_vector_v4)),
cwe_ids = IFNULL(cwe_ids, VALUES(cwe_ids)),
\`references\` = IFNULL(\`references\`, VALUES(\`references\`)),
hasKev = VALUES(hasKev),
hasCertNotes = VALUES(hasCertNotes),
hasCertAlerts = VALUES(hasCertAlerts),
source = VALUES(source)
`,
[
cveId, title, desc, published, modified,
severityV2, scoreV2, vectorV2,
severityV3, scoreV3, vectorV3,
severityV4, scoreV4, vectorV4,
cweIds, references, hasKev ? 1 : 0, hasCertNotes ? 1 : 0, hasCertAlerts ? 1 : 0, 'NVD'
]
);
} catch (err) {
log(`❌ Error inserting CVE ${cveId}: ${err.message}`);

View File

@@ -63,7 +63,7 @@ public class ScriptController {
@PreAuthorize("hasRole('ADMIN')")
@PostMapping("/fetch-cve")
public ResponseEntity<String> runCveScript(@AuthenticationPrincipal Object user) {
return triggerScript("fetchCVE.js", "📡 CVE sync launched in background.", getCveLogFile());
return triggerScript("fetchCVE_v2.js", "📡 CVE enrichment sync launched (runs importCVEEnrichmentFast).", getCveLogFile());
}
@PreAuthorize("hasRole('ADMIN')")

View File

@@ -21,7 +21,7 @@ public class CveStatisticsService {
SELECT
(SELECT COUNT(*) FROM cves),
(SELECT COUNT(*) FROM cves WHERE title IS NULL OR title = ''),
(SELECT COUNT(*) FROM cves WHERE severity IS NULL OR severity = ''),
(SELECT COUNT(*) FROM cves WHERE (severity_v3 IS NULL OR severity_v3 = '') AND (severity_v2 IS NULL OR severity_v2 = '')),
(SELECT COUNT(*) FROM cves WHERE cvss_score IS NULL AND cvss_score_v2 IS NULL AND cvss_score_v3 IS NULL AND cvss_score_v4 IS NULL),
(SELECT COUNT(*) FROM cves WHERE cvss_vector IS NULL AND cvss_vector_v2 IS NULL AND cvss_vector_v3 IS NULL AND cvss_vector_v4 IS NULL),
(SELECT COUNT(*) FROM cves WHERE `references` IS NULL OR `references` = ''),

View File

@@ -41,7 +41,7 @@ server.ssl.key-store-type=PKCS12
# Script Controller (NVD) related
nvd.api.key=42b4f093-e8c4-4110-a7d1-6ab2ba6234aa
nvd.max-range-days=30
nvd.max-range-days=365
scripts.directory=/home/sonder/ld-sysinfo-server/scripts
scripts.logs.directory=/home/sonder/ld-sysinfo-server/scripts