@cdxgen/cdxgen 13.1.0 → 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 +8 -7
- package/bin/cdxgen.js +16 -3
- package/bin/evinse.js +2 -0
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +23 -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 +12 -0
- package/lib/cli/index.js +192 -1
- package/lib/cli/jsBom.js +59 -2
- package/lib/cli/jvmBom.js +22 -0
- package/lib/cli/managedBom.js +152 -0
- package/lib/cli/nativeBom.js +798 -1
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +39 -1
- 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 +241 -91
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +143 -2
- 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-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +172 -0
- 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 +1 -1
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +932 -17
- 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/rusi.js +126 -6
- package/lib/evinser/evinser.js +111 -1
- package/lib/helpers/agenticToolutils.js +925 -0
- 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 +8 -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/dosai.js +77 -0
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +13 -0
- package/lib/inventory/fetchBatch.js +74 -2
- 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 +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/parsers/huggingfaceUtils.js +15 -7
- 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 +169 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/package.json +25 -25
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.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/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 +20 -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-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 +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-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/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +29 -0
- 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/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 +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/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +23 -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 +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/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/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/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/lib/ecosystems/rusi.js
CHANGED
|
@@ -9,11 +9,16 @@ import {
|
|
|
9
9
|
safeRmSync,
|
|
10
10
|
safeSpawnSync,
|
|
11
11
|
} from "../core/fs.js";
|
|
12
|
+
import { dirNameStr } from "../core/paths.js";
|
|
12
13
|
import { applyAlgorithmProperties } from "../inventory/cryptoAlgorithmFamily.js";
|
|
13
14
|
import { resolvePluginBinary } from "../inventory/plugins.js";
|
|
14
15
|
|
|
15
16
|
const RUST_LANGUAGES = new Set(["rust", "rs", "rust-lang"]);
|
|
16
17
|
|
|
18
|
+
const RUSI_CRYPTO_OIDS = JSON.parse(
|
|
19
|
+
readFileSync(join(dirNameStr, "data", "crypto-oid.json"), "utf-8"),
|
|
20
|
+
);
|
|
21
|
+
|
|
17
22
|
/**
|
|
18
23
|
* Resolves the rusi binary.
|
|
19
24
|
*
|
|
@@ -82,6 +87,75 @@ function createPurlAliasMap(components = []) {
|
|
|
82
87
|
return purlAliasMap;
|
|
83
88
|
}
|
|
84
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Path roots that never name a dependency crate.
|
|
92
|
+
*/
|
|
93
|
+
const RUST_NON_CRATE_PATH_ROOTS = new Set([
|
|
94
|
+
"crate",
|
|
95
|
+
"self",
|
|
96
|
+
"super",
|
|
97
|
+
"std",
|
|
98
|
+
"core",
|
|
99
|
+
"alloc",
|
|
100
|
+
"proc_macro",
|
|
101
|
+
"test",
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Index the cargo components by the identifier a Rust path spells them with.
|
|
106
|
+
*
|
|
107
|
+
* A crate named `cfg-if` is written `cfg_if` in source, and a lock file can
|
|
108
|
+
* carry several versions of the same crate, so a name maps to every matching
|
|
109
|
+
* purl.
|
|
110
|
+
*
|
|
111
|
+
* @param {Object[]} components Components from the BOM
|
|
112
|
+
* @returns {Map<string, string[]>} Crate identifier to purls
|
|
113
|
+
*/
|
|
114
|
+
function createCrateNameIndex(components = []) {
|
|
115
|
+
const crateNameIndex = new Map();
|
|
116
|
+
for (const component of components) {
|
|
117
|
+
if (!component?.purl?.startsWith("pkg:cargo/") || !component.name) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const crateIdentifier = component.name.toLowerCase().replaceAll("-", "_");
|
|
121
|
+
const purls = crateNameIndex.get(crateIdentifier) || [];
|
|
122
|
+
if (!purls.includes(component.purl)) {
|
|
123
|
+
purls.push(component.purl);
|
|
124
|
+
}
|
|
125
|
+
crateNameIndex.set(crateIdentifier, purls);
|
|
126
|
+
}
|
|
127
|
+
return crateNameIndex;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Resolve the dependency crates a Rust path refers to.
|
|
132
|
+
*
|
|
133
|
+
* rusi reports an import against the package that declares it, so the imported
|
|
134
|
+
* crate has to be recovered from the path itself. This is what turns an
|
|
135
|
+
* `use hex::encode;` into usage evidence for the `hex` component.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} importPath Rust path, such as `hex::encode`
|
|
138
|
+
* @param {Map<string, string[]>} crateNameIndex Index from {@link createCrateNameIndex}
|
|
139
|
+
* @param {string} [ownerPurl] purl of the package that declares the path
|
|
140
|
+
* @returns {string[]} Matching component purls
|
|
141
|
+
*/
|
|
142
|
+
function resolveImportedCratePurls(importPath, crateNameIndex, ownerPurl) {
|
|
143
|
+
if (!importPath || !crateNameIndex?.size) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const pathRoot = String(importPath)
|
|
147
|
+
.replace(/^::/, "")
|
|
148
|
+
.split("::")[0]
|
|
149
|
+
.trim()
|
|
150
|
+
.toLowerCase();
|
|
151
|
+
if (!pathRoot || RUST_NON_CRATE_PATH_ROOTS.has(pathRoot)) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
return (crateNameIndex.get(pathRoot) || []).filter(
|
|
155
|
+
(purl) => purl !== ownerPurl,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
85
159
|
function resolveComponentPurl(purl, purlAliasMap) {
|
|
86
160
|
if (!purl) {
|
|
87
161
|
return undefined;
|
|
@@ -182,19 +256,37 @@ function mergeCryptoComponent(componentsByRef, component, item) {
|
|
|
182
256
|
return existing;
|
|
183
257
|
}
|
|
184
258
|
|
|
259
|
+
// The schema requires an oid for algorithm assets, so an algorithm the OID
|
|
260
|
+
// table cannot name is skipped rather than emitted invalid. rusi reports
|
|
261
|
+
// display-cased names ("SHA-256") while the table is lowercase ("sha-256").
|
|
262
|
+
function resolveCryptoAlgorithmMetadata(name) {
|
|
263
|
+
if (!name) {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
const lowered = String(name).toLowerCase();
|
|
267
|
+
return (
|
|
268
|
+
RUSI_CRYPTO_OIDS[name] ||
|
|
269
|
+
RUSI_CRYPTO_OIDS[lowered] ||
|
|
270
|
+
RUSI_CRYPTO_OIDS[lowered.replace(/[\s_]+/g, "-")]
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
185
274
|
function cryptoAlgorithmComponent(asset) {
|
|
275
|
+
const algorithmName = asset.algorithm || asset.kind || "unknown";
|
|
276
|
+
const algorithmMetadata = resolveCryptoAlgorithmMetadata(algorithmName);
|
|
277
|
+
if (!algorithmMetadata?.oid) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
186
280
|
const component = {
|
|
187
281
|
type: "cryptographic-asset",
|
|
188
|
-
name:
|
|
189
|
-
"bom-ref": cryptoBomRef(
|
|
190
|
-
"algorithm",
|
|
191
|
-
asset.algorithm || asset.kind,
|
|
192
|
-
asset.provider || "unknown",
|
|
193
|
-
),
|
|
282
|
+
name: algorithmName,
|
|
283
|
+
"bom-ref": cryptoBomRef("algorithm", algorithmName, algorithmMetadata.oid),
|
|
194
284
|
description:
|
|
285
|
+
algorithmMetadata.description ||
|
|
195
286
|
"Cryptographic algorithm/component detected by rusi source analysis",
|
|
196
287
|
cryptoProperties: {
|
|
197
288
|
assetType: "algorithm",
|
|
289
|
+
oid: algorithmMetadata.oid,
|
|
198
290
|
algorithmProperties: { primitive: asset.kind || "unknown" },
|
|
199
291
|
},
|
|
200
292
|
properties: [],
|
|
@@ -361,6 +453,7 @@ function addImportEvidence(
|
|
|
361
453
|
purlAliasMap,
|
|
362
454
|
purlLocationMap,
|
|
363
455
|
componentPropertiesMap,
|
|
456
|
+
crateNameIndex,
|
|
364
457
|
) {
|
|
365
458
|
for (const importUsage of rusiReport.imports || []) {
|
|
366
459
|
const purl = resolveComponentPurl(importUsage.purl, purlAliasMap);
|
|
@@ -380,6 +473,31 @@ function addImportEvidence(
|
|
|
380
473
|
"cdx:rusi:importAlias",
|
|
381
474
|
importUsage.alias,
|
|
382
475
|
);
|
|
476
|
+
// The import is also evidence about the crate being imported, which is the
|
|
477
|
+
// component a consumer filters on.
|
|
478
|
+
for (const cratePurl of resolveImportedCratePurls(
|
|
479
|
+
importUsage.path,
|
|
480
|
+
crateNameIndex,
|
|
481
|
+
purl,
|
|
482
|
+
)) {
|
|
483
|
+
addSetValue(
|
|
484
|
+
purlLocationMap,
|
|
485
|
+
cratePurl,
|
|
486
|
+
positionLocation(importUsage.position),
|
|
487
|
+
);
|
|
488
|
+
addPropertyValue(
|
|
489
|
+
componentPropertiesMap,
|
|
490
|
+
cratePurl,
|
|
491
|
+
"cdx:rusi:importedBy",
|
|
492
|
+
purl,
|
|
493
|
+
);
|
|
494
|
+
addPropertyValue(
|
|
495
|
+
componentPropertiesMap,
|
|
496
|
+
cratePurl,
|
|
497
|
+
"cdx:rusi:importPath",
|
|
498
|
+
importUsage.path,
|
|
499
|
+
);
|
|
500
|
+
}
|
|
383
501
|
}
|
|
384
502
|
}
|
|
385
503
|
|
|
@@ -761,6 +879,7 @@ export function analyzeRusiProject(src, options = {}) {
|
|
|
761
879
|
*/
|
|
762
880
|
export function collectRusiEvidence(rusiReport = {}, components = []) {
|
|
763
881
|
const purlAliasMap = createPurlAliasMap(components);
|
|
882
|
+
const crateNameIndex = createCrateNameIndex(components);
|
|
764
883
|
const purlLocationMap = {};
|
|
765
884
|
const dataFlowFrames = {};
|
|
766
885
|
const componentPropertiesMap = {};
|
|
@@ -774,6 +893,7 @@ export function collectRusiEvidence(rusiReport = {}, components = []) {
|
|
|
774
893
|
purlAliasMap,
|
|
775
894
|
purlLocationMap,
|
|
776
895
|
componentPropertiesMap,
|
|
896
|
+
crateNameIndex,
|
|
777
897
|
);
|
|
778
898
|
addUsageEvidence(
|
|
779
899
|
rusiReport,
|
package/lib/evinser/evinser.js
CHANGED
|
@@ -25,6 +25,15 @@ import {
|
|
|
25
25
|
getGradleCommand,
|
|
26
26
|
getMavenCommand,
|
|
27
27
|
} from "../ecosystems/gradleutils.js";
|
|
28
|
+
import {
|
|
29
|
+
analyzeKosiProject,
|
|
30
|
+
collectKosiApiEndpoints,
|
|
31
|
+
collectKosiEvidence,
|
|
32
|
+
collectKosiServices,
|
|
33
|
+
isKosiKotlinLanguage,
|
|
34
|
+
kosiWorkspaceComponent,
|
|
35
|
+
mergeKosiEvidence,
|
|
36
|
+
} from "../ecosystems/kosi.js";
|
|
28
37
|
import {
|
|
29
38
|
analyzeRusiProject,
|
|
30
39
|
collectRusiEvidence,
|
|
@@ -42,6 +51,7 @@ import {
|
|
|
42
51
|
import { collectMvnDependencies } from "../inventory/deps.js";
|
|
43
52
|
import { mergeServices } from "../inventory/depsUtils.js";
|
|
44
53
|
import {
|
|
54
|
+
applyDosaiReachabilityEvidence,
|
|
45
55
|
collectDosaiAiComponents,
|
|
46
56
|
collectDosaiDataFlowFrames,
|
|
47
57
|
collectDosaiPurlEvidence,
|
|
@@ -616,8 +626,13 @@ export function initFromSbom(components, language) {
|
|
|
616
626
|
});
|
|
617
627
|
}
|
|
618
628
|
if (comp.evidence.occurrences) {
|
|
629
|
+
// Keep the `location#line` shape so a re-run over a cdxgen BOM does not
|
|
630
|
+
// degrade seeded occurrences to file-only entries and duplicate the
|
|
631
|
+
// richer locations the fresh slice merge adds to the same set.
|
|
619
632
|
purlLocationMap[comp.purl] = new Set(
|
|
620
|
-
comp.evidence.occurrences.map((v) =>
|
|
633
|
+
comp.evidence.occurrences.map((v) =>
|
|
634
|
+
v.line ? `${v.location}#${v.line}` : v.location,
|
|
635
|
+
),
|
|
621
636
|
);
|
|
622
637
|
}
|
|
623
638
|
}
|
|
@@ -724,6 +739,84 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
724
739
|
};
|
|
725
740
|
}
|
|
726
741
|
|
|
742
|
+
if (isKosiKotlinLanguage(language)) {
|
|
743
|
+
const kosiReports = analyzeKosiProject(dirPath, options);
|
|
744
|
+
if (kosiReports?.report && Object.keys(kosiReports.report).length) {
|
|
745
|
+
// The workspace component: cdxgen's Kotlin BOM carries no project
|
|
746
|
+
// component, so every workspace-anchored slice (reachability, the
|
|
747
|
+
// app's own flows) would have nothing to attach to. createEvinseFile
|
|
748
|
+
// re-reads the BOM from disk, so the candidate rides the artefacts
|
|
749
|
+
// instead of a mutation here — applied when the BOM lacks a project
|
|
750
|
+
// component OR carries one with no purl: every kosi evidence join is
|
|
751
|
+
// keyed by purl, so a purl-less component anchors nothing and is, for
|
|
752
|
+
// this purpose, no anchor at all (found on the sample app: Gradle
|
|
753
|
+
// identity with empty group and version "latest" emits no purl, and
|
|
754
|
+
// the reachableFromRoots/cryptoFlow evidence silently attached to
|
|
755
|
+
// nothing while the dependency-joined kinds landed).
|
|
756
|
+
const metadataComponentHasPurl = Boolean(
|
|
757
|
+
bomJson.metadata?.component?.purl,
|
|
758
|
+
);
|
|
759
|
+
let workspaceComponent;
|
|
760
|
+
if (!metadataComponentHasPurl) {
|
|
761
|
+
// kosi's modules[] holds the project's OWN Gradle/Maven modules
|
|
762
|
+
// (dependencies never appear there), so the project component is
|
|
763
|
+
// its root: the shortest modulePath, ties broken by name so the
|
|
764
|
+
// choice is deterministic. `workspaceMember` is not the selector —
|
|
765
|
+
// it is a String naming the owning workspace member in kosi's
|
|
766
|
+
// schema, empty for a single-module build and then omitted from the
|
|
767
|
+
// JSON entirely, so testing it against `undefined` picked nothing
|
|
768
|
+
// on exactly the single-module projects this fallback exists for.
|
|
769
|
+
const workspaceModule = (kosiReports.report.modules || [])
|
|
770
|
+
.filter((candidate) => candidate?.purl)
|
|
771
|
+
.sort(
|
|
772
|
+
(a, b) =>
|
|
773
|
+
(a.modulePath || "").length - (b.modulePath || "").length ||
|
|
774
|
+
(a.name || "").localeCompare(b.name || ""),
|
|
775
|
+
)[0];
|
|
776
|
+
if (workspaceModule?.purl) {
|
|
777
|
+
workspaceComponent = kosiWorkspaceComponent(workspaceModule);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const analysisComponents = [
|
|
781
|
+
bomJson.metadata?.component,
|
|
782
|
+
workspaceComponent,
|
|
783
|
+
...components,
|
|
784
|
+
].filter(Boolean);
|
|
785
|
+
const kosiEvidence = collectKosiEvidence(
|
|
786
|
+
kosiReports.report,
|
|
787
|
+
analysisComponents,
|
|
788
|
+
);
|
|
789
|
+
mergeKosiEvidence(
|
|
790
|
+
kosiEvidence,
|
|
791
|
+
collectKosiEvidence(
|
|
792
|
+
kosiReports.reachableReport || {},
|
|
793
|
+
analysisComponents,
|
|
794
|
+
),
|
|
795
|
+
);
|
|
796
|
+
servicesMap = collectKosiServices(kosiReports.report, servicesMap);
|
|
797
|
+
// The INBOUND surface: the routes the app serves, with the
|
|
798
|
+
// authentication kosi found declared on them.
|
|
799
|
+
servicesMap = collectKosiApiEndpoints(kosiReports.report, servicesMap);
|
|
800
|
+
return {
|
|
801
|
+
purlLocationMap: kosiEvidence.purlLocationMap,
|
|
802
|
+
dataFlowFrames: kosiEvidence.dataFlowFrames,
|
|
803
|
+
componentPropertiesMap: kosiEvidence.componentPropertiesMap,
|
|
804
|
+
metadataProperties: kosiEvidence.metadataProperties,
|
|
805
|
+
cryptoComponents: kosiEvidence.cryptoComponents,
|
|
806
|
+
cryptoGeneratePurls: kosiEvidence.cryptoGeneratePurls,
|
|
807
|
+
servicesMap,
|
|
808
|
+
userDefinedTypesMap,
|
|
809
|
+
workspaceComponent,
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
return {
|
|
813
|
+
purlLocationMap,
|
|
814
|
+
servicesMap,
|
|
815
|
+
dataFlowFrames,
|
|
816
|
+
userDefinedTypesMap,
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
|
|
727
820
|
if (isDosaiDotnetLanguage(language)) {
|
|
728
821
|
if (options.profile === "research") {
|
|
729
822
|
options.withDataFlow = true;
|
|
@@ -746,6 +839,10 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
746
839
|
}
|
|
747
840
|
if (usageSlice && Object.keys(usageSlice).length) {
|
|
748
841
|
const dosaiEvidence = collectDosaiPurlEvidence(usageSlice, components);
|
|
842
|
+
// Confidence/evidence facts ride along as properties so an unbuilt tree (Low
|
|
843
|
+
// confidence, unresolved evidence) stays distinguishable from an only-imported
|
|
844
|
+
// package in the emitted BOM.
|
|
845
|
+
applyDosaiReachabilityEvidence(usageSlice, components);
|
|
749
846
|
for (const [purl, locations] of Object.entries(
|
|
750
847
|
dosaiEvidence.purlLocationMap,
|
|
751
848
|
)) {
|
|
@@ -1833,6 +1930,7 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
1833
1930
|
cryptoComponents,
|
|
1834
1931
|
aiComponents,
|
|
1835
1932
|
cryptoGeneratePurls,
|
|
1933
|
+
workspaceComponent,
|
|
1836
1934
|
} = sliceArtefacts;
|
|
1837
1935
|
const bomFile = options.input;
|
|
1838
1936
|
const evinseOutFile = options.output;
|
|
@@ -1840,6 +1938,18 @@ export async function createEvinseFile(sliceArtefacts, options) {
|
|
|
1840
1938
|
const components = bomJson.components || [];
|
|
1841
1939
|
// Clear existing annotations
|
|
1842
1940
|
bomJson.annotations = [];
|
|
1941
|
+
if (workspaceComponent && !bomJson.metadata?.component?.purl) {
|
|
1942
|
+
if (bomJson.metadata?.component) {
|
|
1943
|
+
// A purl-less project component: keep cdxgen's own identity fields
|
|
1944
|
+
// and supply the purl kosi's module discovery published, so the
|
|
1945
|
+
// evidence loop below can anchor workspace evidence to it.
|
|
1946
|
+
bomJson.metadata.component.purl = workspaceComponent.purl;
|
|
1947
|
+
bomJson.metadata.component["bom-ref"] ??= workspaceComponent.purl;
|
|
1948
|
+
} else {
|
|
1949
|
+
bomJson.metadata ??= {};
|
|
1950
|
+
bomJson.metadata.component = workspaceComponent;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1843
1953
|
appendComponentProperties(bomJson.metadata?.component, metadataProperties);
|
|
1844
1954
|
let occEvidencePresent = false;
|
|
1845
1955
|
let csEvidencePresent = false;
|