cbom-tools 0.2.3 → 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,15 +3,27 @@
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
 
13
+ ## What this tool does not do
14
+
15
+ Three things that are easy to misread:
16
+
17
+ - **It does not find vulnerabilities.** No CVE lookup, no taint analysis, no judgement about whether an algorithm choice is sound. It reports *where cryptographic APIs are called*, and says nothing about whether they are called well.
18
+ - **It does not guarantee a complete inventory.** Detection is pattern-based per language and library. A clean scan means "no match against the patterns this version knows" — not "there is no cryptography here". Dynamic loading, generated code, native bindings and vendored binaries can all be missed. The CBOM is evidence of what was found, not a certificate of what exists.
19
+ - **It does not execute the code it scans.** Files are read and parsed. Nothing in the scanned tree is run.
20
+
21
+ A CBOM is an inventory to review. It is not a compliance conclusion, and it cannot replace an audit.
22
+
11
23
  ## Try it without installing
12
24
 
13
25
  ```
14
- npx cbom-tools scan --dir ./my-project --out cbom.json
26
+ npx cbom-tools scan --dir ./my-project --out cbom.json --spec 1.7
15
27
  npx cbom-tools diff cbom-before.json cbom-after.json
16
28
  ```
17
29
 
@@ -26,18 +38,32 @@ npm install -g cbom-tools
26
38
  ### 1. Scan a project
27
39
 
28
40
  ```
29
- npx cbom-tools scan --dir ./my-project --out cbom.json
41
+ npx cbom-tools scan --dir ./my-project --out cbom.json --spec 1.7
30
42
  ```
31
43
 
32
44
  `cbom-scan` is still installed as a standalone alias.
33
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
+
34
59
  Scans:
35
60
  - `package.json` dependencies (declarative rule table)
36
61
  - Source files (`.js/.cjs/.mjs/.ts/.tsx/.jsx`) via regex patterns
37
62
 
38
- Output: a CycloneDX 1.6 JSON with `cryptoProperties` (asset type,
63
+ Output: a CycloneDX 1.7 JSON with `cryptoProperties` (asset type,
39
64
  primitive, quantum security level) and `evidence.occurrences` (file
40
65
  paths + line numbers, capped at 50 per algorithm, deduplicated).
66
+ Pass `--spec 1.6` to emit the previous schema version instead.
41
67
 
42
68
  ### 2. Diff two CBOMs
43
69
 
@@ -59,11 +85,18 @@ Exit codes:
59
85
  npm test
60
86
  ```
61
87
 
62
- 17 CLI contract tests (Node built-in `node:test`, zero dependencies):
63
- both binaries are spawned as child processes and checked against edge
64
- inputs — empty directory, dependencies-only, source-only, an 800-file
65
- tree (occurrence cap, `node_modules` / `.git` / dotfile skipping),
66
- 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.
67
100
 
68
101
  ## Example (real output)
69
102
 
@@ -71,10 +104,10 @@ Scanning a repository before and after a PQC migration:
71
104
 
72
105
  ```
73
106
  $ cbom-scan --dir ./repo-before --out before.json
74
- cbom-scan: wrote 11 algorithms to before.json
107
+ cbom-scan: wrote 11 algorithms to before.json (CycloneDX 1.7)
75
108
 
76
- $ cbom-scan --dir ./repo-after --out after.json
77
- 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)
78
111
 
79
112
  $ cbom-diff before.json after.json
80
113
  ## CBOM Diff
@@ -114,9 +147,9 @@ only — no cryptographic computation is performed.
114
147
 
115
148
  ## Standards
116
149
 
117
- Implements the [CycloneDX 1.6](https://cyclonedx.org/specification/overview/)
118
- CBOM specification, maintained by [OWASP](https://owasp.org/) under
119
- 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.
120
153
 
121
154
  ## License
122
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.3",
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"