UNPKG

@cyclonedx/cdxgen

Version:

Creates CycloneDX Software Bill of Materials (SBOM) from source or container image

194 lines (187 loc) 5.92 kB
#!/usr/bin/env node import fs from "node:fs"; import process from "node:process"; import { findUpSync } from "find-up"; import { parse as _load } from "yaml"; // Evinse (Evinse Verification Is Nearly SBOM Evidence) import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { analyzeProject, createEvinseFile, prepareDB, } from "../lib/evinser/evinser.js"; import { printCallStack, printOccurrences, printReachables, printServices, } from "../lib/helpers/display.js"; import { ATOM_DB } from "../lib/helpers/utils.js"; import { validateBom } from "../lib/helpers/validator.js"; // Support for config files const configPath = findUpSync([ ".cdxgenrc", ".cdxgen.json", ".cdxgen.yml", ".cdxgen.yaml", ]); let config = {}; if (configPath) { try { if (configPath.endsWith(".yml") || configPath.endsWith(".yaml")) { config = _load(fs.readFileSync(configPath, "utf-8")); } else { config = JSON.parse(fs.readFileSync(configPath, "utf-8")); } } catch (e) { console.log("Invalid config file", configPath); } } const args = yargs(hideBin(process.argv)) .env("EVINSE") .option("input", { alias: "i", description: "Input SBOM file. Default bom.json", default: "bom.json", }) .option("output", { alias: "o", description: "Output file. Default bom.evinse.json", default: "bom.evinse.json", }) .option("language", { alias: "l", description: "Application language", default: "java", choices: [ "java", "jar", "js", "ts", "javascript", "nodejs", "py", "python", "android", "c", "cpp", "php", "swift", "ios", "ruby", "scala", ], }) .option("db-path", { description: `Atom slices DB path. Default ${ATOM_DB}`, default: process.env.ATOM_DB || ATOM_DB, }) .option("force", { description: "Force creation of the database", default: false, type: "boolean", }) .option("skip-maven-collector", { description: "Skip collecting jars from maven and gradle caches. Can speedup re-runs if the data was cached previously.", default: false, type: "boolean", }) .option("with-deep-jar-collector", { description: "Enable collection of all jars from maven cache directory. Useful to improve the recall for callstack evidence.", default: false, type: "boolean", }) .option("annotate", { description: "Include contents of atom slices as annotations", default: false, type: "boolean", }) .option("with-data-flow", { description: "Enable inter-procedural data-flow slicing.", default: false, type: "boolean", }) .option("with-reachables", { description: "Enable auto-tagged reachable slicing. Requires SBOM generated with --deep mode.", default: false, type: "boolean", }) .option("usages-slices-file", { description: "Use an existing usages slices file.", default: "usages.slices.json", }) .option("data-flow-slices-file", { description: "Use an existing data-flow slices file.", default: "data-flow.slices.json", }) .option("reachables-slices-file", { description: "Use an existing reachables slices file.", default: "reachables.slices.json", }) .option("semantics-slices-file", { description: "Use an existing semantics slices file.", default: "semantics.slices.json", }) .option("openapi-spec-file", { description: "Use an existing openapi specification file (SaaSBOM).", default: "openapi.json", }) .option("print", { alias: "p", type: "boolean", description: "Print the evidences as table", }) .example([ [ "$0 -i bom.json -o bom.evinse.json -l java .", "Generate a Java SBOM with evidence for the current directory", ], [ "$0 -i bom.json -o bom.evinse.json -l java --with-reachables .", "Generate a Java SBOM with occurrence and reachable evidence for the current directory", ], ]) .completion("completion", "Generate bash/zsh completion") .epilogue("for documentation, visit https://cyclonedx.github.io/cdxgen") .config(config) .scriptName("evinse") .version() .help("h") .alias("h", "help") .wrap(Math.min(120, yargs().terminalWidth())).argv; const evinseArt = ` ███████╗██╗ ██╗██╗███╗ ██╗███████╗███████╗ ██╔════╝██║ ██║██║████╗ ██║██╔════╝██╔════╝ █████╗ ██║ ██║██║██╔██╗ ██║███████╗█████╗ ██╔══╝ ╚██╗ ██╔╝██║██║╚██╗██║╚════██║██╔══╝ ███████╗ ╚████╔╝ ██║██║ ╚████║███████║███████╗ ╚══════╝ ╚═══╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ `; if (process.env?.CDXGEN_NODE_OPTIONS) { process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ""} ${process.env.CDXGEN_NODE_OPTIONS}`; } console.log(evinseArt); (async () => { // First, prepare the database by cataloging jars and other libraries const dbObjMap = await prepareDB(args); if (dbObjMap) { // Analyze the project using atom. Convert package namespaces to purl using the db const sliceArtefacts = await analyzeProject(dbObjMap, args); // Create the SBOM with Evidence const bomJson = createEvinseFile(sliceArtefacts, args); // Validate our final SBOM if (!validateBom(bomJson)) { process.exit(1); } if (args.print) { printOccurrences(bomJson); printCallStack(bomJson); printReachables(sliceArtefacts); printServices(bomJson); } } })();