#!/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); });