@cdxgen/cdxgen 13.0.1 → 13.2.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 +30 -16
- package/bin/audit.js +6 -0
- package/bin/cdxgen.js +141 -7
- package/bin/evinse.js +3 -1
- package/data/component-tags.json +8 -1
- package/data/crypto-oid.json +88 -12
- package/data/frameworks-list.json +86 -1
- package/data/remediations.json +1167 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/asar-archives.yaml +1 -1
- package/data/rules/build-fidelity.yaml +782 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/data/rules/package-integrity.yaml +13 -13
- package/lib/audit/index.js +48 -4
- package/lib/audit/scoring.js +2 -8
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +32 -1
- package/lib/cli/cliOptions.js +94 -7
- package/lib/cli/index.js +268 -49
- package/lib/cli/jsBom.js +91 -5
- package/lib/cli/jvmBom.js +1515 -1314
- package/lib/cli/managedBom.js +249 -28
- package/lib/cli/nativeBom.js +986 -10
- package/lib/core/activity.js +120 -2
- package/lib/core/buildLedger.js +799 -0
- package/lib/core/deferredExit.js +141 -0
- package/lib/core/env.js +108 -2
- package/lib/core/fs.js +60 -0
- package/lib/core/logger.js +12 -99
- package/lib/core/severity.js +16 -0
- package/lib/core/syncFileWriter.js +106 -0
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +13 -5
- package/lib/ecosystems/cppEvidence.js +40 -2
- package/lib/ecosystems/ecosystems.js +250 -91
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +498 -17
- package/lib/ecosystems/kosi.js +1256 -0
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +69 -39
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-js.js +26 -0
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-jvm.js +9 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +188 -2
- 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 +172 -36
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +944 -19
- 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/pypiClassifiers.js +326 -0
- package/lib/ecosystems/pythonutils.js +49 -1
- package/lib/ecosystems/rubyutils.js +13 -0
- package/lib/ecosystems/rusi.js +126 -6
- package/lib/evinser/evinser.js +141 -13
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/remote/dependency-track.js +81 -14
- 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/auditCategories.js +17 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +7 -2
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +72 -0
- package/lib/inventory/dosai.js +508 -22
- package/lib/inventory/envcontext.js +215 -24
- package/lib/inventory/evidenceUtils.js +13 -0
- package/lib/inventory/fetchBatch.js +131 -2
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +594 -0
- 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/osinfo.js +1 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +54 -88
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/source.js +10 -4
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/toolRequirements.js +903 -0
- package/lib/managers/binary.js +24 -0
- package/lib/managers/docker.js +26 -5
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/server/server.js +1 -1
- package/lib/stages/postgen/annotationBuilder.js +97 -0
- package/lib/stages/postgen/auditBom.js +76 -79
- package/lib/stages/postgen/introspection/annotate.js +320 -0
- package/lib/stages/postgen/introspection/commandFacts.js +293 -0
- package/lib/stages/postgen/introspection/emit.js +192 -0
- package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
- package/lib/stages/postgen/introspection/markers.js +250 -0
- package/lib/stages/postgen/introspection/reflect.js +1214 -0
- package/lib/stages/postgen/introspection/report.js +1208 -0
- package/lib/stages/postgen/introspection/score.js +1067 -0
- package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
- package/lib/stages/postgen/parentEdge.js +156 -0
- package/lib/stages/postgen/postgen.js +284 -3
- package/lib/stages/postgen/ruleEngine.js +17 -0
- package/lib/stages/pregen/envAudit.js +42 -2
- package/lib/stages/pregen/pregen.js +1171 -6
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +11 -2
- package/package.json +26 -26
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/scoring.d.ts +2 -8
- package/types/lib/audit/scoring.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +2 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +10 -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/buildLedger.d.ts +272 -0
- package/types/lib/core/buildLedger.d.ts.map +1 -0
- package/types/lib/core/deferredExit.d.ts +84 -0
- package/types/lib/core/deferredExit.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +43 -1
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +21 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/logger.d.ts +2 -11
- package/types/lib/core/logger.d.ts.map +1 -1
- package/types/lib/core/severity.d.ts +11 -0
- package/types/lib/core/severity.d.ts.map +1 -0
- package/types/lib/core/syncFileWriter.d.ts +33 -0
- package/types/lib/core/syncFileWriter.d.ts.map +1 -0
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- 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.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
- 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/gradleutils.d.ts +53 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +73 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- 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-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-jvm.d.ts.map +1 -1
- 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 +23 -0
- 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/pypiClassifiers.d.ts +80 -0
- package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +21 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +34 -1
- package/types/lib/evinser/evinser.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/remote/dependency-track.d.ts +45 -4
- package/types/lib/helpers/remote/dependency-track.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/auditCategories.d.ts +12 -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/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +43 -0
- package/types/lib/inventory/dosai.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 +43 -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 +190 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
- 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/osinfo.d.ts.map +1 -1
- 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/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/toolRequirements.d.ts +216 -0
- package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.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/annotationBuilder.d.ts +53 -0
- package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
- package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +58 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
- package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
- package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
- package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
- package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +35 -2
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +131 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
|
@@ -23,6 +23,21 @@ 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
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Frozen list of audit categories that never activate on their own. These
|
|
34
|
+
* rule packs serve a specific feature (`--introspect` for build-fidelity) and
|
|
35
|
+
* stay inactive unless the caller explicitly requests the category, so a
|
|
36
|
+
* default `--bom-audit` run - which evaluates every loaded rule - cannot pick
|
|
37
|
+
* them up.
|
|
38
|
+
*/
|
|
39
|
+
export const OPT_IN_BOM_AUDIT_CATEGORIES = Object.freeze(["build-fidelity"]);
|
|
40
|
+
|
|
26
41
|
/** Frozen list of AI-bill-of-materials audit categories. */
|
|
27
42
|
export const AI_BOM_AUDIT_CATEGORIES = Object.freeze([
|
|
28
43
|
"ai-governance",
|
|
@@ -43,10 +58,12 @@ export const BOM_AUDIT_CATEGORY_ALIASES = Object.freeze({
|
|
|
43
58
|
"ai-bom": [...AI_BOM_AUDIT_CATEGORIES, "ai-agent", "mcp-server"],
|
|
44
59
|
"ai-provenance": ["ai-provenance", "ai-oversight"],
|
|
45
60
|
"ai-oversight": ["ai-oversight"],
|
|
61
|
+
cargo: [...CARGO_AUDIT_CATEGORIES],
|
|
46
62
|
cbom: [...CBOM_AUDIT_CATEGORIES],
|
|
47
63
|
"crypto-bom": [...CBOM_AUDIT_CATEGORIES],
|
|
48
64
|
golem: [...GOLEM_AUDIT_CATEGORIES],
|
|
49
65
|
hbom: [...HBOM_AUDIT_CATEGORIES],
|
|
66
|
+
rust: [...CARGO_AUDIT_CATEGORIES],
|
|
50
67
|
host: [...HBOM_AUDIT_CATEGORIES, ...HOST_TOPOLOGY_AUDIT_CATEGORIES],
|
|
51
68
|
});
|
|
52
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
|
@@ -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;
|
|
@@ -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;
|
|
@@ -222,6 +222,28 @@ function serviceIdentityKey(service) {
|
|
|
222
222
|
return `${service?.group || ""}:${service?.name || ""}:${service?.version || ""}`.toLowerCase();
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Union two services[].data[] lists, keyed by flow + classification + name so that repeated
|
|
227
|
+
* classifications of the same payload do not accumulate duplicates.
|
|
228
|
+
*
|
|
229
|
+
* @param {Array} existingData Data entries already on the service
|
|
230
|
+
* @param {Array} newData Data entries being merged in
|
|
231
|
+
* @returns {Array} Merged data entries
|
|
232
|
+
*/
|
|
233
|
+
function mergeServiceData(existingData = [], newData = []) {
|
|
234
|
+
const seen = new Map();
|
|
235
|
+
for (const entry of [...existingData, ...newData]) {
|
|
236
|
+
if (!entry?.flow || !entry?.classification) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const key = `${entry.flow}|${entry.classification}|${entry.name || ""}`;
|
|
240
|
+
if (!seen.has(key)) {
|
|
241
|
+
seen.set(key, entry);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return Array.from(seen.values());
|
|
245
|
+
}
|
|
246
|
+
|
|
225
247
|
function mergeServiceProperties(existingProps = [], newProps = []) {
|
|
226
248
|
const merged = [...existingProps];
|
|
227
249
|
for (const newProp of newProps) {
|
|
@@ -249,6 +271,38 @@ function normalizeServiceEndpoints(endpoints) {
|
|
|
249
271
|
return [];
|
|
250
272
|
}
|
|
251
273
|
|
|
274
|
+
function evidenceOccurrenceKey(occurrence) {
|
|
275
|
+
const location = occurrence?.location || {};
|
|
276
|
+
return `${location.path || ""}#${location.line ?? ""}#${location.column ?? ""}`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Union two service evidence objects by their occurrences so a collision keeps
|
|
281
|
+
* every call site instead of whichever side arrived first.
|
|
282
|
+
*
|
|
283
|
+
* @param {Object} [existingEvidence] Evidence already on the service
|
|
284
|
+
* @param {Object} [newEvidence] Evidence being merged in
|
|
285
|
+
* @returns {Object} Evidence covering both inputs
|
|
286
|
+
*/
|
|
287
|
+
function mergeServiceEvidence(existingEvidence, newEvidence) {
|
|
288
|
+
if (!existingEvidence) {
|
|
289
|
+
return newEvidence;
|
|
290
|
+
}
|
|
291
|
+
const occurrences = [...(existingEvidence.occurrences || [])];
|
|
292
|
+
const seen = new Set(occurrences.map(evidenceOccurrenceKey));
|
|
293
|
+
for (const occurrence of newEvidence?.occurrences || []) {
|
|
294
|
+
const key = evidenceOccurrenceKey(occurrence);
|
|
295
|
+
if (!seen.has(key)) {
|
|
296
|
+
seen.add(key);
|
|
297
|
+
occurrences.push(occurrence);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (!occurrences.length) {
|
|
301
|
+
return existingEvidence;
|
|
302
|
+
}
|
|
303
|
+
return { ...existingEvidence, occurrences };
|
|
304
|
+
}
|
|
305
|
+
|
|
252
306
|
/**
|
|
253
307
|
* Merge CycloneDX services using bom-ref or group/name/version identity.
|
|
254
308
|
*
|
|
@@ -313,6 +367,24 @@ export function mergeServices(services, newServices) {
|
|
|
313
367
|
existing.properties || [],
|
|
314
368
|
service.properties || [],
|
|
315
369
|
);
|
|
370
|
+
// Carry the richer fields through a collision too. Without these, a dosai service that collided
|
|
371
|
+
// with one derived from endpoints lost precisely what schema 4.0.0 adds: its bom-ref, its data
|
|
372
|
+
// classifications, and its evidence.
|
|
373
|
+
existing["bom-ref"] = existing["bom-ref"] || service["bom-ref"];
|
|
374
|
+
if (Array.isArray(service.data) && service.data.length) {
|
|
375
|
+
existing.data = mergeServiceData(existing.data || [], service.data);
|
|
376
|
+
}
|
|
377
|
+
if (service.evidence) {
|
|
378
|
+
existing.evidence = mergeServiceEvidence(
|
|
379
|
+
existing.evidence,
|
|
380
|
+
service.evidence,
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (Array.isArray(service.tags) && service.tags.length) {
|
|
384
|
+
existing.tags = Array.from(
|
|
385
|
+
new Set([...(existing.tags || []), ...service.tags]),
|
|
386
|
+
);
|
|
387
|
+
}
|
|
316
388
|
if (Array.isArray(service.services) && service.services.length) {
|
|
317
389
|
existing.services = mergeServices(
|
|
318
390
|
existing.services || [],
|