cbom-tools 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -16
- package/cbom-scan.cjs +52 -15
- package/cli.cjs +3 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
**Cryptographic Bill of Materials — scan, diff, report.**
|
|
4
4
|
|
|
5
5
|
Scan a directory tree for cryptographic assets, emit a
|
|
6
|
-
[CycloneDX 1.
|
|
7
|
-
detect algorithm additions, removals, and metadata changes.
|
|
6
|
+
[CycloneDX 1.7](https://cyclonedx.org/specification/overview/) CBOM, and diff two
|
|
7
|
+
CBOMs to detect algorithm additions, removals, and metadata changes.
|
|
8
|
+
1.6 output is still available with `--spec 1.6` for consumers that have not
|
|
9
|
+
moved yet.
|
|
8
10
|
|
|
9
11
|
Zero external dependencies. Pure Node.js.
|
|
10
12
|
|
|
@@ -21,7 +23,7 @@ A CBOM is an inventory to review. It is not a compliance conclusion, and it cann
|
|
|
21
23
|
## Try it without installing
|
|
22
24
|
|
|
23
25
|
```
|
|
24
|
-
npx cbom-tools scan --dir ./my-project --out cbom.json
|
|
26
|
+
npx cbom-tools scan --dir ./my-project --out cbom.json --spec 1.7
|
|
25
27
|
npx cbom-tools diff cbom-before.json cbom-after.json
|
|
26
28
|
```
|
|
27
29
|
|
|
@@ -36,18 +38,32 @@ npm install -g cbom-tools
|
|
|
36
38
|
### 1. Scan a project
|
|
37
39
|
|
|
38
40
|
```
|
|
39
|
-
npx cbom-tools scan --dir ./my-project --out cbom.json
|
|
41
|
+
npx cbom-tools scan --dir ./my-project --out cbom.json --spec 1.7
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
`cbom-scan` is still installed as a standalone alias.
|
|
43
45
|
|
|
46
|
+
### Spec version
|
|
47
|
+
|
|
48
|
+
`--spec` accepts `1.7` (default) and `1.6`. Both forms work:
|
|
49
|
+
`--spec 1.7` and `--spec=1.7`. Anything else is a usage error (exit 2)
|
|
50
|
+
rather than a silent fallback — a BOM that claims a version it does not
|
|
51
|
+
conform to is worse than no BOM.
|
|
52
|
+
|
|
53
|
+
The schema is the contract, so CI validates the emitted document against the
|
|
54
|
+
official `bom-1.7.schema.json` (and, for `--spec 1.6`, against
|
|
55
|
+
`bom-1.6.schema.json`). The two differ in the declared version and in which
|
|
56
|
+
documented fields are permitted; the findings themselves are identical, and a
|
|
57
|
+
test asserts exactly that.
|
|
58
|
+
|
|
44
59
|
Scans:
|
|
45
60
|
- `package.json` dependencies (declarative rule table)
|
|
46
61
|
- Source files (`.js/.cjs/.mjs/.ts/.tsx/.jsx`) via regex patterns
|
|
47
62
|
|
|
48
|
-
Output: a CycloneDX 1.
|
|
63
|
+
Output: a CycloneDX 1.7 JSON with `cryptoProperties` (asset type,
|
|
49
64
|
primitive, quantum security level) and `evidence.occurrences` (file
|
|
50
65
|
paths + line numbers, capped at 50 per algorithm, deduplicated).
|
|
66
|
+
Pass `--spec 1.6` to emit the previous schema version instead.
|
|
51
67
|
|
|
52
68
|
### 2. Diff two CBOMs
|
|
53
69
|
|
|
@@ -69,11 +85,18 @@ Exit codes:
|
|
|
69
85
|
npm test
|
|
70
86
|
```
|
|
71
87
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
68 tests (Node built-in `node:test`, zero dependencies). Both binaries are
|
|
89
|
+
spawned as child processes and checked against edge inputs — empty directory,
|
|
90
|
+
dependencies-only, source-only, an 800-file tree (occurrence cap,
|
|
91
|
+
`node_modules` / `.git` / dotfile skipping), unreachable directories, missing
|
|
92
|
+
argument values, and malformed JSON.
|
|
93
|
+
|
|
94
|
+
`test/spec-version.test.cjs` covers the `--spec` contract: the default is
|
|
95
|
+
1.7, both `--spec 1.7` and `--spec=1.7` are parsed, an unknown version exits
|
|
96
|
+
2 with the allowed list, the emitted document validates against the vendored
|
|
97
|
+
official schema for the version it claims, and switching versions does not
|
|
98
|
+
change the findings. Schemas are vendored under `test/fixtures`, so the suite
|
|
99
|
+
runs offline.
|
|
77
100
|
|
|
78
101
|
## Example (real output)
|
|
79
102
|
|
|
@@ -81,10 +104,10 @@ Scanning a repository before and after a PQC migration:
|
|
|
81
104
|
|
|
82
105
|
```
|
|
83
106
|
$ cbom-scan --dir ./repo-before --out before.json
|
|
84
|
-
cbom-scan: wrote 11 algorithms to before.json
|
|
107
|
+
cbom-scan: wrote 11 algorithms to before.json (CycloneDX 1.7)
|
|
85
108
|
|
|
86
|
-
$ cbom-scan --dir ./repo-after --out after.json
|
|
87
|
-
cbom-scan: wrote 13 algorithms to after.json
|
|
109
|
+
$ cbom-scan --dir ./repo-after --out after.json --spec 1.6
|
|
110
|
+
cbom-scan: wrote 13 algorithms to after.json (CycloneDX 1.6)
|
|
88
111
|
|
|
89
112
|
$ cbom-diff before.json after.json
|
|
90
113
|
## CBOM Diff
|
|
@@ -124,9 +147,9 @@ only — no cryptographic computation is performed.
|
|
|
124
147
|
|
|
125
148
|
## Standards
|
|
126
149
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
Apache-2.0.
|
|
150
|
+
Emits the [CycloneDX 1.7](https://cyclonedx.org/specification/overview/) CBOM
|
|
151
|
+
format by default, with 1.6 available via `--spec 1.6`. The specification is
|
|
152
|
+
maintained by [OWASP](https://owasp.org/) under Apache-2.0.
|
|
130
153
|
|
|
131
154
|
## License
|
|
132
155
|
|
package/cbom-scan.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Copyright (c) 2026 刘天赫
|
|
4
4
|
//
|
|
5
5
|
// cbom-scan.cjs — scan a directory tree for cryptographic assets,
|
|
6
|
-
// emit CycloneDX 1.6 CBOM with provenance-aware merge policy.
|
|
6
|
+
// emit a CycloneDX 1.6 or 1.7 CBOM with provenance-aware merge policy.
|
|
7
7
|
|
|
8
8
|
'use strict';
|
|
9
9
|
|
|
@@ -333,10 +333,10 @@ function mergeEvidence(fromDeps, fromSource) {
|
|
|
333
333
|
|
|
334
334
|
// ── Output ──────────────────────────────────────────────────────────
|
|
335
335
|
|
|
336
|
-
function toCycloneDX(components) {
|
|
336
|
+
function toCycloneDX(components, specVersion) {
|
|
337
337
|
return {
|
|
338
338
|
bomFormat: 'CycloneDX',
|
|
339
|
-
specVersion
|
|
339
|
+
specVersion,
|
|
340
340
|
version: 1,
|
|
341
341
|
metadata: {
|
|
342
342
|
timestamp: new Date().toISOString(),
|
|
@@ -348,31 +348,68 @@ function toCycloneDX(components) {
|
|
|
348
348
|
|
|
349
349
|
// ── Entry point ─────────────────────────────────────────────────────
|
|
350
350
|
|
|
351
|
+
const SPEC_VERSIONS = ['1.6', '1.7'];
|
|
352
|
+
const DEFAULT_SPEC = '1.7';
|
|
353
|
+
const USAGE = 'usage: cbom-scan [--dir <path>] [--out <file>] [--spec 1.6|1.7]';
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Read `--flag value` or `--flag=value`.
|
|
357
|
+
*
|
|
358
|
+
* The equals form used to be invisible to the parser: `--dir=./x` left the
|
|
359
|
+
* directory at process.cwd() and scanned the wrong tree without complaining.
|
|
360
|
+
* A flag that is silently ignored is worse than a flag that errors.
|
|
361
|
+
*/
|
|
362
|
+
function readFlag(args, name) {
|
|
363
|
+
const eqPrefix = name + '=';
|
|
364
|
+
let value = null;
|
|
365
|
+
for (let i = 0; i < args.length; i++) {
|
|
366
|
+
if (args[i] === name) {
|
|
367
|
+
const next = args[i + 1];
|
|
368
|
+
if (next === undefined || next.startsWith('--')) return { error: name + ' needs a value' };
|
|
369
|
+
value = next;
|
|
370
|
+
} else if (args[i].startsWith(eqPrefix)) {
|
|
371
|
+
value = args[i].slice(eqPrefix.length);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (value === '') return { error: name + ' needs a value' };
|
|
375
|
+
return { value };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function fail(message) {
|
|
379
|
+
console.error(USAGE);
|
|
380
|
+
console.error(' ' + message);
|
|
381
|
+
process.exit(2);
|
|
382
|
+
}
|
|
383
|
+
|
|
351
384
|
function main() {
|
|
352
385
|
const args = process.argv.slice(2);
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if (dirFlag
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
386
|
+
|
|
387
|
+
const dirFlag = readFlag(args, '--dir');
|
|
388
|
+
if (dirFlag.error) fail(dirFlag.error);
|
|
389
|
+
const dir = dirFlag.value === null ? process.cwd() : dirFlag.value;
|
|
390
|
+
|
|
391
|
+
const outFlag = readFlag(args, '--out');
|
|
392
|
+
if (outFlag.error) fail(outFlag.error);
|
|
393
|
+
const outPath = outFlag.value;
|
|
394
|
+
|
|
395
|
+
const specFlag = readFlag(args, '--spec');
|
|
396
|
+
if (specFlag.error) fail(specFlag.error);
|
|
397
|
+
const specVersion = specFlag.value === null ? DEFAULT_SPEC : specFlag.value;
|
|
398
|
+
if (!SPEC_VERSIONS.includes(specVersion)) {
|
|
399
|
+
fail('--spec must be one of ' + SPEC_VERSIONS.join(', ') + ' (got "' + specVersion + '")');
|
|
362
400
|
}
|
|
363
|
-
const outPath = args.includes('--out') ? args[args.indexOf('--out') + 1] : null;
|
|
364
401
|
|
|
365
402
|
const deps = loadPackageDeps(dir);
|
|
366
403
|
const fromDeps = matchDeps(deps, DEFAULT_RULES);
|
|
367
404
|
const rawSource = scanSource(dir, DEFAULT_RULES);
|
|
368
405
|
const fromSource = deduplicateOccurrences(rawSource);
|
|
369
406
|
const components = mergeEvidence(fromDeps, fromSource);
|
|
370
|
-
const bom = toCycloneDX(components);
|
|
407
|
+
const bom = toCycloneDX(components, specVersion);
|
|
371
408
|
|
|
372
409
|
const output = JSON.stringify(bom, null, 2);
|
|
373
410
|
if (outPath) {
|
|
374
411
|
fs.writeFileSync(outPath, output, 'utf-8');
|
|
375
|
-
console.error(`cbom-scan: wrote ${components.length} algorithms to ${outPath}`);
|
|
412
|
+
console.error(`cbom-scan: wrote ${components.length} algorithms to ${outPath} (CycloneDX ${specVersion})`);
|
|
376
413
|
} else {
|
|
377
414
|
console.log(output);
|
|
378
415
|
}
|
package/cli.cjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* with "could not determine executable to run". A package whose name is not a
|
|
10
10
|
* command is a package nobody tries twice.
|
|
11
11
|
*
|
|
12
|
-
* npx cbom-tools scan --dir ./project --out cbom.json
|
|
12
|
+
* npx cbom-tools scan --dir ./project --out cbom.json --spec 1.7
|
|
13
13
|
* npx cbom-tools diff cbom-before.json cbom-after.json
|
|
14
14
|
*
|
|
15
15
|
* The old bin names still work; this is an addition, not a rename.
|
|
@@ -27,7 +27,8 @@ function usage() {
|
|
|
27
27
|
console.log(`cbom-tools — Cryptographic Bill of Materials
|
|
28
28
|
|
|
29
29
|
Usage:
|
|
30
|
-
cbom-tools scan [--dir <path>] [--out <file>]
|
|
30
|
+
cbom-tools scan [--dir <path>] [--out <file>] [--spec 1.6|1.7]
|
|
31
|
+
scan a tree, emit a CBOM (default 1.7)
|
|
31
32
|
cbom-tools diff <old.json> <new.json> [--json] diff two CBOMs
|
|
32
33
|
cbom-tools --version
|
|
33
34
|
cbom-tools --help
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cbom-tools",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Crypto Bill of Materials — scan, diff, report (CycloneDX 1.6 CBOM)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "刘天赫 <research@fibemate.net>",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"sbom",
|
|
25
25
|
"cryptography",
|
|
26
26
|
"post-quantum",
|
|
27
|
-
"compliance"
|
|
27
|
+
"compliance",
|
|
28
|
+
"cyclonedx-1.7"
|
|
28
29
|
],
|
|
29
30
|
"engines": {
|
|
30
31
|
"node": ">=18"
|