Files
ld-sysinfo-server-backend/scripts/verifyCVECountAPI.js
Bailey Taylor c43b3a65c5
All checks were successful
Build & Deploy Backend / build (push) Successful in 47s
Build & Deploy Backend / deploy (push) Successful in 31s
New Github support for CVE verification.
2025-10-08 10:41:19 +08:00

210 lines
5.9 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
import axios from 'axios';
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());
const line = `[${now}] ${msg}`;
console.log(line);
}
async function getGitHubCVEStats() {
log('📡 Fetching CVE statistics from GitHub API...');
try {
// Get repository tree (cves directory structure)
const response = await axios.get(
'https://api.github.com/repos/CVEProject/cvelistV5/git/trees/main?recursive=1',
{
headers: {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'CVE-Verification-Script'
}
}
);
const tree = response.data.tree;
// Count .json files in cves/ directory
const cveFiles = tree.filter(item =>
item.path.startsWith('cves/') &&
item.path.endsWith('.json') &&
item.type === 'blob'
);
// Group by year
const byYear = {};
cveFiles.forEach(file => {
const match = file.path.match(/cves\/(\d{4})\//);
if (match) {
const year = match[1];
byYear[year] = (byYear[year] || 0) + 1;
}
});
log(`✅ Found ${cveFiles.length} CVE files in GitHub repository`);
return {
total: cveFiles.length,
byYear,
lastCommit: response.data.sha
};
} catch (err) {
log(`❌ GitHub API error: ${err.message}`);
if (err.response?.status === 403) {
log('⚠️ GitHub API rate limit exceeded. Try again later or clone the repository.');
}
throw err;
}
}
async function countCVEsInDatabase() {
log('🗄️ Counting CVEs in database...');
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,
});
// Total count
const [totalRows] = await db.query('SELECT COUNT(*) as total FROM cves');
const total = totalRows[0].total;
// Count by year
const [yearRows] = await db.query(`
SELECT
YEAR(published_date) as year,
COUNT(*) as count
FROM cves
WHERE published_date IS NOT NULL
GROUP BY YEAR(published_date)
ORDER BY year
`);
const byYear = {};
yearRows.forEach(row => {
byYear[row.year] = row.count;
});
// Get date range
const [rangeRows] = await db.query(`
SELECT
MIN(published_date) as earliest,
MAX(published_date) as latest
FROM cves
`);
await db.end();
log(`✅ Found ${total} CVEs in database`);
return {
total,
byYear,
earliest: rangeRows[0].earliest,
latest: rangeRows[0].latest
};
}
function compareResults(github, db) {
log('\n📋 Comparison Report:');
log('━'.repeat(70));
log(`GitHub Repository: ${github.total.toLocaleString()} CVEs`);
log(`Your Database: ${db.total.toLocaleString()} CVEs`);
log(`Difference: ${(db.total - github.total).toLocaleString()}`);
log('━'.repeat(70));
log(`\nDatabase Date Range:`);
log(` Earliest: ${db.earliest ? new Date(db.earliest).toLocaleDateString() : 'N/A'}`);
log(` Latest: ${db.latest ? new Date(db.latest).toLocaleDateString() : 'N/A'}`);
// Get all years
const allYears = new Set([
...Object.keys(github.byYear),
...Object.keys(db.byYear)
]);
const sortedYears = Array.from(allYears).sort();
log('\n📅 Year-by-Year Breakdown:');
log('━'.repeat(70));
log('Year | GitHub | Database | Difference | % Complete');
log('━'.repeat(70));
const significantDiffs = [];
for (const year of sortedYears) {
const githubCount = github.byYear[year] || 0;
const dbCount = db.byYear[year] || 0;
const diff = dbCount - githubCount;
const pctComplete = githubCount > 0 ? ((dbCount / githubCount) * 100).toFixed(1) : '0.0';
const diffStr = diff >= 0 ? `+${diff}` : diff.toString();
log(`${year} | ${githubCount.toString().padStart(10)} | ${dbCount.toString().padStart(10)} | ${diffStr.padStart(10)} | ${pctComplete.padStart(6)}%`);
if (Math.abs(diff) > 100) {
significantDiffs.push({ year, githubCount, dbCount, diff });
}
}
log('━'.repeat(70));
if (significantDiffs.length > 0) {
log('\n⚠ Years with significant differences (>100):');
significantDiffs.forEach(({ year, githubCount, dbCount, diff }) => {
if (diff < 0) {
log(` ${year}: Missing ${Math.abs(diff)} CVEs (${dbCount}/${githubCount})`);
} else {
log(` ${year}: Extra ${diff} CVEs (database has more than GitHub)`);
}
});
}
const percentComplete = github.total > 0
? ((db.total / github.total) * 100).toFixed(2)
: '0.00';
log(`\n📊 Overall Completion: ${percentComplete}%`);
if (db.total >= github.total) {
log('🎉 Your database has all CVEs from the official GitHub repository!');
if (db.total > github.total) {
log(' (You may have older CVEs or modified entries not in the current repository)');
}
} else {
const missing = github.total - db.total;
log(`⚠️ Missing ${missing.toLocaleString()} CVEs from GitHub repository`);
log(` Run the backfill script to sync missing CVEs.`);
}
}
async function main() {
try {
log('🚀 Starting CVE verification using GitHub API...\n');
// Step 1: Get stats from GitHub API
const githubStats = await getGitHubCVEStats();
// Step 2: Count CVEs in database
const dbStats = await countCVEsInDatabase();
// Step 3: Compare
compareResults(githubStats, dbStats);
log('\n✅ Verification complete!');
} catch (err) {
log(`❌ Error: ${err.message}`);
console.error(err);
process.exit(1);
}
}
main();