@cdxgen/cdxgen 13.1.0 → 13.3.0
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 +32 -15
- package/bin/cdxgen.js +243 -122
- package/bin/evinse.js +7 -0
- package/bin/sign.js +88 -14
- package/bin/tracebom.js +64 -4
- package/bin/validate.js +29 -7
- package/bin/verify.js +79 -39
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +47 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +117 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +235 -5
- package/lib/cli/jsBom.js +108 -26
- package/lib/cli/jvmBom.js +68 -12
- package/lib/cli/managedBom.js +264 -41
- package/lib/cli/nativeBom.js +923 -15
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +44 -7
- package/lib/core/fs.js +174 -6
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/bunutils.js +269 -101
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +447 -7
- package/lib/ecosystems/cmakeResolver.js +66 -0
- package/lib/ecosystems/cppEvidence.js +104 -33
- package/lib/ecosystems/ecosystems.js +260 -99
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +185 -9
- package/lib/ecosystems/jsEvidence.js +19 -20
- package/lib/ecosystems/kosi.js +1263 -0
- package/lib/ecosystems/npmutils.js +110 -9
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +120 -49
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-go.js +47 -9
- package/lib/ecosystems/parsers-js.js +171 -50
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +919 -161
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +27 -15
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +954 -33
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/rubyutils.js +1426 -166
- package/lib/ecosystems/rusi.js +158 -7
- package/lib/evinser/evinser.js +547 -129
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/bomSigner.js +652 -221
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/atomUtils.js +568 -31
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +110 -14
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +627 -13
- package/lib/inventory/dynamic.js +39 -1
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +26 -5
- package/lib/inventory/fetchBatch.js +190 -19
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +6 -5
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +62 -89
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/source.js +47 -23
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/traceRunner.js +210 -21
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +440 -20
- package/lib/managers/oci.js +39 -1
- package/lib/parsers/cmakeLists.js +452 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/stages/postgen/introspection/markers.js +29 -6
- package/lib/stages/postgen/postgen.js +237 -16
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +48 -10
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +1 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +27 -27
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +2 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +21 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +55 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/bunutils.d.ts +12 -5
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +23 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +40 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +86 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +42 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-go.d.ts +10 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +10 -4
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +143 -2
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +81 -2
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/helpers/bomSigner.d.ts +106 -13
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +145 -3
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +4 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +87 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +34 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/inventory/traceRunner.d.ts +47 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +14 -0
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +76 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +33 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
|
@@ -23,6 +23,12 @@ export const GOLEM_AUDIT_CATEGORIES = Object.freeze([
|
|
|
23
23
|
"golem-compliance",
|
|
24
24
|
]);
|
|
25
25
|
|
|
26
|
+
/** Frozen list of Cargo (Rust) audit categories. */
|
|
27
|
+
export const CARGO_AUDIT_CATEGORIES = Object.freeze([
|
|
28
|
+
"cargo-security",
|
|
29
|
+
"cargo-compliance",
|
|
30
|
+
]);
|
|
31
|
+
|
|
26
32
|
/**
|
|
27
33
|
* Frozen list of audit categories that never activate on their own. These
|
|
28
34
|
* rule packs serve a specific feature (`--introspect` for build-fidelity) and
|
|
@@ -52,10 +58,12 @@ export const BOM_AUDIT_CATEGORY_ALIASES = Object.freeze({
|
|
|
52
58
|
"ai-bom": [...AI_BOM_AUDIT_CATEGORIES, "ai-agent", "mcp-server"],
|
|
53
59
|
"ai-provenance": ["ai-provenance", "ai-oversight"],
|
|
54
60
|
"ai-oversight": ["ai-oversight"],
|
|
61
|
+
cargo: [...CARGO_AUDIT_CATEGORIES],
|
|
55
62
|
cbom: [...CBOM_AUDIT_CATEGORIES],
|
|
56
63
|
"crypto-bom": [...CBOM_AUDIT_CATEGORIES],
|
|
57
64
|
golem: [...GOLEM_AUDIT_CATEGORIES],
|
|
58
65
|
hbom: [...HBOM_AUDIT_CATEGORIES],
|
|
66
|
+
rust: [...CARGO_AUDIT_CATEGORIES],
|
|
59
67
|
host: [...HBOM_AUDIT_CATEGORIES, ...HOST_TOPOLOGY_AUDIT_CATEGORIES],
|
|
60
68
|
});
|
|
61
69
|
|
|
@@ -345,6 +345,36 @@ function normalizeDosaiCryptoNames(cryptoObject) {
|
|
|
345
345
|
names.add("sha-512");
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
|
+
// dosai reports the post-quantum families under their generic names and no
|
|
349
|
+
// generic OID exists for any of them, so the parameter set has to come from
|
|
350
|
+
// the call-site context (dosai's Code field carries the source line, e.g.
|
|
351
|
+
// "MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65)"). Without a parameter set the
|
|
352
|
+
// asset stays unmapped and is skipped rather than emitted with a wrong OID.
|
|
353
|
+
if (/^ML-?DSA$/i.test(cleanName)) {
|
|
354
|
+
const parameterSet = context.match(/(?:MLDsa|ML-DSA-)(44|65|87)\b/i);
|
|
355
|
+
if (parameterSet) {
|
|
356
|
+
names.add(`ml-dsa-${parameterSet[1]}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (/^ML-?KEM$/i.test(cleanName)) {
|
|
360
|
+
const parameterSet = context.match(/(?:MLKem|ML-KEM-)(512|768|1024)\b/i);
|
|
361
|
+
if (parameterSet) {
|
|
362
|
+
names.add(`ml-kem-${parameterSet[1]}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (/^SLH-?DSA$/i.test(cleanName)) {
|
|
366
|
+
// .NET spells it SlhDsaSha2_128s; NIST SLH-DSA-SHA2-128s. The hash family
|
|
367
|
+
// is required: SHA2 and SHAKE have distinct OIDs, so a context naming only
|
|
368
|
+
// the size, such as SLH-DSA-192f, resolves to no single parameter set.
|
|
369
|
+
const parameterSet = context.match(
|
|
370
|
+
/(?:SlhDsa|SLH-DSA-)(SHA2|SHAKE)[-_]?(128|192|256)(s|f)\b/i,
|
|
371
|
+
);
|
|
372
|
+
if (parameterSet) {
|
|
373
|
+
names.add(
|
|
374
|
+
`slh-dsa-${parameterSet[1].toLowerCase()}-${parameterSet[2]}${parameterSet[3].toLowerCase()}`,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
348
378
|
return Array.from(names);
|
|
349
379
|
}
|
|
350
380
|
|
package/lib/inventory/cdxrs.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module is the ONLY place that spawns the `cdxrs` binary. It enforces
|
|
5
5
|
* the governing invariant: **no failure mode may abort SBOM generation.**
|
|
6
|
-
* Every failure (missing binary, non-zero exit, timeout,
|
|
7
|
-
* version-major mismatch, CDXGEN_RS_DISABLE) logs once at
|
|
8
|
-
* a sentinel that makes the caller take the JS path.
|
|
6
|
+
* Every failure (missing binary, non-zero exit, timeout, oversized stdout,
|
|
7
|
+
* malformed stdout, version-major mismatch, CDXGEN_RS_DISABLE) logs once at
|
|
8
|
+
* `warn` and returns a sentinel that makes the caller take the JS path.
|
|
9
9
|
*
|
|
10
10
|
* Protocol:
|
|
11
11
|
* cdxrs <subcommand> --input <file|-> --output <file|-> --format json
|
|
@@ -25,8 +25,13 @@ import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
|
|
|
25
25
|
import { safeExistsSync } from "../core/fs.js";
|
|
26
26
|
import { getDefaultPluginRuntime, resolvePluginBinary } from "./plugins.js";
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Major version this bridge expects from the cdxrs binary. cdxgen-plugins-bin
|
|
30
|
+
* versions every tool in lockstep, so this moves with
|
|
31
|
+
* `@cdxgen/cdxgen-plugins-bin` in package.json even when cdxrs itself carries
|
|
32
|
+
* no breaking change (4.0.0 is 3.2.0 renumbered by the release).
|
|
33
|
+
*/
|
|
34
|
+
const CDXRS_VERSION_MAJOR = "4";
|
|
30
35
|
|
|
31
36
|
/** Default timeout for a cdxrs invocation. */
|
|
32
37
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -37,6 +42,45 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
37
42
|
*/
|
|
38
43
|
const MAX_STDIN_BYTES = 32 * 1024 * 1024;
|
|
39
44
|
|
|
45
|
+
/**
|
|
46
|
+
* V8's maximum string length on 64-bit builds (2^29 − 24). Node exposes no
|
|
47
|
+
* API for it, so it is stated exactly as the runtime's own ERR_STRING_TOO_LONG
|
|
48
|
+
* message does. `Buffer#toString()` throws above it — the crash from issue
|
|
49
|
+
* 4393, where a pnpm workspace's registry fetch crossed the ceiling.
|
|
50
|
+
*/
|
|
51
|
+
const V8_MAX_STRING_CHARS = 0x1fffffe8;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Ceiling on the stdout bytes collected from a cdxrs child. stdout must end up
|
|
55
|
+
* as a single JS string, so a payload above V8's string limit can never be
|
|
56
|
+
* delivered no matter how long the child runs: buffering it only burns memory
|
|
57
|
+
* until the concat throws. Past the ceiling the child is killed and the caller
|
|
58
|
+
* takes the JS path. Counting bytes (never smaller than the decoded character
|
|
59
|
+
* count) keeps the boundary safe for multibyte output.
|
|
60
|
+
*/
|
|
61
|
+
const DEFAULT_MAX_STDOUT_BYTES = V8_MAX_STRING_CHARS;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the stdout ceiling for a run. CDXGEN_RS_MAX_STDOUT_BYTES lowers (or
|
|
65
|
+
* raises) it — used by tests and by hosts that want the failover earlier.
|
|
66
|
+
*
|
|
67
|
+
* @returns {number} Maximum stdout bytes to collect before failing over.
|
|
68
|
+
*/
|
|
69
|
+
function maxStdoutBytes() {
|
|
70
|
+
const raw = readEnvironmentVariable("CDXGEN_RS_MAX_STDOUT_BYTES");
|
|
71
|
+
if (!raw) {
|
|
72
|
+
return DEFAULT_MAX_STDOUT_BYTES;
|
|
73
|
+
}
|
|
74
|
+
const parsed = Number.parseInt(raw, 10);
|
|
75
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
console.warn(
|
|
79
|
+
`cdxrs: ignoring malformed CDXGEN_RS_MAX_STDOUT_BYTES "${raw}"; using the default ${DEFAULT_MAX_STDOUT_BYTES} bytes.`,
|
|
80
|
+
);
|
|
81
|
+
return DEFAULT_MAX_STDOUT_BYTES;
|
|
82
|
+
}
|
|
83
|
+
|
|
40
84
|
/** Sentinel returned when the Rust path is unavailable. Callers take JS path. */
|
|
41
85
|
export const CDXRS_FALLBACK = Object.freeze({
|
|
42
86
|
ok: false,
|
|
@@ -159,6 +203,11 @@ function probeVersion(binPath) {
|
|
|
159
203
|
*
|
|
160
204
|
* Passing `content` is the normal case, since cdxgen holds BOMs in memory.
|
|
161
205
|
*
|
|
206
|
+
* Collected stdout is bounded: it must become a single JS string, so a child
|
|
207
|
+
* that would exceed V8's string limit (by default; see
|
|
208
|
+
* CDXGEN_RS_MAX_STDOUT_BYTES) is killed and reported as a fallback instead of
|
|
209
|
+
* crashing the process at the concat.
|
|
210
|
+
*
|
|
162
211
|
* @param {string} subcommand The subcommand to run (e.g. "info").
|
|
163
212
|
* @param {Object} opts
|
|
164
213
|
* @param {string|Buffer} [opts.content] BOM data to feed over stdin.
|
|
@@ -279,6 +328,9 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
279
328
|
let timedOut = false;
|
|
280
329
|
const stdoutChunks = [];
|
|
281
330
|
const stderrChunks = [];
|
|
331
|
+
const stdoutLimit = maxStdoutBytes();
|
|
332
|
+
let stdoutBytes = 0;
|
|
333
|
+
let stdoutOverflow = false;
|
|
282
334
|
|
|
283
335
|
const pluginRuntime = getDefaultPluginRuntime();
|
|
284
336
|
const env = { ...process.env };
|
|
@@ -308,6 +360,17 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
308
360
|
child.stdin.end(stdinContent);
|
|
309
361
|
}
|
|
310
362
|
|
|
363
|
+
// Kill the process group (-pid) so any child processes of cdxrs die too.
|
|
364
|
+
// Best effort: the process may have already exited, in which case the
|
|
365
|
+
// close/error handlers take over.
|
|
366
|
+
const killProcessGroup = () => {
|
|
367
|
+
try {
|
|
368
|
+
process.kill(-child.pid, "SIGKILL");
|
|
369
|
+
} catch {
|
|
370
|
+
child.kill("SIGKILL");
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
311
374
|
const timer = setTimeout(() => {
|
|
312
375
|
timedOut = true;
|
|
313
376
|
if (DEBUG_MODE) {
|
|
@@ -315,16 +378,21 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
315
378
|
`cdxrs: timeout after ${timeoutMs}ms, killing process group.`,
|
|
316
379
|
);
|
|
317
380
|
}
|
|
318
|
-
|
|
319
|
-
// Kill the process group (-pid) so any child processes of cdxrs die too.
|
|
320
|
-
process.kill(-child.pid, "SIGKILL");
|
|
321
|
-
} catch {
|
|
322
|
-
// Process may have already exited; fall through to error handling.
|
|
323
|
-
child.kill("SIGKILL");
|
|
324
|
-
}
|
|
381
|
+
killProcessGroup();
|
|
325
382
|
}, timeoutMs);
|
|
326
383
|
|
|
327
384
|
child.stdout?.on("data", (chunk) => {
|
|
385
|
+
if (stdoutOverflow) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
stdoutBytes += chunk.length;
|
|
389
|
+
if (stdoutBytes > stdoutLimit) {
|
|
390
|
+
// The payload can never become a deliverable string, so stop
|
|
391
|
+
// buffering, kill the child, and let the close handler fall back.
|
|
392
|
+
stdoutOverflow = true;
|
|
393
|
+
killProcessGroup();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
328
396
|
stdoutChunks.push(chunk);
|
|
329
397
|
});
|
|
330
398
|
|
|
@@ -360,8 +428,36 @@ export async function runCdxrs(subcommand, opts = {}) {
|
|
|
360
428
|
return;
|
|
361
429
|
}
|
|
362
430
|
|
|
363
|
-
|
|
364
|
-
|
|
431
|
+
// Checked before the exit code: the child was killed mid-stream for
|
|
432
|
+
// producing more stdout than can ever be delivered as a string, so even
|
|
433
|
+
// an exit-0 result is an undeliverable truncation (issue 4393).
|
|
434
|
+
if (stdoutOverflow) {
|
|
435
|
+
console.warn(
|
|
436
|
+
`cdxrs: stdout passed ${stdoutLimit} bytes (a string cannot exceed ${V8_MAX_STRING_CHARS} characters), so the child was stopped.`,
|
|
437
|
+
);
|
|
438
|
+
resolve({ ...CDXRS_FALLBACK, reason: "stdout-too-large" });
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
let stdout;
|
|
443
|
+
try {
|
|
444
|
+
stdout = Buffer.concat(stdoutChunks).toString();
|
|
445
|
+
} catch (err) {
|
|
446
|
+
// The byte cap above should make this unreachable; it is kept so that
|
|
447
|
+
// no failure inside this handler can abort SBOM generation.
|
|
448
|
+
console.warn(
|
|
449
|
+
`cdxrs: could not collect child stdout (${err.message}), falling back to JS path.`,
|
|
450
|
+
);
|
|
451
|
+
resolve({ ...CDXRS_FALLBACK, reason: "stdout-collect-failed" });
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
let stderr = "";
|
|
455
|
+
try {
|
|
456
|
+
stderr = Buffer.concat(stderrChunks).toString();
|
|
457
|
+
} catch {
|
|
458
|
+
// stderr carries NDJSON logs, already forwarded as they arrived;
|
|
459
|
+
// dropping the raw tail must not fail a run with valid stdout.
|
|
460
|
+
}
|
|
365
461
|
|
|
366
462
|
// For the validate subcommand, exit code 3 means "validation failure
|
|
367
463
|
// with findings on stdout" — the operation succeeded, the BOM just has
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { basename, dirname, extname } from "node:path";
|
|
2
|
+
import { basename, dirname, extname, join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { parse as loadYaml } from "yaml";
|
|
5
5
|
|
|
6
|
+
import { safeExistsSync } from "../core/fs.js";
|
|
6
7
|
import { sanitizeBomPropertyValue } from "../core/propertySanitizer.js";
|
|
7
8
|
import { parseJsonLike } from "./jsonLike.js";
|
|
8
9
|
import {
|
|
@@ -28,6 +29,18 @@ const COMMUNITY_AI_PATTERNS = [
|
|
|
28
29
|
"**/.claude/skills/*/SKILL.md",
|
|
29
30
|
".agents/skills/*/SKILL.md",
|
|
30
31
|
"**/.agents/skills/*/SKILL.md",
|
|
32
|
+
".codex/skills/*/SKILL.md",
|
|
33
|
+
"**/.codex/skills/*/SKILL.md",
|
|
34
|
+
"skills/*/SKILL.md",
|
|
35
|
+
"**/skills/*/SKILL.md",
|
|
36
|
+
".claude-plugin/plugin.json",
|
|
37
|
+
"**/.claude-plugin/plugin.json",
|
|
38
|
+
".claude-plugin/marketplace.json",
|
|
39
|
+
"**/.claude-plugin/marketplace.json",
|
|
40
|
+
".claude/agents/*.md",
|
|
41
|
+
"**/.claude/agents/*.md",
|
|
42
|
+
".claude/commands/**/*.md",
|
|
43
|
+
"**/.claude/commands/**/*.md",
|
|
31
44
|
".cursor/rules/*.{md,mdc}",
|
|
32
45
|
"**/.cursor/rules/*.{md,mdc}",
|
|
33
46
|
".windsurf/rules/**/*.md",
|
|
@@ -145,9 +158,24 @@ function frameworkForFile(filePath, raw = "") {
|
|
|
145
158
|
if (lowerPath.includes("/.claude/skills/")) {
|
|
146
159
|
return "claude-compatible";
|
|
147
160
|
}
|
|
161
|
+
if (lowerPath.includes("/.claude-plugin/")) {
|
|
162
|
+
return "claude-plugin";
|
|
163
|
+
}
|
|
164
|
+
if (lowerPath.includes("/.claude/agents/")) {
|
|
165
|
+
return "claude-subagent";
|
|
166
|
+
}
|
|
167
|
+
if (lowerPath.includes("/.claude/commands/")) {
|
|
168
|
+
return "claude-command";
|
|
169
|
+
}
|
|
170
|
+
if (lowerPath.includes("/.codex/skills/")) {
|
|
171
|
+
return "codex";
|
|
172
|
+
}
|
|
148
173
|
if (lowerPath.includes("/.agents/skills/")) {
|
|
149
174
|
return "agent-compatible";
|
|
150
175
|
}
|
|
176
|
+
if (/(?:^|\/)skills\/[^/]+\/skill\.md$/u.test(lowerPath)) {
|
|
177
|
+
return "agent-skills";
|
|
178
|
+
}
|
|
151
179
|
return "community-ai";
|
|
152
180
|
}
|
|
153
181
|
|
|
@@ -250,6 +278,173 @@ function parseSkillFile(filePath, raw) {
|
|
|
250
278
|
metadata.metadata,
|
|
251
279
|
);
|
|
252
280
|
}
|
|
281
|
+
// Record whether the frontmatter satisfies the open Agent Skills constraints
|
|
282
|
+
// (name: 1-64 lowercase-alnum-hyphen; description: 1-1024 chars) as a single
|
|
283
|
+
// reviewer-friendly boolean rather than copying the raw values twice.
|
|
284
|
+
const specValid =
|
|
285
|
+
typeof metadata?.name === "string" &&
|
|
286
|
+
/^[a-z0-9-]{1,64}$/u.test(metadata.name) &&
|
|
287
|
+
typeof metadata?.description === "string" &&
|
|
288
|
+
metadata.description.length >= 1 &&
|
|
289
|
+
metadata.description.length <= 1024;
|
|
290
|
+
addUniqueProperty(
|
|
291
|
+
component.properties,
|
|
292
|
+
"cdx:skill:specValid",
|
|
293
|
+
specValid ? "true" : "false",
|
|
294
|
+
);
|
|
295
|
+
const skillDir = dirname(filePath);
|
|
296
|
+
for (const [subdir, property] of [
|
|
297
|
+
["scripts", "cdx:skill:hasScripts"],
|
|
298
|
+
["references", "cdx:skill:hasReferences"],
|
|
299
|
+
["assets", "cdx:skill:hasAssets"],
|
|
300
|
+
]) {
|
|
301
|
+
if (safeExistsSync(join(skillDir, subdir))) {
|
|
302
|
+
addUniqueProperty(component.properties, property, "true");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
maybeAddFileSignals(component.properties, filePath, raw);
|
|
306
|
+
return [component];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Parse a Claude Code plugin manifest (`.claude-plugin/plugin.json`) into a
|
|
311
|
+
* single application component describing the plugin.
|
|
312
|
+
*
|
|
313
|
+
* @param {string} filePath Manifest path
|
|
314
|
+
* @param {string} raw File contents
|
|
315
|
+
* @returns {Object[]} Component list (one plugin component or empty)
|
|
316
|
+
*/
|
|
317
|
+
function parsePluginManifest(filePath, raw) {
|
|
318
|
+
let manifest;
|
|
319
|
+
try {
|
|
320
|
+
manifest = parseJsonLike(raw);
|
|
321
|
+
} catch {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
if (!manifest?.name) {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
const component = createComponent(
|
|
328
|
+
filePath,
|
|
329
|
+
"claude-plugin",
|
|
330
|
+
"agent-plugin",
|
|
331
|
+
manifest.name,
|
|
332
|
+
);
|
|
333
|
+
if (manifest.version) {
|
|
334
|
+
component.version = String(manifest.version);
|
|
335
|
+
}
|
|
336
|
+
addUniqueProperty(
|
|
337
|
+
component.properties,
|
|
338
|
+
"cdx:plugin:description",
|
|
339
|
+
manifest.description,
|
|
340
|
+
);
|
|
341
|
+
addUniqueProperty(
|
|
342
|
+
component.properties,
|
|
343
|
+
"cdx:plugin:author",
|
|
344
|
+
typeof manifest.author === "string"
|
|
345
|
+
? manifest.author
|
|
346
|
+
: manifest.author?.name,
|
|
347
|
+
);
|
|
348
|
+
for (const [key, property] of [
|
|
349
|
+
["commands", "cdx:plugin:commandCount"],
|
|
350
|
+
["agents", "cdx:plugin:agentCount"],
|
|
351
|
+
["skills", "cdx:plugin:skillCount"],
|
|
352
|
+
["hooks", "cdx:plugin:hookCount"],
|
|
353
|
+
["mcpServers", "cdx:plugin:mcpServerCount"],
|
|
354
|
+
]) {
|
|
355
|
+
const value = manifest[key];
|
|
356
|
+
if (Array.isArray(value)) {
|
|
357
|
+
addUniqueProperty(component.properties, property, String(value.length));
|
|
358
|
+
} else if (value && typeof value === "object") {
|
|
359
|
+
addUniqueProperty(
|
|
360
|
+
component.properties,
|
|
361
|
+
property,
|
|
362
|
+
String(Object.keys(value).length),
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
maybeAddFileSignals(component.properties, filePath, raw);
|
|
367
|
+
return [component];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Parse a Claude Code plugin marketplace file
|
|
372
|
+
* (`.claude-plugin/marketplace.json`) into one component per listed plugin.
|
|
373
|
+
*
|
|
374
|
+
* @param {string} filePath Marketplace path
|
|
375
|
+
* @param {string} raw File contents
|
|
376
|
+
* @returns {Object[]} Component list
|
|
377
|
+
*/
|
|
378
|
+
function parsePluginMarketplace(filePath, raw) {
|
|
379
|
+
let marketplace;
|
|
380
|
+
try {
|
|
381
|
+
marketplace = parseJsonLike(raw);
|
|
382
|
+
} catch {
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
385
|
+
const plugins = Array.isArray(marketplace?.plugins)
|
|
386
|
+
? marketplace.plugins
|
|
387
|
+
: [];
|
|
388
|
+
const components = [];
|
|
389
|
+
for (const plugin of plugins) {
|
|
390
|
+
const name = plugin?.name || plugin?.source;
|
|
391
|
+
if (!name) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const component = createComponent(
|
|
395
|
+
filePath,
|
|
396
|
+
"claude-plugin",
|
|
397
|
+
"agent-plugin-listing",
|
|
398
|
+
String(name),
|
|
399
|
+
);
|
|
400
|
+
addUniqueProperty(
|
|
401
|
+
component.properties,
|
|
402
|
+
"cdx:plugin:description",
|
|
403
|
+
plugin.description,
|
|
404
|
+
);
|
|
405
|
+
addUniqueProperty(
|
|
406
|
+
component.properties,
|
|
407
|
+
"cdx:plugin:source",
|
|
408
|
+
typeof plugin.source === "string" ? plugin.source : undefined,
|
|
409
|
+
);
|
|
410
|
+
components.push(component);
|
|
411
|
+
}
|
|
412
|
+
return components;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Parse a Claude Code subagent definition (`.claude/agents/*.md`) into an
|
|
417
|
+
* agent-definition component using its markdown frontmatter.
|
|
418
|
+
*
|
|
419
|
+
* @param {string} filePath Subagent markdown path
|
|
420
|
+
* @param {string} raw File contents
|
|
421
|
+
* @returns {Object[]} Component list
|
|
422
|
+
*/
|
|
423
|
+
function parseClaudeSubagent(filePath, raw) {
|
|
424
|
+
const { metadata } = parseFrontmatter(raw);
|
|
425
|
+
const name = metadata?.name || basename(filePath, extname(filePath));
|
|
426
|
+
const component = createComponent(
|
|
427
|
+
filePath,
|
|
428
|
+
"claude-subagent",
|
|
429
|
+
"agent-definition",
|
|
430
|
+
name,
|
|
431
|
+
);
|
|
432
|
+
addUniqueProperty(component.properties, "cdx:agent:role", "claude-subagent");
|
|
433
|
+
addUniqueProperty(
|
|
434
|
+
component.properties,
|
|
435
|
+
"cdx:agent:description",
|
|
436
|
+
metadata?.description,
|
|
437
|
+
);
|
|
438
|
+
addUniqueProperty(component.properties, "cdx:agent:model", metadata?.model);
|
|
439
|
+
if (Array.isArray(metadata?.tools)) {
|
|
440
|
+
addUniqueProperty(
|
|
441
|
+
component.properties,
|
|
442
|
+
"cdx:agent:tools",
|
|
443
|
+
metadata.tools.join(","),
|
|
444
|
+
);
|
|
445
|
+
} else if (typeof metadata?.tools === "string") {
|
|
446
|
+
addUniqueProperty(component.properties, "cdx:agent:tools", metadata.tools);
|
|
447
|
+
}
|
|
253
448
|
maybeAddFileSignals(component.properties, filePath, raw);
|
|
254
449
|
return [component];
|
|
255
450
|
}
|
|
@@ -708,10 +903,28 @@ export const communityAiConfigParser = {
|
|
|
708
903
|
components.push(...parseAgentMarkdown(filePath, raw));
|
|
709
904
|
continue;
|
|
710
905
|
}
|
|
906
|
+
if (lowerPath.endsWith("/.claude-plugin/plugin.json")) {
|
|
907
|
+
components.push(...parsePluginManifest(filePath, raw));
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (lowerPath.endsWith("/.claude-plugin/marketplace.json")) {
|
|
911
|
+
components.push(...parsePluginMarketplace(filePath, raw));
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (lowerPath.includes("/.claude/agents/")) {
|
|
915
|
+
components.push(...parseClaudeSubagent(filePath, raw));
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
if (lowerPath.includes("/.claude/commands/")) {
|
|
919
|
+
components.push(...parseInstructionMarkdown(filePath, raw));
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
711
922
|
if (
|
|
712
923
|
lowerPath.includes("/.opencode/skills/") ||
|
|
713
924
|
lowerPath.includes("/.claude/skills/") ||
|
|
714
|
-
lowerPath.includes("/.agents/skills/")
|
|
925
|
+
lowerPath.includes("/.agents/skills/") ||
|
|
926
|
+
lowerPath.includes("/.codex/skills/") ||
|
|
927
|
+
lowerPath.endsWith("/skill.md")
|
|
715
928
|
) {
|
|
716
929
|
components.push(...parseSkillFile(filePath, raw));
|
|
717
930
|
continue;
|