-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathsbom.ts
More file actions
77 lines (69 loc) · 2.39 KB
/
Copy pathsbom.ts
File metadata and controls
77 lines (69 loc) · 2.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* SBOM generation via Trivy (PRD §8). CycloneDX or SPDX.
*
* Read-only discipline (PRD §11): by default the SBOM is written to the managed
* dir (~/.codeinspectus/sbom/), NOT into the user's repo. The user can opt into a
* specific location by passing output_path.
*/
import { z } from "zod";
import { mkdir, readFile, stat } from "node:fs/promises";
import { join, basename, dirname } from "node:path";
import { resolve as resolvePath } from "node:path";
import { MANAGED_ROOT } from "./config.js";
import { runTrivySbom } from "./engines/trivy.js";
import type { sbomOutput } from "./schemas.js";
import type { GenerateSbomInput } from "./schemas.js";
type SbomResult = z.infer<typeof sbomOutput>;
function countComponents(json: unknown, format: string): number {
try {
const obj = json as Record<string, unknown>;
if (format === "spdx") {
const pkgs = obj.packages;
return Array.isArray(pkgs) ? pkgs.length : 0;
}
const comps = obj.components;
return Array.isArray(comps) ? comps.length : 0;
} catch {
return 0;
}
}
export async function generateSbom(input: GenerateSbomInput): Promise<SbomResult> {
const format = input.format ?? "cyclonedx";
const target = resolvePath(input.path);
try {
await stat(target);
} catch {
throw new Error(`Path not found: ${target}. Provide an absolute path to an existing project.`);
}
const base = basename(target.replace(/\/$/, "")) || "project";
const defaultOut = join(MANAGED_ROOT, "sbom", `${base}.${format}.json`);
const outputPath = input.output_path ? resolvePath(input.output_path) : defaultOut;
await mkdir(dirname(outputPath), { recursive: true });
const run = await runTrivySbom(target, format, outputPath);
if (!run.ran) {
return {
format,
output_path: outputPath,
component_count: 0,
generated: false,
note:
run.note ??
"SBOM generation failed. Ensure Trivy is installed (`codeinspectus install-engines`).",
};
}
let componentCount = 0;
try {
componentCount = countComponents(JSON.parse(await readFile(outputPath, "utf8")), format);
} catch {
/* keep 0 */
}
return {
format,
output_path: outputPath,
component_count: componentCount,
generated: true,
...(input.output_path
? {}
: { note: `Written to the managed dir (outside your repo) by default. Pass output_path to choose a location.` }),
};
}