dce-dev-wizard
Version:
Wizard for managing development apps at Harvard DCE.
195 lines (177 loc) • 5.99 kB
text/typescript
// 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;