Files
bitvid/scripts/generate-deps-report.cjs
T

100 lines
3.5 KiB
JavaScript

const fs = require('fs');
const audit = JSON.parse(fs.readFileSync('artifacts/npm-audit.json', 'utf8'));
const outdated = JSON.parse(fs.readFileSync('artifacts/npm-outdated.json', 'utf8'));
let report = `# Dependency Report - ${new Date().toISOString().split('T')[0]}\n\n`;
report += `## Vulnerabilities\n`;
const severities = { critical: [], high: [], moderate: [], low: [], info: [] };
if (audit.vulnerabilities) {
for (const [pkg, details] of Object.entries(audit.vulnerabilities)) {
if (severities[details.severity]) {
severities[details.severity].push({ pkg, ...details });
}
}
}
for (const sev of ['critical', 'high', 'moderate', 'low', 'info']) {
if (severities[sev].length > 0) {
report += `### ${sev.toUpperCase()} (${severities[sev].length})\n`;
severities[sev].forEach(v => {
// Process 'via' array
let viaStr = '';
if (Array.isArray(v.via)) {
viaStr = v.via.map(item => {
if (typeof item === 'string') return item;
return item.title || item.name || 'Unknown advisory';
}).join(', ');
} else {
viaStr = String(v.via);
}
report += `- **${v.pkg}**: ${viaStr}\n`;
if (sev === 'critical' || sev === 'high') {
report += ` - Path: Direct dependency? ${v.isDirect ? 'Yes' : 'No'}\n`;
if (v.fixAvailable) {
report += ` - Fix Available: ${JSON.stringify(v.fixAvailable)}\n`;
}
}
});
}
}
if (Object.keys(audit.vulnerabilities || {}).length === 0) {
report += `No vulnerabilities found.\n`;
}
report += `\n## Outdated Packages\n`;
const outdatedList = { major: [], minor: [], patch: [] };
for (const [pkg, details] of Object.entries(outdated)) {
const current = details.current;
const wanted = details.wanted;
const latest = details.latest;
if (!current) continue;
const type = details.type;
if (wanted !== current) {
const cParts = current.split('.');
const wParts = wanted.split('.');
if (cParts[0] !== wParts[0]) outdatedList.major.push({ pkg, current, wanted, latest, type });
else if (cParts[1] !== wParts[1]) outdatedList.minor.push({ pkg, current, wanted, latest, type });
else outdatedList.patch.push({ pkg, current, wanted, latest, type });
} else if (latest !== current) {
outdatedList.major.push({ pkg, current, wanted, latest, type });
}
}
report += `### Major Updates (Risky)\n`;
outdatedList.major.forEach(p => report += `- **${p.pkg}** (${p.type}): ${p.current} -> ${p.latest}\n`);
report += `\n### Minor Updates (Safe-ish)\n`;
outdatedList.minor.forEach(p => report += `- **${p.pkg}** (${p.type}): ${p.current} -> ${p.wanted} (Latest: ${p.latest})\n`);
report += `\n### Patch Updates (Safe)\n`;
outdatedList.patch.forEach(p => report += `- **${p.pkg}** (${p.type}): ${p.current} -> ${p.wanted}\n`);
report += `\n## Triage & Actions\n`;
if (severities.critical.length > 0) {
report += `- [ ] **CRITICAL**: Investigate ${severities.critical.map(v => v.pkg).join(', ')} immediately.\n`;
}
if (severities.high.length > 0) {
report += `- [ ] **HIGH**: Check for safe upgrades for ${severities.high.map(v => v.pkg).join(', ')}.\n`;
}
const safeUpgrades = [...outdatedList.patch, ...outdatedList.minor].filter(p => p.type !== 'devDependencies');
if (safeUpgrades.length > 0) {
report += `- [ ] **Safe Candidates**: ${safeUpgrades.map(p => p.pkg).join(', ')}\n`;
} else {
report += `- No safe direct dependency upgrades identified.\n`;
}
fs.writeFileSync('artifacts/deps-report.md', report);
console.log('Report generated.');