UNPKG

dce-dev-wizard

Version:

Wizard for managing development apps at Harvard DCE.

195 lines (177 loc) 5.99 kB
// Import libs import clear from 'clear'; import fs from 'fs'; import path from 'path'; import Papa from "papaparse"; // Import helpers import print from '../helpers/print'; import prompt from '../helpers/prompt'; import showChooser from '../helpers/showChooser'; import scrapeCVE from '../helpers/scrapeCVE'; // Import shared types import WizReportItem from '../types/WizReportItem'; import WizCSVRow from '../types/WizCSVRow'; import WizSeverity from '../types/WizSeverity'; // Get project directory const currDir = (process.env.PWD || process.env.CWD) ?? __dirname; /** * Review current CVEs (from Wiz CSV) and decide how to handle each one * @author Allison Zhang * @author Gabe Abrams */ const reviewCVEs = async () => { // Clear the screen and show title clear(); print.title('Import Wiz CSV'); console.log('\nFind the desired Wiz Report CSV in your directory and drag/drop it into this window.\n'); // Get the file path const wizReportPath = prompt('> ', true).replace(/['"]/g, '').trim(); // Read in the trivy results if (!fs.existsSync(wizReportPath)) { clear(); print.title('Error [File Not Found]'); console.error(`\nFile not found: ${wizReportPath}\n`); print.enterToContinue(); return; } // Convert to object let wizResults; try { wizResults = Papa.parse<WizCSVRow>(fs.readFileSync(wizReportPath, 'utf8'), { header: true, skipEmptyLines: true, delimitersToGuess: [',', ';', '\t'], dynamicTyping: true, }); } catch (error) { clear(); print.title('Error [CSV Invalid]'); console.error(`\nError parsing CSV file: ${error}\n`); print.enterToContinue(); return; } // Make sure that the schema is correct if (!wizResults || !wizResults.data) { clear(); print.title('Error [Invalid Wiz Report]'); console.error('\nWiz results should use \',\', \';\', and \'\\t\'. Please provide a Wiz scan CSV file.\n'); print.enterToContinue(); return; } let wizRows: WizReportItem[] = []; for (const row of wizResults.data) { let wizRow: WizReportItem = { wizURL: row.WizURL, cve: row.Name, severity: null, remediation: row.Remediation, locationPath: row.LocationPath, package: row.DetailedName, version: row.Version, fixedVersion: row.FixedVersion, advisoryLink: row.Link, awsAccount: row.SubscriptionName, }; switch (row.Severity) { case 'Critical': wizRow.severity = WizSeverity.Critical; break; case 'High': wizRow.severity = WizSeverity.High; break; case 'Medium': wizRow.severity = WizSeverity.Medium; break; case 'Low': wizRow.severity = WizSeverity.Low; break; default: wizRow.severity = null; }; wizRows.push(wizRow); }; // List of explanations for reviewed CVEs const reviewedCVEs: { cve: string, title: string, explanation: string, }[] = []; // If no vulnerabilities, exit if (!wizRows || wizRows.length === 0) { clear(); print.title('Success!'); console.log('\nNo vulnerabilities found! 🎉\n'); print.enterToContinue(); return; } else { // Loop through vulnerabilities for (let i: number = 0; i < wizRows.length; i++) { clear(); let information: { [key: string]: string } try { information = await scrapeCVE(wizRows[i].advisoryLink); } catch (error) { console.error(`Error scraping CVE information: ${error}`); information = { title: 'Unknown', description: 'Unknown' }; } print.title(`Severity: ${wizRows[i].severity} | Vulnerability ${i+1} of ${wizRows.length}`); console.log(`CVE: ${wizRows[i].cve}\n`); console.log(`Title: ${information.title}\n`); console.log(`Description: ${information.description}\n`); console.log(`Version (Installed): ${wizRows[i].version}`); console.log(`Version (Fixed): ${wizRows[i].fixedVersion}`); console.log(`Primary URL: ${wizRows[i].wizURL}`); console.log(`Advisory Link: ${wizRows[i].advisoryLink}\n`); console.log(`Remediation:\n${wizRows[i].remediation}\n`); console.log(`Location Path: ${wizRows[i].locationPath}\n`); console.log(`AWS Account: ${wizRows[i].awsAccount}\n`); // Ask user what to do with this vulnerability const vulnerabilityOptions = showChooser({ question: 'Review Vulnerability:', options: [ { description: 'Reviewed (Not a problem)', tag: 'R', }, { description: 'Snooze (Will fix later)', tag: 'S', }, ], dontClear: true, }) // If reviewing, ask for explanation, then add to reviewed CVEs list if (vulnerabilityOptions.tag === 'R') { console.log('\nPlease explain why this vulnerability can be ignored.'); console.log('Explanation: '); let explanation = prompt('', true).trim(); while (!explanation || explanation.length === 0) { console.log('Please provide an explanation: '); explanation = prompt('', true); } reviewedCVEs.push({ cve: wizRows[i].cve, title: information.title, explanation: explanation, }); } // If snoozing, just continue } } // Write to .reviewed-cves file const reviewedCVEsPath = path.join(currDir, '.reviewed-cves'); // Append all reviewed CVEs to the file let reviewedCVEsContent: string = ''; reviewedCVEs.forEach((cve) => { reviewedCVEsContent += `\n# ${cve.title}\n# Note: ${cve.explanation}\n`; }); try { fs.appendFileSync(reviewedCVEsPath, reviewedCVEsContent); } catch (error) { print.title('Error [File Append]'); console.error(`\nError appending reviewed CVEs to ${reviewedCVEsPath}: ${error}\n`); print.enterToContinue(); return; } }; export default reviewCVEs;