@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/helpers/utils.js
CHANGED
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
} from "./pylockutils.js";
|
|
78
78
|
import { get_python_command_from_env, getVenvMetadata } from "./pythonutils.js";
|
|
79
79
|
import {
|
|
80
|
+
collectCargoRegistryProvenanceProperties,
|
|
80
81
|
collectNpmRegistryProvenanceProperties,
|
|
81
82
|
collectPypiRegistryProvenanceProperties,
|
|
82
83
|
} from "./registryProvenance.js";
|
|
@@ -95,6 +96,15 @@ export const isSecureMode =
|
|
|
95
96
|
["true", "1"].includes(process.env?.CDXGEN_SECURE_MODE) ||
|
|
96
97
|
process.env?.NODE_OPTIONS?.includes("--permission");
|
|
97
98
|
|
|
99
|
+
// CLI dry-run must be detected during module initialization because some probes
|
|
100
|
+
// execute while modules are imported, before bin/cdxgen.js can thread options.
|
|
101
|
+
const hasDryRunArg = process.argv?.some(
|
|
102
|
+
(arg) =>
|
|
103
|
+
arg === "--dry-run" || arg === "--dry-run=true" || arg === "--dry-run=1",
|
|
104
|
+
);
|
|
105
|
+
export let isDryRun =
|
|
106
|
+
["true", "1"].includes(process.env?.CDXGEN_DRY_RUN) || hasDryRunArg;
|
|
107
|
+
|
|
98
108
|
export const isNode = globalThis.process?.versions?.node !== undefined;
|
|
99
109
|
export const isBun = globalThis.Bun?.version !== undefined;
|
|
100
110
|
export const isDeno = globalThis.Deno?.version?.deno !== undefined;
|
|
@@ -102,6 +112,104 @@ export const isDeno = globalThis.Deno?.version?.deno !== undefined;
|
|
|
102
112
|
export const isWin = platform() === "win32";
|
|
103
113
|
export const isMac = platform() === "darwin";
|
|
104
114
|
|
|
115
|
+
export const DRY_RUN_ERROR_CODE = "CDXGEN_DRY_RUN";
|
|
116
|
+
const activityLedger = [];
|
|
117
|
+
let activityCounter = 0;
|
|
118
|
+
let currentActivityContext = {};
|
|
119
|
+
|
|
120
|
+
export function setDryRunMode(enabled) {
|
|
121
|
+
isDryRun = !!enabled;
|
|
122
|
+
if (enabled) {
|
|
123
|
+
process.env.CDXGEN_DRY_RUN = "true";
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
delete process.env.CDXGEN_DRY_RUN;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createDryRunError(action, target, reason) {
|
|
130
|
+
const message =
|
|
131
|
+
reason || `Dry run mode blocked the attempted ${action} operation.`;
|
|
132
|
+
const error = new Error(message);
|
|
133
|
+
error.code = DRY_RUN_ERROR_CODE;
|
|
134
|
+
error.name = "DryRunError";
|
|
135
|
+
error.action = action;
|
|
136
|
+
error.target = target;
|
|
137
|
+
error.dryRun = true;
|
|
138
|
+
return error;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function isDryRunError(error) {
|
|
142
|
+
return !!(error?.dryRun || error?.code === DRY_RUN_ERROR_CODE);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function setActivityContext(context = {}) {
|
|
146
|
+
currentActivityContext = {
|
|
147
|
+
...currentActivityContext,
|
|
148
|
+
...context,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function resetActivityContext() {
|
|
153
|
+
currentActivityContext = {};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function recordActivity(activity) {
|
|
157
|
+
if (!(isDryRun || DEBUG_MODE)) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
const identifier = `ACT-${String(++activityCounter).padStart(4, "0")}`;
|
|
161
|
+
const entry = {
|
|
162
|
+
identifier,
|
|
163
|
+
timestamp: new Date().toISOString(),
|
|
164
|
+
projectType: currentActivityContext.projectType,
|
|
165
|
+
packageType: currentActivityContext.packageType,
|
|
166
|
+
sourcePath: currentActivityContext.sourcePath,
|
|
167
|
+
...activity,
|
|
168
|
+
};
|
|
169
|
+
activityLedger.push(entry);
|
|
170
|
+
traceLog("activity", entry);
|
|
171
|
+
return entry;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function getRecordedActivities() {
|
|
175
|
+
return [...activityLedger];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function resetRecordedActivities() {
|
|
179
|
+
activityLedger.length = 0;
|
|
180
|
+
activityCounter = 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function recordFilesystemActivity(kind, target, status, reason = undefined) {
|
|
184
|
+
return recordActivity({
|
|
185
|
+
kind,
|
|
186
|
+
reason,
|
|
187
|
+
status,
|
|
188
|
+
target,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function hasReadPermission(filePath) {
|
|
193
|
+
if (!(isSecureMode && process.permission)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return process.permission.has("fs.read", join(filePath, "", "*"));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function hasWritePermission(filePath) {
|
|
200
|
+
if (!(isSecureMode && process.permission)) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
const candidatePaths = [
|
|
204
|
+
filePath,
|
|
205
|
+
join(filePath, "", "*"),
|
|
206
|
+
join(dirname(filePath), "*"),
|
|
207
|
+
];
|
|
208
|
+
return candidatePaths.some((candidatePath) =>
|
|
209
|
+
process.permission.has("fs.write", candidatePath),
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
105
213
|
/**
|
|
106
214
|
* Safely check if a file path exists without crashing due to a lack of permissions
|
|
107
215
|
*
|
|
@@ -109,17 +217,42 @@ export const isMac = platform() === "darwin";
|
|
|
109
217
|
* @Boolean True if the path exists. False otherwise
|
|
110
218
|
*/
|
|
111
219
|
export function safeExistsSync(filePath) {
|
|
112
|
-
if (
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
console.log(`cdxgen lacks read permission for: ${filePath}`);
|
|
116
|
-
}
|
|
117
|
-
return false;
|
|
220
|
+
if (!hasReadPermission(filePath)) {
|
|
221
|
+
if (DEBUG_MODE) {
|
|
222
|
+
console.log("cdxgen lacks read permission for a requested path.");
|
|
118
223
|
}
|
|
224
|
+
return false;
|
|
119
225
|
}
|
|
120
226
|
return existsSync(filePath);
|
|
121
227
|
}
|
|
122
228
|
|
|
229
|
+
export function safeWriteSync(filePath, data, options) {
|
|
230
|
+
if (isDryRun) {
|
|
231
|
+
recordFilesystemActivity(
|
|
232
|
+
"write",
|
|
233
|
+
filePath,
|
|
234
|
+
"blocked",
|
|
235
|
+
"Dry run mode blocks filesystem writes.",
|
|
236
|
+
);
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
if (!hasWritePermission(filePath)) {
|
|
240
|
+
if (DEBUG_MODE) {
|
|
241
|
+
console.log("cdxgen lacks write permission for a requested path.");
|
|
242
|
+
}
|
|
243
|
+
recordFilesystemActivity(
|
|
244
|
+
"write",
|
|
245
|
+
filePath,
|
|
246
|
+
"blocked",
|
|
247
|
+
"cdxgen lacks write permission for this path.",
|
|
248
|
+
);
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
const result = writeFileSync(filePath, data, options);
|
|
252
|
+
recordFilesystemActivity("write", filePath, "completed");
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
123
256
|
/**
|
|
124
257
|
* Safely create a directory without crashing due to a lack of permissions
|
|
125
258
|
*
|
|
@@ -128,15 +261,118 @@ export function safeExistsSync(filePath) {
|
|
|
128
261
|
* @Boolean True if the path exists. False otherwise
|
|
129
262
|
*/
|
|
130
263
|
export function safeMkdirSync(filePath, options) {
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
264
|
+
if (isDryRun) {
|
|
265
|
+
recordFilesystemActivity(
|
|
266
|
+
"mkdir",
|
|
267
|
+
filePath,
|
|
268
|
+
"blocked",
|
|
269
|
+
"Dry run mode blocks directory creation.",
|
|
270
|
+
);
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
if (!hasWritePermission(filePath)) {
|
|
274
|
+
if (DEBUG_MODE) {
|
|
275
|
+
console.log("cdxgen lacks write permission for a requested path.");
|
|
137
276
|
}
|
|
277
|
+
recordFilesystemActivity(
|
|
278
|
+
"mkdir",
|
|
279
|
+
filePath,
|
|
280
|
+
"blocked",
|
|
281
|
+
"cdxgen lacks write permission for this path.",
|
|
282
|
+
);
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
const result = mkdirSync(filePath, options);
|
|
286
|
+
recordFilesystemActivity("mkdir", filePath, "completed");
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function safeMkdtempSync(prefix, options = undefined) {
|
|
291
|
+
if (isDryRun) {
|
|
292
|
+
const tempPath = `${prefix}${randomUUID().replaceAll("-", "").slice(0, 6)}`;
|
|
293
|
+
recordFilesystemActivity(
|
|
294
|
+
"temp-dir",
|
|
295
|
+
tempPath,
|
|
296
|
+
"blocked",
|
|
297
|
+
"Dry run mode blocks temporary directory creation.",
|
|
298
|
+
);
|
|
299
|
+
return tempPath;
|
|
300
|
+
}
|
|
301
|
+
const tempPath = mkdtempSync(prefix, options);
|
|
302
|
+
recordFilesystemActivity("temp-dir", tempPath, "completed");
|
|
303
|
+
return tempPath;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function safeRmSync(filePath, options = undefined) {
|
|
307
|
+
if (isDryRun) {
|
|
308
|
+
recordFilesystemActivity(
|
|
309
|
+
"cleanup",
|
|
310
|
+
filePath,
|
|
311
|
+
"blocked",
|
|
312
|
+
"Dry run mode blocks filesystem deletions.",
|
|
313
|
+
);
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
const result = rmSync(filePath, options);
|
|
317
|
+
recordFilesystemActivity("cleanup", filePath, "completed");
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export function safeUnlinkSync(filePath) {
|
|
322
|
+
if (isDryRun) {
|
|
323
|
+
recordFilesystemActivity(
|
|
324
|
+
"cleanup",
|
|
325
|
+
filePath,
|
|
326
|
+
"blocked",
|
|
327
|
+
"Dry run mode blocks file deletions.",
|
|
328
|
+
);
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
const result = unlinkSync(filePath);
|
|
332
|
+
recordFilesystemActivity("cleanup", filePath, "completed");
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function safeCopyFileSync(src, dest, mode = undefined) {
|
|
337
|
+
if (isDryRun) {
|
|
338
|
+
recordFilesystemActivity(
|
|
339
|
+
"write",
|
|
340
|
+
dest,
|
|
341
|
+
"blocked",
|
|
342
|
+
`Dry run mode blocks copying files from ${src}.`,
|
|
343
|
+
);
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
const result =
|
|
347
|
+
mode === undefined
|
|
348
|
+
? copyFileSync(src, dest)
|
|
349
|
+
: copyFileSync(src, dest, mode);
|
|
350
|
+
recordFilesystemActivity("write", dest, "completed", `Copied from ${src}.`);
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export async function safeExtractArchive(
|
|
355
|
+
sourcePath,
|
|
356
|
+
targetPath,
|
|
357
|
+
extractor,
|
|
358
|
+
kind = "unzip",
|
|
359
|
+
) {
|
|
360
|
+
if (isDryRun) {
|
|
361
|
+
recordActivity({
|
|
362
|
+
kind,
|
|
363
|
+
reason: "Dry run mode blocks archive extraction and decompression.",
|
|
364
|
+
status: "blocked",
|
|
365
|
+
target: `${sourcePath} -> ${targetPath}`,
|
|
366
|
+
});
|
|
367
|
+
return false;
|
|
138
368
|
}
|
|
139
|
-
|
|
369
|
+
await extractor();
|
|
370
|
+
recordActivity({
|
|
371
|
+
kind,
|
|
372
|
+
status: "completed",
|
|
373
|
+
target: `${sourcePath} -> ${targetPath}`,
|
|
374
|
+
});
|
|
375
|
+
return true;
|
|
140
376
|
}
|
|
141
377
|
|
|
142
378
|
export const commandsExecuted = new Set();
|
|
@@ -203,6 +439,25 @@ function isWindowsShellHijackRisk(command, options) {
|
|
|
203
439
|
* @returns {Object} spawnSync result object with status, stdout, stderr, and error fields
|
|
204
440
|
*/
|
|
205
441
|
export function safeSpawnSync(command, args, options) {
|
|
442
|
+
if (isDryRun) {
|
|
443
|
+
const error = createDryRunError(
|
|
444
|
+
"execute",
|
|
445
|
+
command,
|
|
446
|
+
"Dry run mode blocks child process execution.",
|
|
447
|
+
);
|
|
448
|
+
recordActivity({
|
|
449
|
+
kind: "execute",
|
|
450
|
+
reason: error.message,
|
|
451
|
+
status: "blocked",
|
|
452
|
+
target: `${command}${args?.length ? ` ${args.join(" ")}` : ""}`,
|
|
453
|
+
});
|
|
454
|
+
return {
|
|
455
|
+
status: 1,
|
|
456
|
+
stdout: undefined,
|
|
457
|
+
stderr: undefined,
|
|
458
|
+
error,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
206
461
|
if (
|
|
207
462
|
(isSecureMode && process.permission && !process.permission.has("child")) ||
|
|
208
463
|
!isAllowedCommand(command)
|
|
@@ -210,6 +465,12 @@ export function safeSpawnSync(command, args, options) {
|
|
|
210
465
|
if (DEBUG_MODE) {
|
|
211
466
|
console.log(`cdxgen lacks execute permission for ${command}`);
|
|
212
467
|
}
|
|
468
|
+
recordActivity({
|
|
469
|
+
kind: "execute",
|
|
470
|
+
reason: "cdxgen lacks execute permission for this command.",
|
|
471
|
+
status: "blocked",
|
|
472
|
+
target: `${command}${args?.length ? ` ${args.join(" ")}` : ""}`,
|
|
473
|
+
});
|
|
213
474
|
return {
|
|
214
475
|
status: 1,
|
|
215
476
|
stdout: undefined,
|
|
@@ -221,6 +482,12 @@ export function safeSpawnSync(command, args, options) {
|
|
|
221
482
|
if (isWindowsShellHijackRisk(command, options)) {
|
|
222
483
|
const blockedReason = `${command} matches local file in cwd (Windows shell hijack risk)`;
|
|
223
484
|
console.warn(`\x1b[1;31mSecurity Alert: ${blockedReason}\x1b[0m`);
|
|
485
|
+
recordActivity({
|
|
486
|
+
kind: "execute",
|
|
487
|
+
reason: blockedReason,
|
|
488
|
+
status: "blocked",
|
|
489
|
+
target: `${command}${args?.length ? ` ${args.join(" ")}` : ""}`,
|
|
490
|
+
});
|
|
224
491
|
return {
|
|
225
492
|
status: 1,
|
|
226
493
|
stdout: undefined,
|
|
@@ -335,7 +602,14 @@ export function safeSpawnSync(command, args, options) {
|
|
|
335
602
|
args = undefined;
|
|
336
603
|
}
|
|
337
604
|
}
|
|
338
|
-
|
|
605
|
+
const result = spawnSync(command, args, options);
|
|
606
|
+
recordActivity({
|
|
607
|
+
kind: "execute",
|
|
608
|
+
reason: result.error?.message,
|
|
609
|
+
status: result.status === 0 && !result.error ? "completed" : "failed",
|
|
610
|
+
target: `${command}${args?.length ? ` ${args.join(" ")}` : ""}`,
|
|
611
|
+
});
|
|
612
|
+
return result;
|
|
339
613
|
}
|
|
340
614
|
|
|
341
615
|
const licenseMapping = JSON.parse(
|
|
@@ -413,7 +687,7 @@ const temporaryFiles = new Set();
|
|
|
413
687
|
process.on("exit", () =>
|
|
414
688
|
temporaryFiles.forEach((tempFile) => {
|
|
415
689
|
if (safeExistsSync(tempFile)) {
|
|
416
|
-
|
|
690
|
+
safeUnlinkSync(tempFile);
|
|
417
691
|
}
|
|
418
692
|
}),
|
|
419
693
|
);
|
|
@@ -620,6 +894,7 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
620
894
|
"gradle-index": ["gradle-index", "gradle-cache"],
|
|
621
895
|
"sbt-index": ["sbt-index", "sbt-cache"],
|
|
622
896
|
"maven-index": ["maven-index", "maven-cache", "maven-core"],
|
|
897
|
+
"cargo-cache": ["cargo-cache", "cargo-index"],
|
|
623
898
|
js: [
|
|
624
899
|
"npm",
|
|
625
900
|
"pnpm",
|
|
@@ -651,6 +926,8 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
651
926
|
"yarn",
|
|
652
927
|
"rush",
|
|
653
928
|
],
|
|
929
|
+
mcp: ["mcp"],
|
|
930
|
+
"ai-skill": ["ai-skill", "skill", "skills"],
|
|
654
931
|
py: [
|
|
655
932
|
"py",
|
|
656
933
|
"python",
|
|
@@ -950,10 +1227,36 @@ export const cdxgenAgent = got.extend({
|
|
|
950
1227
|
hooks: {
|
|
951
1228
|
beforeRequest: [
|
|
952
1229
|
(options) => {
|
|
1230
|
+
options.context = {
|
|
1231
|
+
...options.context,
|
|
1232
|
+
activityTarget: options.url.toString(),
|
|
1233
|
+
};
|
|
1234
|
+
if (isDryRun) {
|
|
1235
|
+
const error = createDryRunError(
|
|
1236
|
+
"network",
|
|
1237
|
+
options.url.toString(),
|
|
1238
|
+
"Dry run mode blocks outbound network access.",
|
|
1239
|
+
);
|
|
1240
|
+
recordActivity({
|
|
1241
|
+
kind: "network",
|
|
1242
|
+
reason: error.message,
|
|
1243
|
+
status: "blocked",
|
|
1244
|
+
target: options.url.toString(),
|
|
1245
|
+
});
|
|
1246
|
+
options.context.activityBlocked = true;
|
|
1247
|
+
throw error;
|
|
1248
|
+
}
|
|
953
1249
|
if (!isAllowedHost(options.url.hostname)) {
|
|
954
1250
|
console.log(
|
|
955
1251
|
`Access to the remote host '${options.url.hostname}' is not permitted.`,
|
|
956
1252
|
);
|
|
1253
|
+
recordActivity({
|
|
1254
|
+
kind: "network",
|
|
1255
|
+
reason: "The remote host is not permitted.",
|
|
1256
|
+
status: "blocked",
|
|
1257
|
+
target: options.url.toString(),
|
|
1258
|
+
});
|
|
1259
|
+
options.context.activityBlocked = true;
|
|
957
1260
|
return new AbortController().abort();
|
|
958
1261
|
}
|
|
959
1262
|
// Only allow https protocol in secure mode
|
|
@@ -961,6 +1264,13 @@ export const cdxgenAgent = got.extend({
|
|
|
961
1264
|
console.log(
|
|
962
1265
|
`Access to the remote host '${options.url.hostname}' is not permitted via the '${options.url.protocol}' protocol.`,
|
|
963
1266
|
);
|
|
1267
|
+
recordActivity({
|
|
1268
|
+
kind: "network",
|
|
1269
|
+
reason: `The '${options.url.protocol}' protocol is not permitted in secure mode.`,
|
|
1270
|
+
status: "blocked",
|
|
1271
|
+
target: options.url.toString(),
|
|
1272
|
+
});
|
|
1273
|
+
options.context.activityBlocked = true;
|
|
964
1274
|
return new AbortController().abort();
|
|
965
1275
|
}
|
|
966
1276
|
remoteHostsAccessed.add(options.url.hostname);
|
|
@@ -971,6 +1281,39 @@ export const cdxgenAgent = got.extend({
|
|
|
971
1281
|
});
|
|
972
1282
|
},
|
|
973
1283
|
],
|
|
1284
|
+
afterResponse: [
|
|
1285
|
+
(response) => {
|
|
1286
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
1287
|
+
return response;
|
|
1288
|
+
}
|
|
1289
|
+
const activityTarget =
|
|
1290
|
+
response.request.options.context?.activityTarget ||
|
|
1291
|
+
response.request.options.url?.toString() ||
|
|
1292
|
+
response.url;
|
|
1293
|
+
recordActivity({
|
|
1294
|
+
kind: "network",
|
|
1295
|
+
status: "completed",
|
|
1296
|
+
target: activityTarget,
|
|
1297
|
+
});
|
|
1298
|
+
return response;
|
|
1299
|
+
},
|
|
1300
|
+
],
|
|
1301
|
+
beforeError: [
|
|
1302
|
+
(error) => {
|
|
1303
|
+
if (error.options?.context?.activityBlocked) {
|
|
1304
|
+
return error;
|
|
1305
|
+
}
|
|
1306
|
+
recordActivity({
|
|
1307
|
+
kind: "network",
|
|
1308
|
+
reason: error.message,
|
|
1309
|
+
status: "failed",
|
|
1310
|
+
target:
|
|
1311
|
+
error.options?.context?.activityTarget ||
|
|
1312
|
+
error.options?.url?.toString(),
|
|
1313
|
+
});
|
|
1314
|
+
return error;
|
|
1315
|
+
},
|
|
1316
|
+
],
|
|
974
1317
|
},
|
|
975
1318
|
});
|
|
976
1319
|
|
|
@@ -9751,12 +10094,24 @@ export async function getCratesMetadata(pkgList) {
|
|
|
9751
10094
|
const cdepList = [];
|
|
9752
10095
|
for (const p of pkgList) {
|
|
9753
10096
|
try {
|
|
10097
|
+
if (!p?.name || !p?.version || p.version === "workspace") {
|
|
10098
|
+
cdepList.push(p);
|
|
10099
|
+
continue;
|
|
10100
|
+
}
|
|
9754
10101
|
if (DEBUG_MODE) {
|
|
9755
10102
|
console.log(`Querying crates.io for ${p.name}@${p.version}`);
|
|
9756
10103
|
}
|
|
9757
10104
|
const res = await cdxgenAgent.get(CRATES_URL + p.name, {
|
|
9758
10105
|
responseType: "json",
|
|
9759
10106
|
});
|
|
10107
|
+
let ownersRes;
|
|
10108
|
+
try {
|
|
10109
|
+
ownersRes = await cdxgenAgent.get(`${CRATES_URL + p.name}/owners`, {
|
|
10110
|
+
responseType: "json",
|
|
10111
|
+
});
|
|
10112
|
+
} catch (_err) {
|
|
10113
|
+
ownersRes = undefined;
|
|
10114
|
+
}
|
|
9760
10115
|
let versionToUse = res?.body?.versions[0];
|
|
9761
10116
|
if (p.version) {
|
|
9762
10117
|
for (const aversion of res.body.versions) {
|
|
@@ -9782,7 +10137,7 @@ export async function getCratesMetadata(pkgList) {
|
|
|
9782
10137
|
p.version = body.newest_version;
|
|
9783
10138
|
}
|
|
9784
10139
|
if (!p._integrity && versionToUse.checksum) {
|
|
9785
|
-
p._integrity =
|
|
10140
|
+
p._integrity = normalizeCargoIntegrity(versionToUse.checksum);
|
|
9786
10141
|
}
|
|
9787
10142
|
if (!p.properties) {
|
|
9788
10143
|
p.properties = [];
|
|
@@ -9808,6 +10163,13 @@ export async function getCratesMetadata(pkgList) {
|
|
|
9808
10163
|
value: JSON.stringify(versionToUse.features),
|
|
9809
10164
|
});
|
|
9810
10165
|
}
|
|
10166
|
+
p.properties = p.properties.concat(
|
|
10167
|
+
collectCargoRegistryProvenanceProperties(
|
|
10168
|
+
res?.body,
|
|
10169
|
+
versionToUse?.num || p.version,
|
|
10170
|
+
ownersRes?.body,
|
|
10171
|
+
),
|
|
10172
|
+
);
|
|
9811
10173
|
cdepList.push(p);
|
|
9812
10174
|
} catch (_err) {
|
|
9813
10175
|
cdepList.push(p);
|
|
@@ -9926,7 +10288,10 @@ function parseCargoDependencyFromPackageNode(packageNode) {
|
|
|
9926
10288
|
}
|
|
9927
10289
|
|
|
9928
10290
|
if (!isExtendFromWorkspace(pkgChecksum) && pkgChecksum) {
|
|
9929
|
-
|
|
10291
|
+
const normalizedCargoIntegrity = normalizeCargoIntegrity(pkgChecksum);
|
|
10292
|
+
if (normalizedCargoIntegrity) {
|
|
10293
|
+
pkg._integrity = normalizedCargoIntegrity;
|
|
10294
|
+
}
|
|
9930
10295
|
}
|
|
9931
10296
|
if (!isExtendFromWorkspace(pkgName) && pkgName) {
|
|
9932
10297
|
pkg.group = group;
|
|
@@ -9957,6 +10322,555 @@ function parseCargoDependencyFromPackageNode(packageNode) {
|
|
|
9957
10322
|
return pkg;
|
|
9958
10323
|
}
|
|
9959
10324
|
|
|
10325
|
+
function normalizeCargoIntegrity(integrity) {
|
|
10326
|
+
if (typeof integrity !== "string") {
|
|
10327
|
+
return undefined;
|
|
10328
|
+
}
|
|
10329
|
+
const normalizedIntegrity = integrity.trim().toLowerCase();
|
|
10330
|
+
const prefixedMatch = /^(sha256|sha384)-(?<digest>[a-f0-9]+)$/i.exec(
|
|
10331
|
+
normalizedIntegrity,
|
|
10332
|
+
);
|
|
10333
|
+
if (prefixedMatch?.groups?.digest) {
|
|
10334
|
+
const algorithm = prefixedMatch[1].toLowerCase();
|
|
10335
|
+
const digest = prefixedMatch.groups.digest;
|
|
10336
|
+
const expectedDigestLength = algorithm === "sha384" ? 96 : 64;
|
|
10337
|
+
if (digest.length === expectedDigestLength) {
|
|
10338
|
+
return `${algorithm}-${digest}`;
|
|
10339
|
+
}
|
|
10340
|
+
return undefined;
|
|
10341
|
+
}
|
|
10342
|
+
if (!/^[a-f0-9]+$/i.test(normalizedIntegrity)) {
|
|
10343
|
+
return undefined;
|
|
10344
|
+
}
|
|
10345
|
+
if (normalizedIntegrity.length === 64) {
|
|
10346
|
+
return `sha256-${normalizedIntegrity}`;
|
|
10347
|
+
}
|
|
10348
|
+
if (normalizedIntegrity.length === 96) {
|
|
10349
|
+
return `sha384-${normalizedIntegrity}`;
|
|
10350
|
+
}
|
|
10351
|
+
return undefined;
|
|
10352
|
+
}
|
|
10353
|
+
|
|
10354
|
+
function cargoIntegrityToComponentHash(integrity) {
|
|
10355
|
+
const normalizedIntegrity = normalizeCargoIntegrity(integrity);
|
|
10356
|
+
if (!normalizedIntegrity) {
|
|
10357
|
+
return undefined;
|
|
10358
|
+
}
|
|
10359
|
+
const [, algorithm, digest] = /^(sha256|sha384)-([a-f0-9]+)$/i.exec(
|
|
10360
|
+
normalizedIntegrity,
|
|
10361
|
+
);
|
|
10362
|
+
return {
|
|
10363
|
+
alg: algorithm.toLowerCase() === "sha384" ? "SHA-384" : "SHA-256",
|
|
10364
|
+
content: digest,
|
|
10365
|
+
};
|
|
10366
|
+
}
|
|
10367
|
+
|
|
10368
|
+
function readCargoTomlData(cargoTomlFile) {
|
|
10369
|
+
if (!cargoTomlFile || !safeExistsSync(cargoTomlFile)) {
|
|
10370
|
+
return undefined;
|
|
10371
|
+
}
|
|
10372
|
+
try {
|
|
10373
|
+
return toml.parse(readFileSync(cargoTomlFile, { encoding: "utf-8" }));
|
|
10374
|
+
} catch (error) {
|
|
10375
|
+
traceLog("cargo", {
|
|
10376
|
+
cargoTomlFile,
|
|
10377
|
+
error: error.message,
|
|
10378
|
+
});
|
|
10379
|
+
if (DEBUG_MODE) {
|
|
10380
|
+
console.warn(`Failed to parse Cargo manifest ${cargoTomlFile}:`, error);
|
|
10381
|
+
}
|
|
10382
|
+
return undefined;
|
|
10383
|
+
}
|
|
10384
|
+
}
|
|
10385
|
+
|
|
10386
|
+
function isCargoWorkspaceReference(value) {
|
|
10387
|
+
return Boolean(value?.workspace);
|
|
10388
|
+
}
|
|
10389
|
+
|
|
10390
|
+
function cargoPackageInfoToPurl(pkg) {
|
|
10391
|
+
return decodeURIComponent(
|
|
10392
|
+
new PackageURL(
|
|
10393
|
+
"cargo",
|
|
10394
|
+
pkg?.group,
|
|
10395
|
+
pkg?.name,
|
|
10396
|
+
pkg?.version,
|
|
10397
|
+
null,
|
|
10398
|
+
null,
|
|
10399
|
+
).toString(),
|
|
10400
|
+
);
|
|
10401
|
+
}
|
|
10402
|
+
|
|
10403
|
+
function resolveCargoDependencyAliasName(dependencyName, dependencyNode) {
|
|
10404
|
+
if (
|
|
10405
|
+
dependencyNode &&
|
|
10406
|
+
typeof dependencyNode === "object" &&
|
|
10407
|
+
typeof dependencyNode.package === "string" &&
|
|
10408
|
+
dependencyNode.package
|
|
10409
|
+
) {
|
|
10410
|
+
return dependencyNode.package;
|
|
10411
|
+
}
|
|
10412
|
+
return dependencyName;
|
|
10413
|
+
}
|
|
10414
|
+
|
|
10415
|
+
function resolveCargoWorkspaceContext(cargoTomlFile, cargoData, context = {}) {
|
|
10416
|
+
if (
|
|
10417
|
+
context?.workspaceRootFile &&
|
|
10418
|
+
context?.workspaceRootData &&
|
|
10419
|
+
safeExistsSync(context.workspaceRootFile)
|
|
10420
|
+
) {
|
|
10421
|
+
return {
|
|
10422
|
+
isVirtualWorkspace: !context.workspaceRootData?.package,
|
|
10423
|
+
isWorkspaceRoot: context.workspaceRootFile === cargoTomlFile,
|
|
10424
|
+
workspaceData: context.workspaceRootData.workspace,
|
|
10425
|
+
workspaceRootData: context.workspaceRootData,
|
|
10426
|
+
workspaceRootFile: context.workspaceRootFile,
|
|
10427
|
+
};
|
|
10428
|
+
}
|
|
10429
|
+
let currentDir = dirname(cargoTomlFile);
|
|
10430
|
+
while (currentDir && currentDir !== dirname(currentDir)) {
|
|
10431
|
+
const candidateFile = join(currentDir, "Cargo.toml");
|
|
10432
|
+
if (safeExistsSync(candidateFile)) {
|
|
10433
|
+
const candidateData =
|
|
10434
|
+
candidateFile === cargoTomlFile
|
|
10435
|
+
? cargoData
|
|
10436
|
+
: readCargoTomlData(candidateFile);
|
|
10437
|
+
if (candidateData?.workspace) {
|
|
10438
|
+
return {
|
|
10439
|
+
isVirtualWorkspace: !candidateData?.package,
|
|
10440
|
+
isWorkspaceRoot: candidateFile === cargoTomlFile,
|
|
10441
|
+
workspaceData: candidateData.workspace,
|
|
10442
|
+
workspaceRootData: candidateData,
|
|
10443
|
+
workspaceRootFile: candidateFile,
|
|
10444
|
+
};
|
|
10445
|
+
}
|
|
10446
|
+
}
|
|
10447
|
+
const parentDir = dirname(currentDir);
|
|
10448
|
+
if (parentDir === currentDir) {
|
|
10449
|
+
break;
|
|
10450
|
+
}
|
|
10451
|
+
currentDir = parentDir;
|
|
10452
|
+
}
|
|
10453
|
+
return {};
|
|
10454
|
+
}
|
|
10455
|
+
|
|
10456
|
+
function resolveCargoWorkspaceMembers(workspaceRootFile, workspaceData) {
|
|
10457
|
+
const workspaceRootDir = dirname(workspaceRootFile);
|
|
10458
|
+
const members = [];
|
|
10459
|
+
const excludedRoots = new Set();
|
|
10460
|
+
for (const excludedPattern of workspaceData?.exclude || []) {
|
|
10461
|
+
excludedRoots.add(resolve(workspaceRootDir, excludedPattern));
|
|
10462
|
+
}
|
|
10463
|
+
for (const memberPattern of workspaceData?.members || []) {
|
|
10464
|
+
const directMemberFile = resolve(
|
|
10465
|
+
workspaceRootDir,
|
|
10466
|
+
memberPattern,
|
|
10467
|
+
"Cargo.toml",
|
|
10468
|
+
);
|
|
10469
|
+
if (safeExistsSync(directMemberFile)) {
|
|
10470
|
+
members.push(directMemberFile);
|
|
10471
|
+
continue;
|
|
10472
|
+
}
|
|
10473
|
+
const matchedMemberFiles = globSync(
|
|
10474
|
+
join(memberPattern, "Cargo.toml").replaceAll("\\", "/"),
|
|
10475
|
+
{
|
|
10476
|
+
absolute: true,
|
|
10477
|
+
cwd: workspaceRootDir,
|
|
10478
|
+
nodir: true,
|
|
10479
|
+
windowsPathsNoEscape: true,
|
|
10480
|
+
},
|
|
10481
|
+
);
|
|
10482
|
+
if (matchedMemberFiles?.length) {
|
|
10483
|
+
members.push(...matchedMemberFiles);
|
|
10484
|
+
}
|
|
10485
|
+
}
|
|
10486
|
+
return [...new Set(members)]
|
|
10487
|
+
.filter((memberFile) => {
|
|
10488
|
+
const memberDir = resolve(dirname(memberFile));
|
|
10489
|
+
for (const excludedRoot of excludedRoots) {
|
|
10490
|
+
if (
|
|
10491
|
+
memberDir === excludedRoot ||
|
|
10492
|
+
memberDir.startsWith(`${excludedRoot}${_sep}`)
|
|
10493
|
+
) {
|
|
10494
|
+
return false;
|
|
10495
|
+
}
|
|
10496
|
+
}
|
|
10497
|
+
return true;
|
|
10498
|
+
})
|
|
10499
|
+
.sort();
|
|
10500
|
+
}
|
|
10501
|
+
|
|
10502
|
+
function resolveCargoWorkspacePackageNode(packageNode, workspacePackageNode) {
|
|
10503
|
+
if (!packageNode || typeof packageNode !== "object") {
|
|
10504
|
+
return packageNode;
|
|
10505
|
+
}
|
|
10506
|
+
const mergedNode = { ...packageNode };
|
|
10507
|
+
for (const fieldName of [
|
|
10508
|
+
"authors",
|
|
10509
|
+
"description",
|
|
10510
|
+
"documentation",
|
|
10511
|
+
"edition",
|
|
10512
|
+
"homepage",
|
|
10513
|
+
"keywords",
|
|
10514
|
+
"license",
|
|
10515
|
+
"name",
|
|
10516
|
+
"readme",
|
|
10517
|
+
"repository",
|
|
10518
|
+
"rust-version",
|
|
10519
|
+
"version",
|
|
10520
|
+
]) {
|
|
10521
|
+
if (
|
|
10522
|
+
isCargoWorkspaceReference(packageNode[fieldName]) &&
|
|
10523
|
+
workspacePackageNode?.[fieldName] !== undefined
|
|
10524
|
+
) {
|
|
10525
|
+
mergedNode[fieldName] = workspacePackageNode[fieldName];
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
return mergedNode;
|
|
10529
|
+
}
|
|
10530
|
+
|
|
10531
|
+
function resolveCargoManifestPackageIdentity(
|
|
10532
|
+
cargoTomlFile,
|
|
10533
|
+
cargoData,
|
|
10534
|
+
context = {},
|
|
10535
|
+
) {
|
|
10536
|
+
const workspaceContext = resolveCargoWorkspaceContext(
|
|
10537
|
+
cargoTomlFile,
|
|
10538
|
+
cargoData,
|
|
10539
|
+
context,
|
|
10540
|
+
);
|
|
10541
|
+
const resolvedPackageNode = resolveCargoWorkspacePackageNode(
|
|
10542
|
+
cargoData?.package,
|
|
10543
|
+
workspaceContext?.workspaceData?.package,
|
|
10544
|
+
);
|
|
10545
|
+
if (
|
|
10546
|
+
resolvedPackageNode &&
|
|
10547
|
+
typeof resolvedPackageNode === "object" &&
|
|
10548
|
+
!Array.isArray(resolvedPackageNode)
|
|
10549
|
+
) {
|
|
10550
|
+
try {
|
|
10551
|
+
return parseCargoDependencyFromPackageNode(resolvedPackageNode);
|
|
10552
|
+
} catch {
|
|
10553
|
+
return undefined;
|
|
10554
|
+
}
|
|
10555
|
+
}
|
|
10556
|
+
if (
|
|
10557
|
+
cargoData?.workspace &&
|
|
10558
|
+
workspaceContext?.isWorkspaceRoot &&
|
|
10559
|
+
workspaceContext?.isVirtualWorkspace
|
|
10560
|
+
) {
|
|
10561
|
+
return {
|
|
10562
|
+
group: "",
|
|
10563
|
+
name: basename(dirname(cargoTomlFile)),
|
|
10564
|
+
version: "workspace",
|
|
10565
|
+
};
|
|
10566
|
+
}
|
|
10567
|
+
return undefined;
|
|
10568
|
+
}
|
|
10569
|
+
|
|
10570
|
+
function normalizeCargoDependencySpec(dependencySpec) {
|
|
10571
|
+
if (typeof dependencySpec === "string") {
|
|
10572
|
+
return { version: dependencySpec };
|
|
10573
|
+
}
|
|
10574
|
+
if (!dependencySpec || typeof dependencySpec !== "object") {
|
|
10575
|
+
return {};
|
|
10576
|
+
}
|
|
10577
|
+
return { ...dependencySpec };
|
|
10578
|
+
}
|
|
10579
|
+
|
|
10580
|
+
function mergeCargoWorkspaceDependencySpec(
|
|
10581
|
+
dependencyName,
|
|
10582
|
+
dependencyNode,
|
|
10583
|
+
workspaceDependencies,
|
|
10584
|
+
) {
|
|
10585
|
+
if (
|
|
10586
|
+
!dependencyNode ||
|
|
10587
|
+
typeof dependencyNode !== "object" ||
|
|
10588
|
+
dependencyNode.workspace !== true
|
|
10589
|
+
) {
|
|
10590
|
+
return dependencyNode;
|
|
10591
|
+
}
|
|
10592
|
+
const workspaceDependencyNode = workspaceDependencies?.[dependencyName];
|
|
10593
|
+
if (workspaceDependencyNode === undefined) {
|
|
10594
|
+
return dependencyNode;
|
|
10595
|
+
}
|
|
10596
|
+
const mergedSpec = {
|
|
10597
|
+
...normalizeCargoDependencySpec(workspaceDependencyNode),
|
|
10598
|
+
...normalizeCargoDependencySpec(dependencyNode),
|
|
10599
|
+
};
|
|
10600
|
+
mergedSpec.workspace = true;
|
|
10601
|
+
if (
|
|
10602
|
+
Array.isArray(workspaceDependencyNode?.features) ||
|
|
10603
|
+
Array.isArray(dependencyNode?.features)
|
|
10604
|
+
) {
|
|
10605
|
+
mergedSpec.features = [
|
|
10606
|
+
...new Set([
|
|
10607
|
+
...(workspaceDependencyNode?.features || []),
|
|
10608
|
+
...(dependencyNode?.features || []),
|
|
10609
|
+
]),
|
|
10610
|
+
];
|
|
10611
|
+
}
|
|
10612
|
+
return mergedSpec;
|
|
10613
|
+
}
|
|
10614
|
+
|
|
10615
|
+
function resolveCargoWorkspaceMemberMap(
|
|
10616
|
+
workspaceRootFile,
|
|
10617
|
+
workspaceRootData,
|
|
10618
|
+
workspaceMemberCache,
|
|
10619
|
+
) {
|
|
10620
|
+
if (!workspaceRootFile || !workspaceRootData?.workspace) {
|
|
10621
|
+
return new Map();
|
|
10622
|
+
}
|
|
10623
|
+
const cacheKey = resolve(workspaceRootFile);
|
|
10624
|
+
if (workspaceMemberCache?.has(cacheKey)) {
|
|
10625
|
+
return workspaceMemberCache.get(cacheKey);
|
|
10626
|
+
}
|
|
10627
|
+
const memberMap = new Map();
|
|
10628
|
+
const workspaceMemberFiles = resolveCargoWorkspaceMembers(
|
|
10629
|
+
workspaceRootFile,
|
|
10630
|
+
workspaceRootData.workspace,
|
|
10631
|
+
);
|
|
10632
|
+
for (const workspaceMemberFile of workspaceMemberFiles) {
|
|
10633
|
+
const memberCargoData = readCargoTomlData(workspaceMemberFile);
|
|
10634
|
+
if (!memberCargoData) {
|
|
10635
|
+
continue;
|
|
10636
|
+
}
|
|
10637
|
+
const memberIdentity = resolveCargoManifestPackageIdentity(
|
|
10638
|
+
workspaceMemberFile,
|
|
10639
|
+
memberCargoData,
|
|
10640
|
+
{
|
|
10641
|
+
workspaceRootData,
|
|
10642
|
+
workspaceRootFile,
|
|
10643
|
+
},
|
|
10644
|
+
);
|
|
10645
|
+
if (!memberIdentity?.name || !memberIdentity?.version) {
|
|
10646
|
+
continue;
|
|
10647
|
+
}
|
|
10648
|
+
memberMap.set(memberIdentity.name, {
|
|
10649
|
+
...memberIdentity,
|
|
10650
|
+
filePath: workspaceMemberFile,
|
|
10651
|
+
ref: cargoPackageInfoToPurl(memberIdentity),
|
|
10652
|
+
});
|
|
10653
|
+
}
|
|
10654
|
+
workspaceMemberCache?.set(cacheKey, memberMap);
|
|
10655
|
+
return memberMap;
|
|
10656
|
+
}
|
|
10657
|
+
|
|
10658
|
+
function resolveCargoWorkspaceDependencyTarget(
|
|
10659
|
+
cargoTomlFile,
|
|
10660
|
+
dependencyName,
|
|
10661
|
+
dependencyNode,
|
|
10662
|
+
workspaceContext,
|
|
10663
|
+
workspaceMemberMap,
|
|
10664
|
+
) {
|
|
10665
|
+
const resolvedDependencyName = resolveCargoDependencyAliasName(
|
|
10666
|
+
dependencyName,
|
|
10667
|
+
dependencyNode,
|
|
10668
|
+
);
|
|
10669
|
+
if (
|
|
10670
|
+
dependencyNode &&
|
|
10671
|
+
typeof dependencyNode === "object" &&
|
|
10672
|
+
dependencyNode.workspace === true &&
|
|
10673
|
+
workspaceMemberMap?.has(resolvedDependencyName)
|
|
10674
|
+
) {
|
|
10675
|
+
return workspaceMemberMap.get(resolvedDependencyName);
|
|
10676
|
+
}
|
|
10677
|
+
const dependencyPath =
|
|
10678
|
+
dependencyNode &&
|
|
10679
|
+
typeof dependencyNode === "object" &&
|
|
10680
|
+
typeof dependencyNode.path === "string" &&
|
|
10681
|
+
dependencyNode.path
|
|
10682
|
+
? resolve(dirname(cargoTomlFile), dependencyNode.path, "Cargo.toml")
|
|
10683
|
+
: undefined;
|
|
10684
|
+
if (!dependencyPath || !safeExistsSync(dependencyPath)) {
|
|
10685
|
+
return undefined;
|
|
10686
|
+
}
|
|
10687
|
+
const dependencyCargoData = readCargoTomlData(dependencyPath);
|
|
10688
|
+
if (!dependencyCargoData) {
|
|
10689
|
+
return undefined;
|
|
10690
|
+
}
|
|
10691
|
+
const dependencyIdentity = resolveCargoManifestPackageIdentity(
|
|
10692
|
+
dependencyPath,
|
|
10693
|
+
dependencyCargoData,
|
|
10694
|
+
{
|
|
10695
|
+
workspaceRootData: workspaceContext?.workspaceRootData,
|
|
10696
|
+
workspaceRootFile: workspaceContext?.workspaceRootFile,
|
|
10697
|
+
},
|
|
10698
|
+
);
|
|
10699
|
+
if (!dependencyIdentity?.name || !dependencyIdentity?.version) {
|
|
10700
|
+
return undefined;
|
|
10701
|
+
}
|
|
10702
|
+
return {
|
|
10703
|
+
...dependencyIdentity,
|
|
10704
|
+
filePath: dependencyPath,
|
|
10705
|
+
ref: cargoPackageInfoToPurl(dependencyIdentity),
|
|
10706
|
+
};
|
|
10707
|
+
}
|
|
10708
|
+
|
|
10709
|
+
function ensurePropertiesArray(pkg) {
|
|
10710
|
+
if (!pkg.properties) {
|
|
10711
|
+
pkg.properties = [];
|
|
10712
|
+
}
|
|
10713
|
+
return pkg.properties;
|
|
10714
|
+
}
|
|
10715
|
+
|
|
10716
|
+
function appendCargoProperty(pkg, name, value) {
|
|
10717
|
+
if (!name || value === undefined || value === null || value === "") {
|
|
10718
|
+
return;
|
|
10719
|
+
}
|
|
10720
|
+
const properties = ensurePropertiesArray(pkg);
|
|
10721
|
+
const stringValue = typeof value === "string" ? value : String(value);
|
|
10722
|
+
if (
|
|
10723
|
+
properties.some(
|
|
10724
|
+
(property) => property.name === name && property.value === stringValue,
|
|
10725
|
+
)
|
|
10726
|
+
) {
|
|
10727
|
+
return;
|
|
10728
|
+
}
|
|
10729
|
+
properties.push({
|
|
10730
|
+
name,
|
|
10731
|
+
value: stringValue,
|
|
10732
|
+
});
|
|
10733
|
+
}
|
|
10734
|
+
|
|
10735
|
+
function normalizeCargoDependencyVersion(dependencyNode) {
|
|
10736
|
+
if (typeof dependencyNode === "string" || dependencyNode instanceof String) {
|
|
10737
|
+
return dependencyNode.trim();
|
|
10738
|
+
}
|
|
10739
|
+
if (!dependencyNode || typeof dependencyNode !== "object") {
|
|
10740
|
+
return "";
|
|
10741
|
+
}
|
|
10742
|
+
if (typeof dependencyNode.version === "string" && dependencyNode.version) {
|
|
10743
|
+
return dependencyNode.version;
|
|
10744
|
+
}
|
|
10745
|
+
if (typeof dependencyNode.git === "string" && dependencyNode.git) {
|
|
10746
|
+
return `git+${dependencyNode.git}`;
|
|
10747
|
+
}
|
|
10748
|
+
if (typeof dependencyNode.path === "string" && dependencyNode.path) {
|
|
10749
|
+
return `path+${dependencyNode.path}`;
|
|
10750
|
+
}
|
|
10751
|
+
if (dependencyNode.workspace === true) {
|
|
10752
|
+
return "workspace";
|
|
10753
|
+
}
|
|
10754
|
+
return "";
|
|
10755
|
+
}
|
|
10756
|
+
|
|
10757
|
+
function applyCargoDependencySpecMetadata(
|
|
10758
|
+
pkg,
|
|
10759
|
+
dependencyNode,
|
|
10760
|
+
dependencyKind,
|
|
10761
|
+
targetSelector,
|
|
10762
|
+
resolvedWorkspaceTarget,
|
|
10763
|
+
) {
|
|
10764
|
+
appendCargoProperty(pkg, "cdx:cargo:dependencyKind", dependencyKind);
|
|
10765
|
+
appendCargoProperty(pkg, "cdx:cargo:scope", dependencyKind);
|
|
10766
|
+
if (targetSelector) {
|
|
10767
|
+
appendCargoProperty(pkg, "cdx:cargo:target", targetSelector);
|
|
10768
|
+
}
|
|
10769
|
+
if (!dependencyNode || typeof dependencyNode !== "object") {
|
|
10770
|
+
if (dependencyKind === "dev") {
|
|
10771
|
+
pkg.scope = "excluded";
|
|
10772
|
+
}
|
|
10773
|
+
return;
|
|
10774
|
+
}
|
|
10775
|
+
if (dependencyKind === "dev") {
|
|
10776
|
+
pkg.scope = "excluded";
|
|
10777
|
+
}
|
|
10778
|
+
if (dependencyNode.optional === true) {
|
|
10779
|
+
pkg.scope = "optional";
|
|
10780
|
+
appendCargoProperty(pkg, "cdx:cargo:optional", "true");
|
|
10781
|
+
}
|
|
10782
|
+
if (dependencyNode.default_features === false) {
|
|
10783
|
+
appendCargoProperty(pkg, "cdx:cargo:defaultFeatures", "false");
|
|
10784
|
+
}
|
|
10785
|
+
if (dependencyNode["default-features"] === false) {
|
|
10786
|
+
appendCargoProperty(pkg, "cdx:cargo:defaultFeatures", "false");
|
|
10787
|
+
}
|
|
10788
|
+
if (
|
|
10789
|
+
Array.isArray(dependencyNode.features) &&
|
|
10790
|
+
dependencyNode.features.length
|
|
10791
|
+
) {
|
|
10792
|
+
appendCargoProperty(
|
|
10793
|
+
pkg,
|
|
10794
|
+
"cdx:cargo:dependencyFeatures",
|
|
10795
|
+
JSON.stringify(dependencyNode.features),
|
|
10796
|
+
);
|
|
10797
|
+
}
|
|
10798
|
+
appendCargoProperty(pkg, "cdx:cargo:path", dependencyNode.path);
|
|
10799
|
+
appendCargoProperty(pkg, "cdx:cargo:git", dependencyNode.git);
|
|
10800
|
+
appendCargoProperty(pkg, "cdx:cargo:gitBranch", dependencyNode.branch);
|
|
10801
|
+
appendCargoProperty(pkg, "cdx:cargo:gitTag", dependencyNode.tag);
|
|
10802
|
+
appendCargoProperty(pkg, "cdx:cargo:gitRev", dependencyNode.rev);
|
|
10803
|
+
appendCargoProperty(pkg, "cdx:cargo:registry", dependencyNode.registry);
|
|
10804
|
+
appendCargoProperty(pkg, "cdx:cargo:package", dependencyNode.package);
|
|
10805
|
+
appendCargoProperty(
|
|
10806
|
+
pkg,
|
|
10807
|
+
"cdx:cargo:workspaceDependency",
|
|
10808
|
+
dependencyNode.workspace === true ? "true" : undefined,
|
|
10809
|
+
);
|
|
10810
|
+
appendCargoProperty(
|
|
10811
|
+
pkg,
|
|
10812
|
+
"cdx:cargo:workspaceDependencyResolved",
|
|
10813
|
+
resolvedWorkspaceTarget ? "true" : undefined,
|
|
10814
|
+
);
|
|
10815
|
+
appendCargoProperty(
|
|
10816
|
+
pkg,
|
|
10817
|
+
"cdx:cargo:resolvedWorkspaceMember",
|
|
10818
|
+
resolvedWorkspaceTarget?.name,
|
|
10819
|
+
);
|
|
10820
|
+
appendCargoProperty(
|
|
10821
|
+
pkg,
|
|
10822
|
+
"cdx:cargo:resolvedMemberPath",
|
|
10823
|
+
resolvedWorkspaceTarget?.filePath,
|
|
10824
|
+
);
|
|
10825
|
+
}
|
|
10826
|
+
|
|
10827
|
+
function collectCargoManifestDependencyComponents(
|
|
10828
|
+
dependencyEntries,
|
|
10829
|
+
addPackageToList,
|
|
10830
|
+
pkgList,
|
|
10831
|
+
simple,
|
|
10832
|
+
dependencyKind,
|
|
10833
|
+
targetSelector,
|
|
10834
|
+
workspaceDependencies,
|
|
10835
|
+
cargoTomlFile,
|
|
10836
|
+
workspaceContext,
|
|
10837
|
+
workspaceMemberMap,
|
|
10838
|
+
) {
|
|
10839
|
+
if (!dependencyEntries || typeof dependencyEntries !== "object") {
|
|
10840
|
+
return;
|
|
10841
|
+
}
|
|
10842
|
+
for (const dependencyName of Object.keys(dependencyEntries)) {
|
|
10843
|
+
const dependencyNode = mergeCargoWorkspaceDependencySpec(
|
|
10844
|
+
dependencyName,
|
|
10845
|
+
dependencyEntries[dependencyName],
|
|
10846
|
+
workspaceDependencies,
|
|
10847
|
+
);
|
|
10848
|
+
const resolvedWorkspaceTarget = resolveCargoWorkspaceDependencyTarget(
|
|
10849
|
+
cargoTomlFile,
|
|
10850
|
+
dependencyName,
|
|
10851
|
+
dependencyNode,
|
|
10852
|
+
workspaceContext,
|
|
10853
|
+
workspaceMemberMap,
|
|
10854
|
+
);
|
|
10855
|
+
const version = normalizeCargoDependencyVersion(dependencyNode);
|
|
10856
|
+
if (!dependencyName || !version) {
|
|
10857
|
+
continue;
|
|
10858
|
+
}
|
|
10859
|
+
const pkg = {
|
|
10860
|
+
name: dependencyName,
|
|
10861
|
+
version,
|
|
10862
|
+
};
|
|
10863
|
+
applyCargoDependencySpecMetadata(
|
|
10864
|
+
pkg,
|
|
10865
|
+
dependencyNode,
|
|
10866
|
+
dependencyKind,
|
|
10867
|
+
targetSelector,
|
|
10868
|
+
resolvedWorkspaceTarget,
|
|
10869
|
+
);
|
|
10870
|
+
addPackageToList(pkgList, pkg, { packageMode: false, simple });
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
|
|
9960
10874
|
/**
|
|
9961
10875
|
* Method to parse cargo.toml data
|
|
9962
10876
|
*
|
|
@@ -9979,6 +10893,7 @@ export async function parseCargoTomlData(
|
|
|
9979
10893
|
cargoTomlFile,
|
|
9980
10894
|
simple = false,
|
|
9981
10895
|
pkgFilesMap = {},
|
|
10896
|
+
context = {},
|
|
9982
10897
|
) {
|
|
9983
10898
|
const pkgList = [];
|
|
9984
10899
|
|
|
@@ -9995,6 +10910,7 @@ export async function parseCargoTomlData(
|
|
|
9995
10910
|
name: "SrcFile",
|
|
9996
10911
|
value: cargoTomlFile,
|
|
9997
10912
|
},
|
|
10913
|
+
...(pkg.properties || []),
|
|
9998
10914
|
];
|
|
9999
10915
|
if (pkgFilesMap?.[pkg.name]) {
|
|
10000
10916
|
pkg.components = fileListToComponents(pkgFilesMap[pkg.name]);
|
|
@@ -10038,16 +10954,39 @@ export async function parseCargoTomlData(
|
|
|
10038
10954
|
if (!cargoTomlFile || !safeExistsSync(cargoTomlFile)) {
|
|
10039
10955
|
return pkgList;
|
|
10040
10956
|
}
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
|
|
10957
|
+
const normalizedCargoTomlFile = resolve(cargoTomlFile);
|
|
10958
|
+
cargoTomlFile = normalizedCargoTomlFile;
|
|
10959
|
+
const visitedCargoTomlFiles = context?.visitedCargoTomlFiles || new Set();
|
|
10960
|
+
for (const visitedCargoTomlFile of visitedCargoTomlFiles) {
|
|
10961
|
+
if (typeof visitedCargoTomlFile === "string") {
|
|
10962
|
+
visitedCargoTomlFiles.add(resolve(visitedCargoTomlFile));
|
|
10963
|
+
}
|
|
10046
10964
|
}
|
|
10965
|
+
if (visitedCargoTomlFiles.has(normalizedCargoTomlFile)) {
|
|
10966
|
+
return pkgList;
|
|
10967
|
+
}
|
|
10968
|
+
visitedCargoTomlFiles.add(normalizedCargoTomlFile);
|
|
10969
|
+
const cargoData = readCargoTomlData(normalizedCargoTomlFile);
|
|
10047
10970
|
if (!cargoData) {
|
|
10048
10971
|
return pkgList;
|
|
10049
10972
|
}
|
|
10050
|
-
const
|
|
10973
|
+
const workspaceContext = resolveCargoWorkspaceContext(
|
|
10974
|
+
normalizedCargoTomlFile,
|
|
10975
|
+
cargoData,
|
|
10976
|
+
context,
|
|
10977
|
+
);
|
|
10978
|
+
const workspacePackageNode = workspaceContext?.workspaceData?.package;
|
|
10979
|
+
const workspaceDependencies = workspaceContext?.workspaceData?.dependencies;
|
|
10980
|
+
const workspaceMemberCache = context?.workspaceMemberCache || new Map();
|
|
10981
|
+
const workspaceMemberMap = resolveCargoWorkspaceMemberMap(
|
|
10982
|
+
workspaceContext?.workspaceRootFile,
|
|
10983
|
+
workspaceContext?.workspaceRootData,
|
|
10984
|
+
workspaceMemberCache,
|
|
10985
|
+
);
|
|
10986
|
+
const packageNode = resolveCargoWorkspacePackageNode(
|
|
10987
|
+
cargoData["package"],
|
|
10988
|
+
workspacePackageNode,
|
|
10989
|
+
);
|
|
10051
10990
|
// parse `[package]`
|
|
10052
10991
|
if (packageNode instanceof Object && !Array.isArray(packageNode)) {
|
|
10053
10992
|
/** @type {Object} */
|
|
@@ -10060,45 +10999,159 @@ export async function parseCargoTomlData(
|
|
|
10060
10999
|
`Failed to parse package: ${packageObjNode?.name}@${packageObjNode?.version},fail with:${e.message}`,
|
|
10061
11000
|
);
|
|
10062
11001
|
}
|
|
11002
|
+
} else if (
|
|
11003
|
+
cargoData.workspace &&
|
|
11004
|
+
workspaceContext?.isWorkspaceRoot &&
|
|
11005
|
+
workspaceContext?.isVirtualWorkspace
|
|
11006
|
+
) {
|
|
11007
|
+
const workspaceComponent = {
|
|
11008
|
+
name: basename(dirname(cargoTomlFile)),
|
|
11009
|
+
properties: [],
|
|
11010
|
+
version: "workspace",
|
|
11011
|
+
};
|
|
11012
|
+
appendCargoProperty(
|
|
11013
|
+
workspaceComponent,
|
|
11014
|
+
"cdx:cargo:manifestMode",
|
|
11015
|
+
"virtual-workspace",
|
|
11016
|
+
);
|
|
11017
|
+
addPackageToList(pkgList, workspaceComponent, {
|
|
11018
|
+
packageMode: true,
|
|
11019
|
+
simple,
|
|
11020
|
+
});
|
|
10063
11021
|
}
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
11022
|
+
if (pkgList[0] && workspaceContext?.workspaceRootFile) {
|
|
11023
|
+
appendCargoProperty(
|
|
11024
|
+
pkgList[0],
|
|
11025
|
+
"cdx:cargo:workspaceRoot",
|
|
11026
|
+
workspaceContext.workspaceRootFile,
|
|
11027
|
+
);
|
|
11028
|
+
appendCargoProperty(
|
|
11029
|
+
pkgList[0],
|
|
11030
|
+
"cdx:cargo:manifestMode",
|
|
11031
|
+
cargoData?.workspace
|
|
11032
|
+
? workspaceContext?.isVirtualWorkspace
|
|
11033
|
+
? "virtual-workspace"
|
|
11034
|
+
: "workspace"
|
|
11035
|
+
: "package",
|
|
11036
|
+
);
|
|
11037
|
+
}
|
|
11038
|
+
if (Array.isArray(cargoData?.workspace?.members) && pkgList[0]) {
|
|
11039
|
+
appendCargoProperty(pkgList[0], "cdx:cargo:hasWorkspaceMembers", "true");
|
|
11040
|
+
appendCargoProperty(
|
|
11041
|
+
pkgList[0],
|
|
11042
|
+
"cdx:cargo:workspaceMembers",
|
|
11043
|
+
cargoData.workspace.members.join(", "),
|
|
11044
|
+
);
|
|
11045
|
+
}
|
|
11046
|
+
collectCargoManifestDependencyComponents(
|
|
11047
|
+
cargoData["dependencies"],
|
|
11048
|
+
addPackageToList,
|
|
11049
|
+
pkgList,
|
|
11050
|
+
simple,
|
|
11051
|
+
"runtime",
|
|
11052
|
+
undefined,
|
|
11053
|
+
workspaceDependencies,
|
|
11054
|
+
cargoTomlFile,
|
|
11055
|
+
workspaceContext,
|
|
11056
|
+
workspaceMemberMap,
|
|
11057
|
+
);
|
|
11058
|
+
collectCargoManifestDependencyComponents(
|
|
11059
|
+
cargoData["build-dependencies"],
|
|
11060
|
+
addPackageToList,
|
|
11061
|
+
pkgList,
|
|
11062
|
+
simple,
|
|
11063
|
+
"build",
|
|
11064
|
+
undefined,
|
|
11065
|
+
workspaceDependencies,
|
|
11066
|
+
cargoTomlFile,
|
|
11067
|
+
workspaceContext,
|
|
11068
|
+
workspaceMemberMap,
|
|
11069
|
+
);
|
|
11070
|
+
collectCargoManifestDependencyComponents(
|
|
11071
|
+
cargoData["dev-dependencies"],
|
|
11072
|
+
addPackageToList,
|
|
11073
|
+
pkgList,
|
|
11074
|
+
simple,
|
|
11075
|
+
"dev",
|
|
11076
|
+
undefined,
|
|
11077
|
+
workspaceDependencies,
|
|
11078
|
+
cargoTomlFile,
|
|
11079
|
+
workspaceContext,
|
|
11080
|
+
workspaceMemberMap,
|
|
11081
|
+
);
|
|
11082
|
+
if (cargoData.target && typeof cargoData.target === "object") {
|
|
11083
|
+
for (const targetSelector of Object.keys(cargoData.target)) {
|
|
11084
|
+
const targetBlock = cargoData.target[targetSelector];
|
|
11085
|
+
if (!targetBlock || typeof targetBlock !== "object") {
|
|
11086
|
+
continue;
|
|
10089
11087
|
}
|
|
11088
|
+
collectCargoManifestDependencyComponents(
|
|
11089
|
+
targetBlock["dependencies"],
|
|
11090
|
+
addPackageToList,
|
|
11091
|
+
pkgList,
|
|
11092
|
+
simple,
|
|
11093
|
+
"runtime",
|
|
11094
|
+
targetSelector,
|
|
11095
|
+
workspaceDependencies,
|
|
11096
|
+
cargoTomlFile,
|
|
11097
|
+
workspaceContext,
|
|
11098
|
+
workspaceMemberMap,
|
|
11099
|
+
);
|
|
11100
|
+
collectCargoManifestDependencyComponents(
|
|
11101
|
+
targetBlock["build-dependencies"],
|
|
11102
|
+
addPackageToList,
|
|
11103
|
+
pkgList,
|
|
11104
|
+
simple,
|
|
11105
|
+
"build",
|
|
11106
|
+
targetSelector,
|
|
11107
|
+
workspaceDependencies,
|
|
11108
|
+
cargoTomlFile,
|
|
11109
|
+
workspaceContext,
|
|
11110
|
+
workspaceMemberMap,
|
|
11111
|
+
);
|
|
11112
|
+
collectCargoManifestDependencyComponents(
|
|
11113
|
+
targetBlock["dev-dependencies"],
|
|
11114
|
+
addPackageToList,
|
|
11115
|
+
pkgList,
|
|
11116
|
+
simple,
|
|
11117
|
+
"dev",
|
|
11118
|
+
targetSelector,
|
|
11119
|
+
workspaceDependencies,
|
|
11120
|
+
cargoTomlFile,
|
|
11121
|
+
workspaceContext,
|
|
11122
|
+
workspaceMemberMap,
|
|
11123
|
+
);
|
|
10090
11124
|
}
|
|
10091
11125
|
}
|
|
10092
|
-
|
|
10093
|
-
|
|
10094
|
-
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
`Found [workspace] section in ${cargoTomlFile}. Workspaces are currently not fully supported. Verify that the parent component is correct.`,
|
|
11126
|
+
if (
|
|
11127
|
+
context?.includeWorkspaceMembers !== false &&
|
|
11128
|
+
workspaceContext?.isWorkspaceRoot &&
|
|
11129
|
+
Array.isArray(cargoData?.workspace?.members)
|
|
11130
|
+
) {
|
|
11131
|
+
const workspaceMemberFiles = resolveCargoWorkspaceMembers(
|
|
11132
|
+
cargoTomlFile,
|
|
11133
|
+
cargoData.workspace,
|
|
10101
11134
|
);
|
|
11135
|
+
for (const workspaceMemberFile of workspaceMemberFiles) {
|
|
11136
|
+
if (workspaceMemberFile === cargoTomlFile) {
|
|
11137
|
+
continue;
|
|
11138
|
+
}
|
|
11139
|
+
const workspaceMemberPackages = await parseCargoTomlData(
|
|
11140
|
+
workspaceMemberFile,
|
|
11141
|
+
simple,
|
|
11142
|
+
pkgFilesMap,
|
|
11143
|
+
{
|
|
11144
|
+
includeWorkspaceMembers: false,
|
|
11145
|
+
visitedCargoTomlFiles,
|
|
11146
|
+
workspaceMemberCache,
|
|
11147
|
+
workspaceRootData: cargoData,
|
|
11148
|
+
workspaceRootFile: cargoTomlFile,
|
|
11149
|
+
},
|
|
11150
|
+
);
|
|
11151
|
+
if (workspaceMemberPackages?.length) {
|
|
11152
|
+
pkgList.push(...workspaceMemberPackages);
|
|
11153
|
+
}
|
|
11154
|
+
}
|
|
10102
11155
|
}
|
|
10103
11156
|
|
|
10104
11157
|
if (!simple && shouldFetchLicense()) {
|
|
@@ -10143,13 +11196,9 @@ export async function parseCargoData(
|
|
|
10143
11196
|
version: newPackage.version,
|
|
10144
11197
|
};
|
|
10145
11198
|
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
alg: "SHA-384",
|
|
10150
|
-
content: newPackage._integrity,
|
|
10151
|
-
},
|
|
10152
|
-
];
|
|
11199
|
+
const integrityHash = cargoIntegrityToComponentHash(newPackage._integrity);
|
|
11200
|
+
if (integrityHash) {
|
|
11201
|
+
component.hashes = [integrityHash];
|
|
10153
11202
|
}
|
|
10154
11203
|
|
|
10155
11204
|
if (!simple) {
|
|
@@ -10221,6 +11270,178 @@ export async function parseCargoData(
|
|
|
10221
11270
|
return pkgList;
|
|
10222
11271
|
}
|
|
10223
11272
|
|
|
11273
|
+
function collectCargoManifestDependencyRefs(
|
|
11274
|
+
cargoTomlFile,
|
|
11275
|
+
dependencyEntries,
|
|
11276
|
+
workspaceDependencies,
|
|
11277
|
+
workspaceContext,
|
|
11278
|
+
workspaceMemberMap,
|
|
11279
|
+
dependsOn,
|
|
11280
|
+
) {
|
|
11281
|
+
if (!dependencyEntries || typeof dependencyEntries !== "object") {
|
|
11282
|
+
return;
|
|
11283
|
+
}
|
|
11284
|
+
for (const dependencyName of Object.keys(dependencyEntries)) {
|
|
11285
|
+
const dependencyNode = mergeCargoWorkspaceDependencySpec(
|
|
11286
|
+
dependencyName,
|
|
11287
|
+
dependencyEntries[dependencyName],
|
|
11288
|
+
workspaceDependencies,
|
|
11289
|
+
);
|
|
11290
|
+
const resolvedWorkspaceTarget = resolveCargoWorkspaceDependencyTarget(
|
|
11291
|
+
cargoTomlFile,
|
|
11292
|
+
dependencyName,
|
|
11293
|
+
dependencyNode,
|
|
11294
|
+
workspaceContext,
|
|
11295
|
+
workspaceMemberMap,
|
|
11296
|
+
);
|
|
11297
|
+
if (resolvedWorkspaceTarget?.ref) {
|
|
11298
|
+
dependsOn.add(resolvedWorkspaceTarget.ref);
|
|
11299
|
+
}
|
|
11300
|
+
}
|
|
11301
|
+
}
|
|
11302
|
+
|
|
11303
|
+
/**
|
|
11304
|
+
* Build a Cargo dependency graph from manifest relationships so workspace roots
|
|
11305
|
+
* and member-to-member links can complement lockfile-derived dependency data.
|
|
11306
|
+
*
|
|
11307
|
+
* @param {string} cargoTomlFile Cargo.toml path
|
|
11308
|
+
* @param {object} [context] manifest graph context
|
|
11309
|
+
* @returns {object[]} Cargo dependency relationships
|
|
11310
|
+
*/
|
|
11311
|
+
export function parseCargoManifestDependencyData(cargoTomlFile, context = {}) {
|
|
11312
|
+
if (!cargoTomlFile || !safeExistsSync(cargoTomlFile)) {
|
|
11313
|
+
return [];
|
|
11314
|
+
}
|
|
11315
|
+
const normalizedCargoTomlFile = resolve(cargoTomlFile);
|
|
11316
|
+
cargoTomlFile = normalizedCargoTomlFile;
|
|
11317
|
+
const visitedCargoTomlFiles =
|
|
11318
|
+
context?.visitedCargoTomlDependencyGraphFiles || new Set();
|
|
11319
|
+
for (const visitedCargoTomlFile of visitedCargoTomlFiles) {
|
|
11320
|
+
if (typeof visitedCargoTomlFile === "string") {
|
|
11321
|
+
visitedCargoTomlFiles.add(resolve(visitedCargoTomlFile));
|
|
11322
|
+
}
|
|
11323
|
+
}
|
|
11324
|
+
if (visitedCargoTomlFiles.has(normalizedCargoTomlFile)) {
|
|
11325
|
+
return [];
|
|
11326
|
+
}
|
|
11327
|
+
visitedCargoTomlFiles.add(normalizedCargoTomlFile);
|
|
11328
|
+
const cargoData = readCargoTomlData(normalizedCargoTomlFile);
|
|
11329
|
+
if (!cargoData) {
|
|
11330
|
+
return [];
|
|
11331
|
+
}
|
|
11332
|
+
const workspaceContext = resolveCargoWorkspaceContext(
|
|
11333
|
+
cargoTomlFile,
|
|
11334
|
+
cargoData,
|
|
11335
|
+
context,
|
|
11336
|
+
);
|
|
11337
|
+
const workspaceMemberCache = context?.workspaceMemberCache || new Map();
|
|
11338
|
+
const workspaceMemberMap = resolveCargoWorkspaceMemberMap(
|
|
11339
|
+
workspaceContext?.workspaceRootFile,
|
|
11340
|
+
workspaceContext?.workspaceRootData,
|
|
11341
|
+
workspaceMemberCache,
|
|
11342
|
+
);
|
|
11343
|
+
const workspaceDependencies = workspaceContext?.workspaceData?.dependencies;
|
|
11344
|
+
const currentIdentity = resolveCargoManifestPackageIdentity(
|
|
11345
|
+
cargoTomlFile,
|
|
11346
|
+
cargoData,
|
|
11347
|
+
context,
|
|
11348
|
+
);
|
|
11349
|
+
const dependencyGraph = [];
|
|
11350
|
+
const dependsOn = new Set();
|
|
11351
|
+
if (workspaceContext?.isWorkspaceRoot) {
|
|
11352
|
+
for (const workspaceMember of workspaceMemberMap.values()) {
|
|
11353
|
+
if (workspaceMember?.ref) {
|
|
11354
|
+
dependsOn.add(workspaceMember.ref);
|
|
11355
|
+
}
|
|
11356
|
+
}
|
|
11357
|
+
}
|
|
11358
|
+
collectCargoManifestDependencyRefs(
|
|
11359
|
+
cargoTomlFile,
|
|
11360
|
+
cargoData.dependencies,
|
|
11361
|
+
workspaceDependencies,
|
|
11362
|
+
workspaceContext,
|
|
11363
|
+
workspaceMemberMap,
|
|
11364
|
+
dependsOn,
|
|
11365
|
+
);
|
|
11366
|
+
collectCargoManifestDependencyRefs(
|
|
11367
|
+
cargoTomlFile,
|
|
11368
|
+
cargoData["build-dependencies"],
|
|
11369
|
+
workspaceDependencies,
|
|
11370
|
+
workspaceContext,
|
|
11371
|
+
workspaceMemberMap,
|
|
11372
|
+
dependsOn,
|
|
11373
|
+
);
|
|
11374
|
+
collectCargoManifestDependencyRefs(
|
|
11375
|
+
cargoTomlFile,
|
|
11376
|
+
cargoData["dev-dependencies"],
|
|
11377
|
+
workspaceDependencies,
|
|
11378
|
+
workspaceContext,
|
|
11379
|
+
workspaceMemberMap,
|
|
11380
|
+
dependsOn,
|
|
11381
|
+
);
|
|
11382
|
+
if (cargoData.target && typeof cargoData.target === "object") {
|
|
11383
|
+
for (const targetBlock of Object.values(cargoData.target)) {
|
|
11384
|
+
if (!targetBlock || typeof targetBlock !== "object") {
|
|
11385
|
+
continue;
|
|
11386
|
+
}
|
|
11387
|
+
collectCargoManifestDependencyRefs(
|
|
11388
|
+
cargoTomlFile,
|
|
11389
|
+
targetBlock.dependencies,
|
|
11390
|
+
workspaceDependencies,
|
|
11391
|
+
workspaceContext,
|
|
11392
|
+
workspaceMemberMap,
|
|
11393
|
+
dependsOn,
|
|
11394
|
+
);
|
|
11395
|
+
collectCargoManifestDependencyRefs(
|
|
11396
|
+
cargoTomlFile,
|
|
11397
|
+
targetBlock["build-dependencies"],
|
|
11398
|
+
workspaceDependencies,
|
|
11399
|
+
workspaceContext,
|
|
11400
|
+
workspaceMemberMap,
|
|
11401
|
+
dependsOn,
|
|
11402
|
+
);
|
|
11403
|
+
collectCargoManifestDependencyRefs(
|
|
11404
|
+
cargoTomlFile,
|
|
11405
|
+
targetBlock["dev-dependencies"],
|
|
11406
|
+
workspaceDependencies,
|
|
11407
|
+
workspaceContext,
|
|
11408
|
+
workspaceMemberMap,
|
|
11409
|
+
dependsOn,
|
|
11410
|
+
);
|
|
11411
|
+
}
|
|
11412
|
+
}
|
|
11413
|
+
if (currentIdentity?.name && currentIdentity?.version) {
|
|
11414
|
+
dependencyGraph.push({
|
|
11415
|
+
dependsOn: [...dependsOn].sort(),
|
|
11416
|
+
ref: cargoPackageInfoToPurl(currentIdentity),
|
|
11417
|
+
});
|
|
11418
|
+
}
|
|
11419
|
+
if (
|
|
11420
|
+
context?.includeWorkspaceMembers !== false &&
|
|
11421
|
+
workspaceContext?.isWorkspaceRoot &&
|
|
11422
|
+
Array.isArray(cargoData?.workspace?.members)
|
|
11423
|
+
) {
|
|
11424
|
+
for (const workspaceMemberFile of resolveCargoWorkspaceMembers(
|
|
11425
|
+
cargoTomlFile,
|
|
11426
|
+
cargoData.workspace,
|
|
11427
|
+
)) {
|
|
11428
|
+
if (workspaceMemberFile === cargoTomlFile) {
|
|
11429
|
+
continue;
|
|
11430
|
+
}
|
|
11431
|
+
dependencyGraph.push(
|
|
11432
|
+
...parseCargoManifestDependencyData(workspaceMemberFile, {
|
|
11433
|
+
includeWorkspaceMembers: false,
|
|
11434
|
+
visitedCargoTomlDependencyGraphFiles: visitedCargoTomlFiles,
|
|
11435
|
+
workspaceMemberCache,
|
|
11436
|
+
workspaceRootData: cargoData,
|
|
11437
|
+
workspaceRootFile: cargoTomlFile,
|
|
11438
|
+
}),
|
|
11439
|
+
);
|
|
11440
|
+
}
|
|
11441
|
+
}
|
|
11442
|
+
return dependencyGraph;
|
|
11443
|
+
}
|
|
11444
|
+
|
|
10224
11445
|
/**
|
|
10225
11446
|
* Parses a Cargo.lock file's TOML data and returns a flat dependency graph as an
|
|
10226
11447
|
* array of objects mapping each package purl to the purls it directly depends on.
|
|
@@ -11082,7 +12303,15 @@ export function parseMixLockData(mixData) {
|
|
|
11082
12303
|
*/
|
|
11083
12304
|
export function parseGitHubWorkflowData(f) {
|
|
11084
12305
|
const { components } = parseWorkflowFile(f);
|
|
11085
|
-
return components.filter(
|
|
12306
|
+
return components.filter(
|
|
12307
|
+
(component) =>
|
|
12308
|
+
component.scope === "required" ||
|
|
12309
|
+
component.properties?.some(
|
|
12310
|
+
(property) =>
|
|
12311
|
+
property?.name === "cdx:github:step:usesCargo" &&
|
|
12312
|
+
property?.value === "true",
|
|
12313
|
+
),
|
|
12314
|
+
);
|
|
11086
12315
|
}
|
|
11087
12316
|
|
|
11088
12317
|
/**
|
|
@@ -13843,7 +15072,7 @@ export async function collectMvnDependencies(
|
|
|
13843
15072
|
let jarNSMapping = {};
|
|
13844
15073
|
const MAVEN_CACHE_DIR =
|
|
13845
15074
|
process.env.MAVEN_CACHE_DIR || join(homedir(), ".m2", "repository");
|
|
13846
|
-
const tempDir =
|
|
15075
|
+
const tempDir = safeMkdtempSync(join(tmpdir(), "mvn-deps-"));
|
|
13847
15076
|
let copyArgs = [
|
|
13848
15077
|
"dependency:copy-dependencies",
|
|
13849
15078
|
`-DoutputDirectory=${tempDir}`,
|
|
@@ -13888,8 +15117,8 @@ export async function collectMvnDependencies(
|
|
|
13888
15117
|
}
|
|
13889
15118
|
|
|
13890
15119
|
// Clean up
|
|
13891
|
-
if (cleanup && tempDir?.startsWith(tmpdir())
|
|
13892
|
-
|
|
15120
|
+
if (cleanup && tempDir?.startsWith(tmpdir())) {
|
|
15121
|
+
safeRmSync(tempDir, { recursive: true, force: true });
|
|
13893
15122
|
}
|
|
13894
15123
|
return jarNSMapping;
|
|
13895
15124
|
}
|
|
@@ -14465,7 +15694,7 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
14465
15694
|
lstatSync(jarFile).isFile()
|
|
14466
15695
|
) {
|
|
14467
15696
|
// Only copy if the file doesn't exist
|
|
14468
|
-
|
|
15697
|
+
safeCopyFileSync(jarFile, join(tempDir, fname), constants.COPYFILE_FICLONE);
|
|
14469
15698
|
}
|
|
14470
15699
|
const env = {
|
|
14471
15700
|
...process.env,
|
|
@@ -14485,8 +15714,17 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
14485
15714
|
if (safeExistsSync(join(tempDir, fname))) {
|
|
14486
15715
|
try {
|
|
14487
15716
|
const zip = new StreamZip.async({ file: join(tempDir, fname) });
|
|
14488
|
-
await
|
|
15717
|
+
const extracted = await safeExtractArchive(
|
|
15718
|
+
join(tempDir, fname),
|
|
15719
|
+
tempDir,
|
|
15720
|
+
async () => {
|
|
15721
|
+
await zip.extract(null, tempDir);
|
|
15722
|
+
},
|
|
15723
|
+
);
|
|
14489
15724
|
await zip.close();
|
|
15725
|
+
if (!extracted) {
|
|
15726
|
+
return pkgList;
|
|
15727
|
+
}
|
|
14490
15728
|
} catch (e) {
|
|
14491
15729
|
console.log(`Unable to extract ${join(tempDir, fname)}. Skipping.`, e);
|
|
14492
15730
|
return pkgList;
|
|
@@ -14541,9 +15779,11 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
14541
15779
|
// Unzip natively
|
|
14542
15780
|
try {
|
|
14543
15781
|
const zip = new StreamZip.async({ file: jf });
|
|
14544
|
-
await
|
|
15782
|
+
const extracted = await safeExtractArchive(jf, tempDir, async () => {
|
|
15783
|
+
await zip.extract(null, tempDir);
|
|
15784
|
+
});
|
|
14545
15785
|
await zip.close();
|
|
14546
|
-
jarResult = { status: 0 };
|
|
15786
|
+
jarResult = { status: extracted ? 0 : 1 };
|
|
14547
15787
|
} catch (_e) {
|
|
14548
15788
|
if (DEBUG_MODE) {
|
|
14549
15789
|
console.log(`Unable to extract ${jf}. Skipping.`);
|
|
@@ -14786,9 +16026,9 @@ export async function extractJarArchive(jarFile, tempDir, jarNSMapping = {}) {
|
|
|
14786
16026
|
}
|
|
14787
16027
|
}
|
|
14788
16028
|
try {
|
|
14789
|
-
if (
|
|
16029
|
+
if (safeExistsSync(join(tempDir, "META-INF"))) {
|
|
14790
16030
|
// Clean up META-INF
|
|
14791
|
-
|
|
16031
|
+
safeRmSync(join(tempDir, "META-INF"), {
|
|
14792
16032
|
recursive: true,
|
|
14793
16033
|
force: true,
|
|
14794
16034
|
});
|
|
@@ -14859,10 +16099,14 @@ export function addPlugin(projectPath, plugin) {
|
|
|
14859
16099
|
let originalPluginsFile = null;
|
|
14860
16100
|
if (safeExistsSync(pluginsFile)) {
|
|
14861
16101
|
originalPluginsFile = `${pluginsFile}.cdxgen`;
|
|
14862
|
-
|
|
16102
|
+
safeCopyFileSync(
|
|
16103
|
+
pluginsFile,
|
|
16104
|
+
originalPluginsFile,
|
|
16105
|
+
constants.COPYFILE_FICLONE,
|
|
16106
|
+
);
|
|
14863
16107
|
}
|
|
14864
16108
|
|
|
14865
|
-
|
|
16109
|
+
safeWriteSync(pluginsFile, plugin, { flag: "a" });
|
|
14866
16110
|
return originalPluginsFile;
|
|
14867
16111
|
}
|
|
14868
16112
|
|
|
@@ -14878,12 +16122,16 @@ export function cleanupPlugin(projectPath, originalPluginsFile) {
|
|
|
14878
16122
|
if (safeExistsSync(pluginsFile)) {
|
|
14879
16123
|
if (!originalPluginsFile) {
|
|
14880
16124
|
// just remove the file, it was never there
|
|
14881
|
-
|
|
16125
|
+
safeUnlinkSync(pluginsFile);
|
|
14882
16126
|
return !safeExistsSync(pluginsFile);
|
|
14883
16127
|
}
|
|
14884
16128
|
// Bring back the original file
|
|
14885
|
-
|
|
14886
|
-
|
|
16129
|
+
safeCopyFileSync(
|
|
16130
|
+
originalPluginsFile,
|
|
16131
|
+
pluginsFile,
|
|
16132
|
+
constants.COPYFILE_FICLONE,
|
|
16133
|
+
);
|
|
16134
|
+
safeUnlinkSync(originalPluginsFile);
|
|
14887
16135
|
return true;
|
|
14888
16136
|
}
|
|
14889
16137
|
return false;
|
|
@@ -15600,7 +16848,7 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
15600
16848
|
dirname(podspecLocation),
|
|
15601
16849
|
`${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`,
|
|
15602
16850
|
);
|
|
15603
|
-
|
|
16851
|
+
safeWriteSync(podspecLocation, podspecContent);
|
|
15604
16852
|
temporaryFiles.add(podspecLocation);
|
|
15605
16853
|
}
|
|
15606
16854
|
result = executePodCommand(
|
|
@@ -16123,7 +17371,7 @@ export function findAppModules(
|
|
|
16123
17371
|
methodology = "usages",
|
|
16124
17372
|
slicesFile = undefined,
|
|
16125
17373
|
) {
|
|
16126
|
-
const tempDir =
|
|
17374
|
+
const tempDir = safeMkdtempSync(join(tmpdir(), "atom-deps-"));
|
|
16127
17375
|
const atomFile = join(tempDir, `${language}-app.atom`);
|
|
16128
17376
|
if (!slicesFile) {
|
|
16129
17377
|
slicesFile = join(tempDir, "slices.json");
|
|
@@ -16155,8 +17403,8 @@ export function findAppModules(
|
|
|
16155
17403
|
);
|
|
16156
17404
|
}
|
|
16157
17405
|
// Clean up
|
|
16158
|
-
if (tempDir?.startsWith(tmpdir())
|
|
16159
|
-
|
|
17406
|
+
if (tempDir?.startsWith(tmpdir())) {
|
|
17407
|
+
safeRmSync(tempDir, { recursive: true, force: true });
|
|
16160
17408
|
}
|
|
16161
17409
|
return retList;
|
|
16162
17410
|
}
|