@cyclonedx/cdxgen 12.3.0 → 12.3.2
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 +15 -5
- package/bin/audit.js +7 -0
- package/bin/cdxgen.js +241 -81
- package/bin/repl.js +138 -0
- package/data/rules/ai-agent-governance.yaml +249 -0
- package/data/rules/dependency-sources.yaml +41 -0
- package/data/rules/mcp-servers.yaml +304 -0
- package/data/rules/package-integrity.yaml +123 -0
- package/lib/audit/index.js +353 -29
- package/lib/audit/index.poku.js +247 -7
- package/lib/audit/reporters.js +26 -0
- package/lib/audit/scoring.js +262 -13
- package/lib/audit/scoring.poku.js +179 -0
- package/lib/audit/targets.js +391 -2
- package/lib/audit/targets.poku.js +416 -3
- package/lib/cli/index.js +588 -45
- package/lib/cli/index.poku.js +735 -1
- package/lib/evinser/evinser.js +8 -5
- package/lib/helpers/agentFormulationParser.js +318 -0
- package/lib/helpers/aiInventory.js +262 -0
- package/lib/helpers/aiInventory.poku.js +111 -0
- package/lib/helpers/analyzer.js +1769 -0
- package/lib/helpers/analyzer.poku.js +284 -3
- package/lib/helpers/auditCategories.js +76 -0
- package/lib/helpers/ciParsers/githubActions.js +140 -16
- package/lib/helpers/ciParsers/githubActions.poku.js +110 -0
- package/lib/helpers/communityAiConfigParser.js +672 -0
- package/lib/helpers/communityAiConfigParser.poku.js +63 -0
- package/lib/helpers/depsUtils.js +108 -0
- package/lib/helpers/depsUtils.poku.js +72 -1
- package/lib/helpers/display.js +325 -3
- package/lib/helpers/display.poku.js +301 -0
- package/lib/helpers/formulationParsers.js +28 -0
- package/lib/helpers/formulationParsers.poku.js +504 -1
- package/lib/helpers/jsonLike.js +102 -0
- package/lib/helpers/jsonLike.poku.js +34 -0
- package/lib/helpers/mcp.js +248 -0
- package/lib/helpers/mcp.poku.js +101 -0
- package/lib/helpers/mcpConfigParser.js +656 -0
- package/lib/helpers/mcpConfigParser.poku.js +126 -0
- package/lib/helpers/mcpDiscovery.js +84 -0
- package/lib/helpers/mcpDiscovery.poku.js +21 -0
- package/lib/helpers/protobom.js +3 -3
- package/lib/helpers/provenanceUtils.js +29 -4
- package/lib/helpers/provenanceUtils.poku.js +29 -3
- package/lib/helpers/registryProvenance.js +210 -0
- package/lib/helpers/registryProvenance.poku.js +144 -0
- package/lib/helpers/rustFormulationParser.js +330 -0
- package/lib/helpers/source.js +21 -2
- package/lib/helpers/source.poku.js +38 -0
- package/lib/helpers/utils.js +1331 -83
- package/lib/helpers/utils.poku.js +599 -188
- package/lib/helpers/vsixutils.js +12 -4
- package/lib/helpers/vsixutils.poku.js +34 -0
- package/lib/managers/binary.js +36 -12
- package/lib/managers/binary.poku.js +68 -0
- package/lib/managers/docker.js +59 -9
- package/lib/managers/docker.poku.js +61 -0
- package/lib/managers/piptree.js +12 -7
- package/lib/managers/piptree.poku.js +44 -0
- package/lib/stages/postgen/annotator.js +2 -1
- package/lib/stages/postgen/annotator.poku.js +15 -0
- package/lib/stages/postgen/auditBom.js +20 -6
- package/lib/stages/postgen/auditBom.poku.js +694 -1
- package/lib/stages/postgen/postgen.js +262 -11
- package/lib/stages/postgen/postgen.poku.js +306 -2
- package/lib/stages/postgen/ruleEngine.js +49 -1
- package/lib/stages/postgen/spdxConverter.poku.js +70 -0
- package/lib/stages/pregen/pregen.js +6 -4
- package/package.json +1 -1
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/reporters.d.ts.map +1 -1
- package/types/lib/audit/scoring.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts +12 -0
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +2 -8
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/agentFormulationParser.d.ts +19 -0
- package/types/lib/helpers/agentFormulationParser.d.ts.map +1 -0
- package/types/lib/helpers/aiInventory.d.ts +23 -0
- package/types/lib/helpers/aiInventory.d.ts.map +1 -0
- package/types/lib/helpers/analyzer.d.ts +10 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/auditCategories.d.ts +12 -0
- package/types/lib/helpers/auditCategories.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/communityAiConfigParser.d.ts +29 -0
- package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -0
- package/types/lib/helpers/depsUtils.d.ts +8 -0
- package/types/lib/helpers/depsUtils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts +17 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
- package/types/lib/helpers/jsonLike.d.ts +4 -0
- package/types/lib/helpers/jsonLike.d.ts.map +1 -0
- package/types/lib/helpers/mcp.d.ts +29 -0
- package/types/lib/helpers/mcp.d.ts.map +1 -0
- package/types/lib/helpers/mcpConfigParser.d.ts +30 -0
- package/types/lib/helpers/mcpConfigParser.d.ts.map +1 -0
- package/types/lib/helpers/mcpDiscovery.d.ts +5 -0
- package/types/lib/helpers/mcpDiscovery.d.ts.map +1 -0
- package/types/lib/helpers/provenanceUtils.d.ts +5 -3
- package/types/lib/helpers/provenanceUtils.d.ts.map +1 -1
- package/types/lib/helpers/registryProvenance.d.ts +9 -0
- package/types/lib/helpers/registryProvenance.d.ts.map +1 -1
- package/types/lib/helpers/rustFormulationParser.d.ts +17 -0
- package/types/lib/helpers/rustFormulationParser.d.ts.map +1 -0
- package/types/lib/helpers/source.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +31 -1
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/helpers/vsixutils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/piptree.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- 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/pregen.d.ts.map +1 -1
package/lib/audit/targets.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { PackageURL } from "packageurl-js";
|
|
2
2
|
|
|
3
3
|
import { hasTrustedPublishingProperties } from "../helpers/provenanceUtils.js";
|
|
4
|
+
import {
|
|
5
|
+
getCratesMetadata,
|
|
6
|
+
getNpmMetadata,
|
|
7
|
+
getPyMetadata,
|
|
8
|
+
} from "../helpers/utils.js";
|
|
4
9
|
|
|
5
|
-
const SUPPORTED_PURL_TYPES = new Set(["npm", "pypi"]);
|
|
10
|
+
const SUPPORTED_PURL_TYPES = new Set(["cargo", "npm", "pypi"]);
|
|
6
11
|
const NON_REQUIRED_SCOPES = new Set(["excluded", "optional"]);
|
|
7
12
|
|
|
8
13
|
/**
|
|
@@ -19,12 +24,14 @@ function normalizeTargetSelectionOptions(options) {
|
|
|
19
24
|
if (typeof options === "number") {
|
|
20
25
|
return {
|
|
21
26
|
maxTargets: options,
|
|
27
|
+
prioritizeDirectRuntime: true,
|
|
22
28
|
scope: undefined,
|
|
23
29
|
trusted: "exclude",
|
|
24
30
|
};
|
|
25
31
|
}
|
|
26
32
|
return {
|
|
27
33
|
maxTargets: options?.maxTargets,
|
|
34
|
+
prioritizeDirectRuntime: options?.prioritizeDirectRuntime ?? true,
|
|
28
35
|
scope: options?.scope === "required" ? "required" : undefined,
|
|
29
36
|
trusted:
|
|
30
37
|
options?.trusted === "only"
|
|
@@ -57,6 +64,313 @@ function normalizeComponentScope(scope) {
|
|
|
57
64
|
return scope.toLowerCase();
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
function getComponentPropertyValue(component, propertyName) {
|
|
68
|
+
return component?.properties?.find(
|
|
69
|
+
(property) => property.name === propertyName,
|
|
70
|
+
)?.value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getComponentPropertyValues(component, propertyName) {
|
|
74
|
+
return (component?.properties || [])
|
|
75
|
+
.filter((property) => property?.name === propertyName)
|
|
76
|
+
.map((property) => property?.value)
|
|
77
|
+
.filter((propertyValue) => typeof propertyValue === "string")
|
|
78
|
+
.map((propertyValue) => propertyValue.trim())
|
|
79
|
+
.filter(Boolean);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function hasTruthyComponentProperty(component, propertyName) {
|
|
83
|
+
return getComponentPropertyValue(component, propertyName) === "true";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function hasNonEmptyComponentProperty(component, propertyName) {
|
|
87
|
+
const propertyValue = getComponentPropertyValue(component, propertyName);
|
|
88
|
+
return typeof propertyValue === "string" && propertyValue.trim().length > 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function extractRootDependencyRefs(bomJson) {
|
|
92
|
+
const directRefs = new Set();
|
|
93
|
+
const rootRef =
|
|
94
|
+
bomJson?.metadata?.component?.["bom-ref"] ||
|
|
95
|
+
bomJson?.metadata?.component?.bomRef ||
|
|
96
|
+
bomJson?.metadata?.component?.purl;
|
|
97
|
+
if (!rootRef || !Array.isArray(bomJson?.dependencies)) {
|
|
98
|
+
return directRefs;
|
|
99
|
+
}
|
|
100
|
+
const rootDependency = bomJson.dependencies.find(
|
|
101
|
+
(dependency) => dependency?.ref === rootRef,
|
|
102
|
+
);
|
|
103
|
+
if (!Array.isArray(rootDependency?.dependsOn)) {
|
|
104
|
+
return directRefs;
|
|
105
|
+
}
|
|
106
|
+
for (const dependencyRef of rootDependency.dependsOn) {
|
|
107
|
+
if (dependencyRef) {
|
|
108
|
+
directRefs.add(dependencyRef);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return directRefs;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function isPlatformSpecificComponent(component, type) {
|
|
115
|
+
if (type === "npm") {
|
|
116
|
+
return ["cdx:npm:cpu", "cdx:npm:libc", "cdx:npm:os"].some((propertyName) =>
|
|
117
|
+
hasNonEmptyComponentProperty(component, propertyName),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
if (type === "pypi") {
|
|
121
|
+
return [
|
|
122
|
+
"cdx:pip:markers",
|
|
123
|
+
"cdx:pypi:requiresPython",
|
|
124
|
+
"cdx:python:requires_python",
|
|
125
|
+
].some((propertyName) =>
|
|
126
|
+
hasNonEmptyComponentProperty(component, propertyName),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (type === "cargo") {
|
|
130
|
+
return ["cdx:cargo:target"].some((propertyName) =>
|
|
131
|
+
hasNonEmptyComponentProperty(component, propertyName),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isDevelopmentOnlyComponent(component, type) {
|
|
138
|
+
if (type === "npm") {
|
|
139
|
+
return hasTruthyComponentProperty(component, "cdx:npm:package:development");
|
|
140
|
+
}
|
|
141
|
+
if (type === "cargo") {
|
|
142
|
+
const dependencyKinds = getComponentPropertyValues(
|
|
143
|
+
component,
|
|
144
|
+
"cdx:cargo:dependencyKind",
|
|
145
|
+
).map((value) => value.toLowerCase());
|
|
146
|
+
return dependencyKinds.length
|
|
147
|
+
? dependencyKinds.every((value) => value === "dev")
|
|
148
|
+
: false;
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function isBuildOnlyWorkspaceCargoComponent(component) {
|
|
154
|
+
const dependencyKinds = getComponentPropertyValues(
|
|
155
|
+
component,
|
|
156
|
+
"cdx:cargo:dependencyKind",
|
|
157
|
+
).map((value) => value.toLowerCase());
|
|
158
|
+
return (
|
|
159
|
+
dependencyKinds.length > 0 &&
|
|
160
|
+
dependencyKinds.every((value) => value === "build") &&
|
|
161
|
+
hasTruthyComponentProperty(
|
|
162
|
+
component,
|
|
163
|
+
"cdx:cargo:workspaceDependencyResolved",
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function isRuntimeFacingCargoComponent(component, directDependency) {
|
|
169
|
+
const dependencyKinds = getComponentPropertyValues(
|
|
170
|
+
component,
|
|
171
|
+
"cdx:cargo:dependencyKind",
|
|
172
|
+
).map((value) => value.toLowerCase());
|
|
173
|
+
if (dependencyKinds.includes("runtime")) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return Boolean(
|
|
177
|
+
directDependency &&
|
|
178
|
+
!isDevelopmentOnlyComponent(component, "cargo") &&
|
|
179
|
+
!isBuildOnlyWorkspaceCargoComponent(component),
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function getComponentOccurrenceCount(component) {
|
|
184
|
+
return Array.isArray(component?.evidence?.occurrences)
|
|
185
|
+
? component.evidence.occurrences.length
|
|
186
|
+
: 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function triagePriorityScore(target) {
|
|
190
|
+
// Triage should start with the most user-actionable packages:
|
|
191
|
+
// direct runtime dependencies first, then other required deps, then
|
|
192
|
+
// deprioritize dev-only and platform-constrained packages.
|
|
193
|
+
let priority = 0;
|
|
194
|
+
if (target?.directDependency) {
|
|
195
|
+
priority += 16;
|
|
196
|
+
}
|
|
197
|
+
if (target?.explicitRequiredScope) {
|
|
198
|
+
priority += 8;
|
|
199
|
+
}
|
|
200
|
+
if (target?.required) {
|
|
201
|
+
priority += 4;
|
|
202
|
+
}
|
|
203
|
+
if (target?.type === "cargo" && target?.runtimeFacingCargo) {
|
|
204
|
+
priority += 6;
|
|
205
|
+
}
|
|
206
|
+
priority += Math.min(target?.occurrenceCount || 0, 6);
|
|
207
|
+
if (!target?.developmentOnly) {
|
|
208
|
+
priority += 2;
|
|
209
|
+
}
|
|
210
|
+
if (!target?.platformSpecific) {
|
|
211
|
+
priority += 1;
|
|
212
|
+
}
|
|
213
|
+
if (target?.type === "cargo" && target?.buildOnlyWorkspace) {
|
|
214
|
+
priority -= 5;
|
|
215
|
+
}
|
|
216
|
+
return priority;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function registryMetadataPropertyName(propertyName) {
|
|
220
|
+
return (
|
|
221
|
+
propertyName?.startsWith("cdx:cargo:") ||
|
|
222
|
+
propertyName?.startsWith("cdx:npm:trustedPublishing") ||
|
|
223
|
+
propertyName?.startsWith("cdx:npm:provenance") ||
|
|
224
|
+
propertyName?.startsWith("cdx:pypi:trustedPublishing") ||
|
|
225
|
+
propertyName?.startsWith("cdx:pypi:provenance") ||
|
|
226
|
+
propertyName === "cdx:pypi:uploaderVerified"
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function appendUniqueProperties(targetProperties, extraProperties) {
|
|
231
|
+
const existing = new Set(
|
|
232
|
+
(targetProperties || []).map((property) =>
|
|
233
|
+
[property.name, property.value].join("="),
|
|
234
|
+
),
|
|
235
|
+
);
|
|
236
|
+
for (const property of extraProperties || []) {
|
|
237
|
+
if (!property?.name) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const key = [property.name, property.value].join("=");
|
|
241
|
+
if (existing.has(key)) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
targetProperties.push(property);
|
|
245
|
+
existing.add(key);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function auditRegistryMetadataKey(type, namespace, name, version) {
|
|
250
|
+
return [type, namespace || "", name || "", version || ""].join("|");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function buildRegistryMetadataCandidate(componentPurl, component) {
|
|
254
|
+
let purlObj;
|
|
255
|
+
try {
|
|
256
|
+
purlObj = PackageURL.fromString(componentPurl);
|
|
257
|
+
} catch {
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
if (!SUPPORTED_PURL_TYPES.has(purlObj.type) || !purlObj.version) {
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
const auditMetadataKey = auditRegistryMetadataKey(
|
|
264
|
+
purlObj.type,
|
|
265
|
+
purlObj.namespace,
|
|
266
|
+
purlObj.name,
|
|
267
|
+
purlObj.version,
|
|
268
|
+
);
|
|
269
|
+
return {
|
|
270
|
+
_auditMetadataKey: auditMetadataKey,
|
|
271
|
+
group: purlObj.namespace,
|
|
272
|
+
name: purlObj.name,
|
|
273
|
+
properties: Array.isArray(component?.properties)
|
|
274
|
+
? component.properties.map((property) => ({ ...property }))
|
|
275
|
+
: [],
|
|
276
|
+
type: purlObj.type,
|
|
277
|
+
version: purlObj.version,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function restoreFetchPackageMetadata(originalFetchPackageMetadata) {
|
|
282
|
+
if (originalFetchPackageMetadata === undefined) {
|
|
283
|
+
delete process.env.CDXGEN_FETCH_PKG_METADATA;
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
process.env.CDXGEN_FETCH_PKG_METADATA = originalFetchPackageMetadata;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Enrich input BOM components with registry provenance/trusted-publishing
|
|
291
|
+
* metadata so audit target filtering can exclude trusted packages even when the
|
|
292
|
+
* input BOM was generated without --bom-audit.
|
|
293
|
+
*
|
|
294
|
+
* @param {{ source: string, bomJson: object }[]} inputBoms loaded input BOMs
|
|
295
|
+
* @returns {Promise<void>}
|
|
296
|
+
*/
|
|
297
|
+
export async function enrichInputBomsWithRegistryMetadata(inputBoms) {
|
|
298
|
+
const cargoCandidates = [];
|
|
299
|
+
const npmCandidates = [];
|
|
300
|
+
const pypiCandidates = [];
|
|
301
|
+
const componentRefs = new Map();
|
|
302
|
+
for (const inputBom of inputBoms || []) {
|
|
303
|
+
const components = Array.isArray(inputBom?.bomJson?.components)
|
|
304
|
+
? inputBom.bomJson.components
|
|
305
|
+
: [];
|
|
306
|
+
for (const component of components) {
|
|
307
|
+
const componentPurl = component?.purl;
|
|
308
|
+
if (
|
|
309
|
+
!componentPurl ||
|
|
310
|
+
hasTrustedPublishingProperties(component?.properties || [])
|
|
311
|
+
) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const candidate = buildRegistryMetadataCandidate(
|
|
315
|
+
componentPurl,
|
|
316
|
+
component,
|
|
317
|
+
);
|
|
318
|
+
if (!candidate) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (!componentRefs.has(candidate._auditMetadataKey)) {
|
|
322
|
+
componentRefs.set(candidate._auditMetadataKey, []);
|
|
323
|
+
if (candidate.type === "cargo") {
|
|
324
|
+
cargoCandidates.push(candidate);
|
|
325
|
+
} else if (candidate.type === "npm") {
|
|
326
|
+
npmCandidates.push(candidate);
|
|
327
|
+
} else if (candidate.type === "pypi") {
|
|
328
|
+
pypiCandidates.push(candidate);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
componentRefs.get(candidate._auditMetadataKey).push(component);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (
|
|
335
|
+
!cargoCandidates.length &&
|
|
336
|
+
!npmCandidates.length &&
|
|
337
|
+
!pypiCandidates.length
|
|
338
|
+
) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const originalFetchPackageMetadata = process.env.CDXGEN_FETCH_PKG_METADATA;
|
|
342
|
+
process.env.CDXGEN_FETCH_PKG_METADATA = "true";
|
|
343
|
+
try {
|
|
344
|
+
const enrichedCandidates = [
|
|
345
|
+
...(cargoCandidates.length
|
|
346
|
+
? await getCratesMetadata(cargoCandidates)
|
|
347
|
+
: []),
|
|
348
|
+
...(npmCandidates.length ? await getNpmMetadata(npmCandidates) : []),
|
|
349
|
+
...(pypiCandidates.length
|
|
350
|
+
? await getPyMetadata(pypiCandidates, false)
|
|
351
|
+
: []),
|
|
352
|
+
];
|
|
353
|
+
for (const candidate of enrichedCandidates) {
|
|
354
|
+
const matchedComponents =
|
|
355
|
+
componentRefs.get(candidate._auditMetadataKey) || [];
|
|
356
|
+
const registryProperties = (candidate.properties || []).filter(
|
|
357
|
+
(property) => registryMetadataPropertyName(property?.name),
|
|
358
|
+
);
|
|
359
|
+
if (!registryProperties.length) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
for (const component of matchedComponents) {
|
|
363
|
+
component.properties = Array.isArray(component.properties)
|
|
364
|
+
? component.properties
|
|
365
|
+
: [];
|
|
366
|
+
appendUniqueProperties(component.properties, registryProperties);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} finally {
|
|
370
|
+
restoreFetchPackageMetadata(originalFetchPackageMetadata);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
60
374
|
function mergeTargetScope(existingTarget, nextTarget) {
|
|
61
375
|
const mergedRequired = Boolean(
|
|
62
376
|
existingTarget.required || nextTarget.required,
|
|
@@ -101,6 +415,7 @@ export function extractPurlTargetsFromBom(bomJson, sourceName, options) {
|
|
|
101
415
|
const components = Array.isArray(bomJson?.components)
|
|
102
416
|
? bomJson.components
|
|
103
417
|
: [];
|
|
418
|
+
const rootDependencyRefs = extractRootDependencyRefs(bomJson);
|
|
104
419
|
for (const component of components) {
|
|
105
420
|
const componentScope = normalizeComponentScope(component?.scope);
|
|
106
421
|
if (
|
|
@@ -139,6 +454,10 @@ export function extractPurlTargetsFromBom(bomJson, sourceName, options) {
|
|
|
139
454
|
}
|
|
140
455
|
targets.push({
|
|
141
456
|
bomRef: component?.["bom-ref"],
|
|
457
|
+
buildOnlyWorkspace:
|
|
458
|
+
purlObj.type === "cargo"
|
|
459
|
+
? isBuildOnlyWorkspaceCargoComponent(component)
|
|
460
|
+
: false,
|
|
142
461
|
name: purlObj.name,
|
|
143
462
|
namespace: purlObj.namespace,
|
|
144
463
|
purl: componentPurl,
|
|
@@ -146,7 +465,22 @@ export function extractPurlTargetsFromBom(bomJson, sourceName, options) {
|
|
|
146
465
|
? component.properties.map((property) => ({ ...property }))
|
|
147
466
|
: [],
|
|
148
467
|
qualifiers: purlObj.qualifiers,
|
|
468
|
+
directDependency:
|
|
469
|
+
Boolean(component?.["bom-ref"]) &&
|
|
470
|
+
rootDependencyRefs.has(component["bom-ref"]),
|
|
471
|
+
explicitRequiredScope: componentScope === "required",
|
|
472
|
+
developmentOnly: isDevelopmentOnlyComponent(component, purlObj.type),
|
|
473
|
+
occurrenceCount: getComponentOccurrenceCount(component),
|
|
474
|
+
platformSpecific: isPlatformSpecificComponent(component, purlObj.type),
|
|
149
475
|
required: isRequiredComponentScope(componentScope),
|
|
476
|
+
runtimeFacingCargo:
|
|
477
|
+
purlObj.type === "cargo"
|
|
478
|
+
? isRuntimeFacingCargoComponent(
|
|
479
|
+
component,
|
|
480
|
+
Boolean(component?.["bom-ref"]) &&
|
|
481
|
+
rootDependencyRefs.has(component["bom-ref"]),
|
|
482
|
+
)
|
|
483
|
+
: false,
|
|
150
484
|
scope: componentScope,
|
|
151
485
|
source: sourceName,
|
|
152
486
|
trustedPublishing: hasTrustedPublishingProperties(component?.properties),
|
|
@@ -189,7 +523,30 @@ export function collectAuditTargets(inputBoms, options) {
|
|
|
189
523
|
for (const target of extracted.targets) {
|
|
190
524
|
const existing = targetMap.get(target.purl);
|
|
191
525
|
if (existing) {
|
|
526
|
+
existing.directDependency = Boolean(
|
|
527
|
+
existing.directDependency || target.directDependency,
|
|
528
|
+
);
|
|
529
|
+
existing.explicitRequiredScope = Boolean(
|
|
530
|
+
existing.explicitRequiredScope || target.explicitRequiredScope,
|
|
531
|
+
);
|
|
532
|
+
// A package stays dev-only or platform-specific only when every observed
|
|
533
|
+
// occurrence carries that constraint. Any runtime/general occurrence
|
|
534
|
+
// should lift the deprioritization signal for triage ordering.
|
|
535
|
+
existing.developmentOnly = Boolean(
|
|
536
|
+
existing.developmentOnly && target.developmentOnly,
|
|
537
|
+
);
|
|
538
|
+
existing.occurrenceCount =
|
|
539
|
+
(existing.occurrenceCount || 0) + (target.occurrenceCount || 0);
|
|
540
|
+
existing.platformSpecific = Boolean(
|
|
541
|
+
existing.platformSpecific && target.platformSpecific,
|
|
542
|
+
);
|
|
543
|
+
existing.buildOnlyWorkspace = Boolean(
|
|
544
|
+
existing.buildOnlyWorkspace && target.buildOnlyWorkspace,
|
|
545
|
+
);
|
|
192
546
|
existing.required = Boolean(existing.required || target.required);
|
|
547
|
+
existing.runtimeFacingCargo = Boolean(
|
|
548
|
+
existing.runtimeFacingCargo || target.runtimeFacingCargo,
|
|
549
|
+
);
|
|
193
550
|
existing.scope = mergeTargetScope(existing, target);
|
|
194
551
|
existing.trustedPublishing = Boolean(
|
|
195
552
|
existing.trustedPublishing || target.trustedPublishing,
|
|
@@ -223,7 +580,15 @@ export function collectAuditTargets(inputBoms, options) {
|
|
|
223
580
|
normalizedName: normalizePackageName(target.name),
|
|
224
581
|
sources: [...target.sources].sort(),
|
|
225
582
|
}));
|
|
226
|
-
targets.sort((left, right) =>
|
|
583
|
+
targets.sort((left, right) => {
|
|
584
|
+
if (selectorOptions.prioritizeDirectRuntime) {
|
|
585
|
+
const scoreDelta = triagePriorityScore(right) - triagePriorityScore(left);
|
|
586
|
+
if (scoreDelta !== 0) {
|
|
587
|
+
return scoreDelta;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return left.purl.localeCompare(right.purl);
|
|
591
|
+
});
|
|
227
592
|
const trustedTargets = targets.filter((target) => target.trustedPublishing);
|
|
228
593
|
if (selectorOptions.trusted === "only") {
|
|
229
594
|
targets = trustedTargets;
|
|
@@ -232,6 +597,25 @@ export function collectAuditTargets(inputBoms, options) {
|
|
|
232
597
|
}
|
|
233
598
|
const requiredTargets = targets.filter((target) => target.required);
|
|
234
599
|
const nonRequiredTargets = targets.filter((target) => !target.required);
|
|
600
|
+
const directRuntimeTargets = targets.filter(
|
|
601
|
+
(target) =>
|
|
602
|
+
target.directDependency &&
|
|
603
|
+
target.required &&
|
|
604
|
+
!target.developmentOnly &&
|
|
605
|
+
!target.platformSpecific,
|
|
606
|
+
);
|
|
607
|
+
const developmentOnlyTargets = targets.filter(
|
|
608
|
+
(target) => target.developmentOnly,
|
|
609
|
+
);
|
|
610
|
+
const platformSpecificTargets = targets.filter(
|
|
611
|
+
(target) => target.platformSpecific,
|
|
612
|
+
);
|
|
613
|
+
const buildOnlyWorkspaceTargets = targets.filter(
|
|
614
|
+
(target) => target.type === "cargo" && target.buildOnlyWorkspace,
|
|
615
|
+
);
|
|
616
|
+
const cargoRuntimeFacingTargets = targets.filter(
|
|
617
|
+
(target) => target.type === "cargo" && target.runtimeFacingCargo,
|
|
618
|
+
);
|
|
235
619
|
const availableTargets = targets.length;
|
|
236
620
|
if (
|
|
237
621
|
typeof selectorOptions.maxTargets === "number" &&
|
|
@@ -246,7 +630,12 @@ export function collectAuditTargets(inputBoms, options) {
|
|
|
246
630
|
skipped,
|
|
247
631
|
stats: {
|
|
248
632
|
availableTargets,
|
|
633
|
+
directRuntimeTargets: directRuntimeTargets.length,
|
|
634
|
+
buildOnlyWorkspaceTargets: buildOnlyWorkspaceTargets.length,
|
|
635
|
+
cargoRuntimeFacingTargets: cargoRuntimeFacingTargets.length,
|
|
636
|
+
developmentOnlyTargets: developmentOnlyTargets.length,
|
|
249
637
|
nonRequiredTargets: nonRequiredTargets.length,
|
|
638
|
+
platformSpecificTargets: platformSpecificTargets.length,
|
|
250
639
|
requiredTargets: requiredTargets.length,
|
|
251
640
|
trustedTargets: trustedTargets.length,
|
|
252
641
|
trustedTargetsExcluded:
|