Implements a comprehensive system for uploading and processing weekly vulnerability reports that automatically splits multiple CVE IDs in a single cell into separate rows for easier filtering and analysis. Backend Changes: - Add weekly_reports table with migration - Create Excel processor helper using Python child_process - Implement API routes for upload, list, download, delete - Mount routes in server.js after multer initialization - Move split_cve_report.py to backend/scripts/ Frontend Changes: - Add WeeklyReportModal component with phase-based UI - Add "Weekly Report" button next to NVD Sync - Integrate modal into App.js with state management - Display existing reports with current report indicator - Download buttons for original and processed files Features: - Upload .xlsx files (editor/admin only) - Automatic CVE ID splitting via Python script - Store metadata in database + files on filesystem - Auto-archive previous reports (mark one as current) - Download both original and processed versions - Audit logging for all operations - Security: file validation, auth checks, path sanitization Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
94 lines
2.9 KiB
JavaScript
94 lines
2.9 KiB
JavaScript
const { spawn } = require('child_process');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
/**
|
|
* Process vulnerability report Excel file by splitting CVE IDs into separate rows
|
|
* @param {string} inputPath - Path to original Excel file
|
|
* @param {string} outputPath - Path for processed Excel file
|
|
* @returns {Promise<{original_rows: number, processed_rows: number, output_path: string}>}
|
|
*/
|
|
function processVulnerabilityReport(inputPath, outputPath) {
|
|
return new Promise((resolve, reject) => {
|
|
const scriptPath = path.join(__dirname, '..', 'scripts', 'split_cve_report.py');
|
|
|
|
// Verify script exists
|
|
if (!fs.existsSync(scriptPath)) {
|
|
return reject(new Error(`Python script not found: ${scriptPath}`));
|
|
}
|
|
|
|
// Verify input file exists
|
|
if (!fs.existsSync(inputPath)) {
|
|
return reject(new Error(`Input file not found: ${inputPath}`));
|
|
}
|
|
|
|
const python = spawn('python3', [scriptPath, inputPath, outputPath]);
|
|
|
|
let stdout = '';
|
|
let stderr = '';
|
|
let timedOut = false;
|
|
|
|
// 30 second timeout
|
|
const timeout = setTimeout(() => {
|
|
timedOut = true;
|
|
python.kill();
|
|
reject(new Error('Processing timed out. File may be too large or corrupted.'));
|
|
}, 30000);
|
|
|
|
python.stdout.on('data', (data) => {
|
|
stdout += data.toString();
|
|
});
|
|
|
|
python.stderr.on('data', (data) => {
|
|
stderr += data.toString();
|
|
});
|
|
|
|
python.on('close', (code) => {
|
|
clearTimeout(timeout);
|
|
|
|
if (timedOut) return;
|
|
|
|
if (code !== 0) {
|
|
// Parse Python error messages
|
|
if (stderr.includes('Sheet') && stderr.includes('not found')) {
|
|
return reject(new Error('Invalid Excel file. Expected "Vulnerabilities" sheet with "CVE ID" column.'));
|
|
}
|
|
if (stderr.includes('pandas') || stderr.includes('openpyxl')) {
|
|
return reject(new Error('Python dependencies missing. Run: pip3 install pandas openpyxl'));
|
|
}
|
|
return reject(new Error(`Python script failed: ${stderr || 'Unknown error'}`));
|
|
}
|
|
|
|
// Parse output for row counts
|
|
const originalMatch = stdout.match(/Original rows:\s*(\d+)/);
|
|
const newMatch = stdout.match(/New rows:\s*(\d+)/);
|
|
|
|
if (!originalMatch || !newMatch) {
|
|
return reject(new Error('Failed to parse row counts from Python output'));
|
|
}
|
|
|
|
// Verify output file was created
|
|
if (!fs.existsSync(outputPath)) {
|
|
return reject(new Error('Processed file was not created'));
|
|
}
|
|
|
|
resolve({
|
|
original_rows: parseInt(originalMatch[1]),
|
|
processed_rows: parseInt(newMatch[1]),
|
|
output_path: outputPath
|
|
});
|
|
});
|
|
|
|
python.on('error', (err) => {
|
|
clearTimeout(timeout);
|
|
if (err.code === 'ENOENT') {
|
|
reject(new Error('Python 3 is required but not found. Please install Python.'));
|
|
} else {
|
|
reject(err);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
module.exports = { processVulnerabilityReport };
|