diff --git a/bin/repl.js b/bin/repl.js index cefac2f6f..8dc087fa7 100755 --- a/bin/repl.js +++ b/bin/repl.js @@ -14,6 +14,7 @@ import { printOccurrences, printServices, printTable, + printVulnerabilities, } from "../display.js"; import { createBom } from "../index.js"; import { validateBom } from "../validator.js"; @@ -61,9 +62,13 @@ export const importSbom = (sbomOrPath) => { if (sbomOrPath?.endsWith(".json") && fs.existsSync(sbomOrPath)) { try { sbom = JSON.parse(fs.readFileSync(sbomOrPath, "utf-8")); - console.log(`✅ SBOM imported successfully from ${sbomOrPath}`); + let bomType = "SBOM"; + if (sbom?.vulnerabilities && Array.isArray(sbom.vulnerabilities)) { + bomType = "VDR"; + } + console.log(`✅ ${bomType} imported successfully from ${sbomOrPath}`); } catch (e) { - console.log(`⚠ Unable to import the SBOM from ${sbomOrPath} due to ${e}`); + console.log(`⚠ Unable to import the BOM from ${sbomOrPath} due to ${e}`); } } else { console.log(`⚠ ${sbomOrPath} is invalid.`); @@ -72,13 +77,13 @@ export const importSbom = (sbomOrPath) => { // Load any sbom passed from the command line if (process.argv.length > 2) { importSbom(process.argv[process.argv.length - 1]); - console.log("💭 Type .print to view the SBOM as a table"); + console.log("💭 Type .print to view the BOM as a table"); } else if (fs.existsSync("bom.json")) { // If the current directory has a bom.json load it importSbom("bom.json"); } else { console.log("💭 Use .create to create an SBOM for the given path."); - console.log("💭 Use .import to import an existing SBOM."); + console.log("💭 Use .import to import an existing BOM."); console.log("💭 Type .exit or press ctrl+d to close."); } @@ -302,7 +307,7 @@ cdxgenRepl.defineCommand("validate", { if (sbom) { const result = validateBom(sbom); if (result) { - console.log("SBOM is valid!"); + console.log("BOM is valid!"); } } else { console.log( @@ -426,7 +431,7 @@ cdxgenRepl.defineCommand("services", { let services = await expression.evaluate(sbom); if (!services) { console.log( - "No services found. Use evinse command to generate an SBOM with evidence.", + "No services found. Use evinse command to generate a SaaSBOM with evidence.", ); } else { if (!Array.isArray(services)) { @@ -439,12 +444,38 @@ cdxgenRepl.defineCommand("services", { } } else { console.log( - "⚠ No SBOM is loaded. Use .import command to import an evinse SBOM", + "⚠ No SaaSBOM is loaded. Use .import command to import a SaaSBOM", ); } this.displayPrompt(); }, }); +cdxgenRepl.defineCommand("vulnerabilities", { + help: "view vulnerabilities", + async action() { + if (sbom) { + try { + const expression = jsonata("vulnerabilities"); + let vulnerabilities = await expression.evaluate(sbom); + if (!vulnerabilities) { + console.log( + "No vulnerabilities found. Use depscan to generate a VDR file with vulnerabilities.", + ); + } else { + if (!Array.isArray(vulnerabilities)) { + vulnerabilities = [vulnerabilities]; + } + printVulnerabilities(vulnerabilities); + } + } catch (e) { + console.log(e); + } + } else { + console.log("⚠ No BOM is loaded. Use .import command to import a VDR"); + } + this.displayPrompt(); + }, +}); cdxgenRepl.defineCommand("osinfocategories", { help: "view the category names for the OS info from the obom", async action() { diff --git a/display.js b/display.js index 85d42571f..c3e2c8347 100644 --- a/display.js +++ b/display.js @@ -340,3 +340,31 @@ export const printReachables = (sliceArtefacts) => { console.log(table(data, config)); } }; + +export function printVulnerabilities(vulnerabilities) { + if (!vulnerabilities) { + return; + } + const data = [["Ref", "Ratings", "State", "Justification"]]; + for (const avuln of vulnerabilities) { + const arow = [ + avuln["bom-ref"], + `${avuln?.ratings + .map((r) => r?.severity?.toUpperCase()) + .join("\n")}\n${avuln?.ratings.map((r) => r?.score).join("\n")}`, + avuln?.analysis?.state || "", + avuln?.analysis?.justification || "", + ]; + data.push(arow); + } + const config = { + header: { + alignment: "center", + content: "Vulnerabilities\nGenerated with \u2665 by cdxgen", + }, + }; + if (data.length > 1) { + console.log(table(data, config)); + } + console.log(`${vulnerabilities.length} vulnerabilities found.`); +} diff --git a/types/display.d.ts b/types/display.d.ts index e7d27e0ff..463d2da3a 100644 --- a/types/display.d.ts +++ b/types/display.d.ts @@ -1,3 +1,4 @@ +export function printVulnerabilities(vulnerabilities: any): void; export function printTable(bomJson: any, filterTypes?: any): void; export function printOSTable(bomJson: any): void; export function printServices(bomJson: any): void; diff --git a/types/display.d.ts.map b/types/display.d.ts.map index d95889d89..d5235c4c5 100644 --- a/types/display.d.ts.map +++ b/types/display.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../display.js"],"names":[],"mappings":"AAcO,kEA+DN;AAQM,iDAkBN;AACM,kDAsBN;AAeM,qDA4BN;AACM,mDA8CN;AACM,uEAiCN;AA4DM,2DA+BN"} \ No newline at end of file +{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../display.js"],"names":[],"mappings":"AAuVA,iEA0BC;AAnWM,kEA+DN;AAQM,iDAkBN;AACM,kDAsBN;AAeM,qDA4BN;AACM,mDA8CN;AACM,uEAiCN;AA4DM,2DA+BN"} \ No newline at end of file