cbom-tools 0.2.5 → 0.2.7-rc.1

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.
Files changed (3) hide show
  1. package/README.md +17 -2
  2. package/cbom-diff.js +32 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -77,7 +77,22 @@ Exit codes:
77
77
  - `0` — no changes
78
78
  - `1` — informational changes (algorithm properties moved)
79
79
  - `2` — warnings (algorithm removed, new classical reference), or file/parse error
80
- - `3` — (reserved) quantum-vulnerable/weak algorithm detection
80
+ - `3` — a quantum-vulnerable algorithm was newly introduced (requires both CBOMs)
81
+
82
+ Exit 3 follows the definition agreed in [#43](https://github.com/Lennonhaha/fibemate-tools/issues/43):
83
+ an algorithm whose `fibemate:quantumSecurity:level` is `vulnerable` appears in the
84
+ after CBOM but not in the before CBOM. Two consequences are deliberate:
85
+
86
+ - An algorithm that was already vulnerable and stays vulnerable is not a
87
+ regression, so it never turns the build red.
88
+ - A single-CBOM scan has no baseline to compare against, so it stays at exit 1.
89
+ Otherwise the first scan of a legacy codebase would light up every historical
90
+ finding at once.
91
+
92
+ The level is read from `component.properties` (`fibemate:quantumSecurity:level`),
93
+ which is where `cbom-scan` writes it; `cryptoProperties.quantumSecurity.level` is
94
+ accepted as a fallback for hand-written CBOMs. A missing or malformed level stays
95
+ unknown — it is never inferred from the algorithm name.
81
96
 
82
97
  ## Testing
83
98
 
@@ -85,7 +100,7 @@ Exit codes:
85
100
  npm test
86
101
  ```
87
102
 
88
- 68 tests (Node built-in `node:test`, zero dependencies). Both binaries are
103
+ 76 tests (Node built-in `node:test`, zero dependencies). Both binaries are
89
104
  spawned as child processes and checked against edge inputs — empty directory,
90
105
  dependencies-only, source-only, an 800-file tree (occurrence cap,
91
106
  `node_modules` / `.git` / dotfile skipping), unreachable directories, missing
package/cbom-diff.js CHANGED
@@ -17,7 +17,7 @@
17
17
  * 0 — 无变更
18
18
  * 1 — 有信息性变更(算法属性升级等)
19
19
  * 2 — 有警告性变更(算法删除、新经典算法引用等)
20
- * 3 — (reserved) 量子脆弱/弱算法引入检测(0.2.0 规划)
20
+ * 3 — 加密材料依赖引入了量子脆弱算法(仅在同时提供 before 与 after 时判定)
21
21
  */
22
22
 
23
23
  const { execSync } = require('child_process');
@@ -164,7 +164,7 @@ if (oldCBOM && newCBOM) {
164
164
  ref,
165
165
  name: c.name,
166
166
  type: assetType,
167
- risk: classifyRisk(crypto),
167
+ risk: classifyRisk(crypto, quantumLevel(c)),
168
168
  });
169
169
  }
170
170
  });
@@ -196,8 +196,10 @@ if (oldCBOM && newCBOM) {
196
196
  ref: c['bom-ref'],
197
197
  name: c.name,
198
198
  type: (c.cryptoProperties || {}).assetType || c.type,
199
- risk: 'info',
200
- }));
199
+ // No baseline: every component is "present", but none is "newly
200
+ // introduced", so the exit-3 regression signal stays off here.
201
+ risk: 'info',
202
+ }));
201
203
  }
202
204
 
203
205
  // --- 依赖关系比较 ---
@@ -226,6 +228,8 @@ if (oldCBOM && newCBOM) {
226
228
  }
227
229
 
228
230
  // --- 严重性判定 ---
231
+ // Exit 3 fires only when an added component is quantum-vulnerable and a
232
+ // baseline exists to compare against (see the single-CBOM branch).
229
233
  const hasCriticalAdd = diff.components.added.some(c => c.risk === 'critical');
230
234
  const hasWarningAdd = diff.components.added.some(c => c.risk === 'warning');
231
235
  const hasRemoval = diff.components.removed.length > 0;
@@ -249,7 +253,30 @@ if (hasCriticalAdd) {
249
253
  // ════════════════════════════
250
254
  // 算法风险分类
251
255
  // ════════════════════════════
252
- function classifyRisk(cryptoProps) {
256
+ // Quantum level lives in `component.properties` as a name/value pair:
257
+ // cbom-scan emits `fibemate:quantumSecurity:level`. Hand-written CBOMs may
258
+ // instead put it under `cryptoProperties.quantumSecurity.level`, so accept
259
+ // both, properties first. Never guess a level from the algorithm name: an
260
+ // unknown level must stay unknown, otherwise exit 3 becomes a name-match.
261
+ function quantumLevel(component) {
262
+ const props = component && Array.isArray(component.properties) ? component.properties : [];
263
+ // Literal, not a module-level const: the diff runs at the top of this script,
264
+ // long before this line is evaluated, so a module-level binding would still be
265
+ // in its temporal dead zone when quantumLevel() is first called.
266
+ const fromProps = props.find((entry) => entry && entry.name === 'fibemate:quantumSecurity:level');
267
+ if (fromProps && typeof fromProps.value === 'string') return fromProps.value;
268
+ const cryptoProps = (component && component.cryptoProperties) || {};
269
+ const fromCrypto = cryptoProps.quantumSecurity && cryptoProps.quantumSecurity.level;
270
+ return typeof fromCrypto === 'string' ? fromCrypto : null;
271
+ }
272
+
273
+ // Risk of a component that is *added* by this diff. Definition 2 from #43:
274
+ // exit 3 means "a quantum-vulnerable algorithm was newly introduced", and the
275
+ // caller only classifies added components, so "present here and absent before"
276
+ // is already established by the time we get here. A vulnerable algorithm that
277
+ // merely stays present is not a regression and must not turn the build red.
278
+ function classifyRisk(cryptoProps, quantum) {
279
+ if (quantum === 'vulnerable') return 'critical';
253
280
  const type = ((cryptoProps && cryptoProps.assetType) || '').toLowerCase();
254
281
  // PQC algorithms: safe additions
255
282
  if (/kem|sign|signature|post.quantum|pqc|mlkem|mldsa|slh/i.test(type)) return 'info';
@@ -259,8 +286,6 @@ function classifyRisk(cryptoProps) {
259
286
  if (/ecc|ecdh|p-256|sm2|curve/i.test(type)) return 'warning';
260
287
  // Classic symmetric: info (still safe with large keys)
261
288
  if (/sym|sm4|aes|gcm/i.test(type)) return 'info';
262
- // TODO(v0.2.0): add 'critical' exit code for quantum-vulnerable algorithms
263
- // using cryptoProps.quantumSecurity?.level
264
289
  // Unknown: treat as warning
265
290
  return 'warning';
266
291
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cbom-tools",
3
- "version": "0.2.5",
3
+ "version": "0.2.7-rc.1",
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>",