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 };
|