199 lines
5.6 KiB
JavaScript
Executable File
199 lines
5.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
||
|
||
import fs from 'fs';
|
||
import { execSync } from 'child_process';
|
||
import mysql from 'mysql2/promise';
|
||
import path from 'path';
|
||
|
||
const REPO_URL = 'https://github.com/CVEProject/cvelistV5.git';
|
||
const REPO_DIR = './cvelistV5';
|
||
|
||
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 cloneOrPullRepo() {
|
||
log('📦 Checking CVE repository...');
|
||
|
||
if (fs.existsSync(REPO_DIR)) {
|
||
log('🔄 Repository exists, pulling latest changes...');
|
||
try {
|
||
execSync('git pull', { cwd: REPO_DIR, stdio: 'inherit' });
|
||
log('✅ Repository updated');
|
||
} catch (err) {
|
||
log(`⚠️ Git pull failed, trying fresh clone: ${err.message}`);
|
||
execSync(`rm -rf ${REPO_DIR}`);
|
||
execSync(`git clone --depth 1 ${REPO_URL} ${REPO_DIR}`, { stdio: 'inherit' });
|
||
}
|
||
} else {
|
||
log('📥 Cloning CVE repository (this may take a while)...');
|
||
execSync(`git clone --depth 1 ${REPO_URL} ${REPO_DIR}`, { stdio: 'inherit' });
|
||
log('✅ Repository cloned');
|
||
}
|
||
}
|
||
|
||
function countCVEsInRepo() {
|
||
log('🔍 Counting CVEs in repository...');
|
||
|
||
const cvesDir = path.join(REPO_DIR, 'cves');
|
||
let totalCount = 0;
|
||
const yearCounts = {};
|
||
|
||
if (!fs.existsSync(cvesDir)) {
|
||
log('❌ CVEs directory not found');
|
||
return { total: 0, byYear: {} };
|
||
}
|
||
|
||
// Iterate through year directories (e.g., cves/2023/)
|
||
const years = fs.readdirSync(cvesDir).filter(f => /^\d{4}$/.test(f));
|
||
|
||
for (const year of years) {
|
||
const yearPath = path.join(cvesDir, year);
|
||
if (!fs.statSync(yearPath).isDirectory()) continue;
|
||
|
||
let yearCount = 0;
|
||
|
||
// Each year has subdirectories like 0xxx, 1xxx, etc.
|
||
const subdirs = fs.readdirSync(yearPath);
|
||
|
||
for (const subdir of subdirs) {
|
||
const subdirPath = path.join(yearPath, subdir);
|
||
if (!fs.statSync(subdirPath).isDirectory()) continue;
|
||
|
||
// Count .json files in this subdirectory
|
||
const files = fs.readdirSync(subdirPath).filter(f => f.endsWith('.json'));
|
||
yearCount += files.length;
|
||
}
|
||
|
||
totalCount += yearCount;
|
||
yearCounts[year] = yearCount;
|
||
}
|
||
|
||
log(`📊 Found ${totalCount} CVE JSON files in repository`);
|
||
return { total: totalCount, byYear: yearCounts };
|
||
}
|
||
|
||
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;
|
||
});
|
||
|
||
await db.end();
|
||
|
||
log(`📊 Found ${total} CVEs in database`);
|
||
return { total, byYear };
|
||
}
|
||
|
||
function compareResults(repo, db) {
|
||
log('\n📋 Comparison Report:');
|
||
log('━'.repeat(60));
|
||
log(`Repository Total: ${repo.total.toLocaleString()}`);
|
||
log(`Database Total: ${db.total.toLocaleString()}`);
|
||
log(`Difference: ${(db.total - repo.total).toLocaleString()}`);
|
||
log('━'.repeat(60));
|
||
|
||
// Get all years from both sources
|
||
const allYears = new Set([
|
||
...Object.keys(repo.byYear),
|
||
...Object.keys(db.byYear)
|
||
]);
|
||
|
||
const sortedYears = Array.from(allYears).sort();
|
||
|
||
log('\n📅 Year-by-Year Breakdown:');
|
||
log('━'.repeat(60));
|
||
log('Year | Repository | Database | Difference');
|
||
log('━'.repeat(60));
|
||
|
||
const missingYears = [];
|
||
|
||
for (const year of sortedYears) {
|
||
const repoCount = repo.byYear[year] || 0;
|
||
const dbCount = db.byYear[year] || 0;
|
||
const diff = dbCount - repoCount;
|
||
|
||
const diffStr = diff >= 0 ? `+${diff}` : diff.toString();
|
||
log(`${year} | ${repoCount.toString().padStart(10)} | ${dbCount.toString().padStart(9)} | ${diffStr}`);
|
||
|
||
if (Math.abs(diff) > 100) {
|
||
missingYears.push({ year, repoCount, dbCount, diff });
|
||
}
|
||
}
|
||
|
||
log('━'.repeat(60));
|
||
|
||
if (missingYears.length > 0) {
|
||
log('\n⚠️ Years with significant differences (>100):');
|
||
missingYears.forEach(({ year, repoCount, dbCount, diff }) => {
|
||
log(` ${year}: ${diff > 0 ? 'Extra' : 'Missing'} ${Math.abs(diff)} CVEs`);
|
||
});
|
||
}
|
||
|
||
const percentComplete = ((db.total / repo.total) * 100).toFixed(2);
|
||
log(`\n✅ Database is ${percentComplete}% complete`);
|
||
|
||
if (db.total >= repo.total) {
|
||
log('🎉 Your database has all CVEs from the official repository!');
|
||
} else {
|
||
log(`⚠️ Missing ${(repo.total - db.total).toLocaleString()} CVEs`);
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
try {
|
||
log('🚀 Starting CVE verification...\n');
|
||
|
||
// Step 1: Clone or pull repo
|
||
await cloneOrPullRepo();
|
||
|
||
// Step 2: Count CVEs in repo
|
||
const repoStats = countCVEsInRepo();
|
||
|
||
// Step 3: Count CVEs in database
|
||
const dbStats = await countCVEsInDatabase();
|
||
|
||
// Step 4: Compare
|
||
compareResults(repoStats, dbStats);
|
||
|
||
log('\n✅ Verification complete!');
|
||
} catch (err) {
|
||
log(`❌ Error: ${err.message}`);
|
||
console.error(err);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
main();
|