cbom-tools 0.2.4 → 0.2.5

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 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.6](https://cyclonedx.org/) CBOM, and diff two CBOMs to
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.6 JSON with `cryptoProperties` (asset type,
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
- 17 CLI contract tests (Node built-in `node:test`, zero dependencies):
73
- both binaries are spawned as child processes and checked against edge
74
- inputs — empty directory, dependencies-only, source-only, an 800-file
75
- tree (occurrence cap, `node_modules` / `.git` / dotfile skipping),
76
- unreachable directories, missing argument values, and malformed JSON.
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
- Implements the [CycloneDX 1.6](https://cyclonedx.org/specification/overview/)
128
- CBOM specification, maintained by [OWASP](https://owasp.org/) under
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: '1.6',
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
- const dirFlag = args.indexOf('--dir');
354
- let dir = process.cwd();
355
- if (dirFlag !== -1) {
356
- const v = args[dirFlag + 1];
357
- if (!v || v.startsWith('--')) {
358
- console.error('usage: cbom-scan [--dir <path>] [--out <file>]');
359
- process.exit(2);
360
- }
361
- dir = v;
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>] scan a tree, emit CycloneDX CBOM
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.4",
3
+ "version": "0.2.5",
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"