@cdxgen/cdxgen 13.0.0 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -13
- package/bin/audit.js +6 -0
- package/bin/cdxgen.js +140 -8
- package/bin/convert.js +7 -0
- package/bin/evinse.js +1 -1
- package/bin/hbom.js +13 -4
- package/bin/tracebom.js +9 -1
- package/data/component-tags.json +8 -1
- package/data/frameworks-list.json +86 -1
- package/data/remediations.json +1144 -0
- package/data/rules/asar-archives.yaml +1 -1
- package/data/rules/build-fidelity.yaml +782 -0
- package/data/rules/package-integrity.yaml +13 -13
- package/lib/audit/index.js +48 -4
- package/lib/audit/scoring.js +2 -8
- package/lib/cli/bomAssembly.js +83 -15
- package/lib/cli/cliOptions.js +124 -7
- package/lib/cli/index.js +76 -48
- package/lib/cli/jsBom.js +32 -3
- package/lib/cli/jvmBom.js +1494 -1315
- package/lib/cli/managedBom.js +97 -28
- package/lib/cli/nativeBom.js +189 -10
- package/lib/core/buildLedger.js +799 -0
- package/lib/core/deferredExit.js +141 -0
- package/lib/core/env.js +69 -1
- package/lib/core/fs.js +60 -0
- package/lib/core/logger.js +76 -25
- package/lib/core/severity.js +16 -0
- package/lib/core/syncFileWriter.js +106 -0
- package/lib/core/ui.js +46 -2
- package/lib/ecosystems/ecosystems.js +9 -0
- package/lib/ecosystems/gradleutils.js +355 -15
- package/lib/ecosystems/parsers-js.js +26 -0
- package/lib/ecosystems/parsers-jvm.js +9 -0
- package/lib/ecosystems/parsers-misc.js +16 -2
- package/lib/ecosystems/parsers-python.js +171 -35
- package/lib/ecosystems/parsers-rust.js +12 -2
- package/lib/ecosystems/pypiClassifiers.js +326 -0
- package/lib/ecosystems/pythonutils.js +49 -1
- package/lib/ecosystems/rubyutils.js +13 -0
- package/lib/ecosystems/utils.js +9 -1
- package/lib/evinser/evinser.js +30 -12
- package/lib/helpers/remote/dependency-track.js +81 -14
- package/lib/inventory/auditCategories.js +9 -0
- package/lib/inventory/depsUtils.js +72 -0
- package/lib/inventory/dosai.js +431 -22
- package/lib/inventory/envcontext.js +211 -19
- package/lib/inventory/fetchBatch.js +57 -0
- package/lib/inventory/jvmToolEnv.js +593 -0
- package/lib/inventory/osinfo.js +123 -16
- package/lib/inventory/table.js +9 -5
- package/lib/inventory/toolRequirements.js +903 -0
- package/lib/managers/binary.js +216 -40
- package/lib/managers/docker.js +26 -5
- package/lib/server/server.js +1 -1
- package/lib/stages/postgen/annotationBuilder.js +97 -0
- package/lib/stages/postgen/auditBom.js +76 -79
- package/lib/stages/postgen/introspection/annotate.js +320 -0
- package/lib/stages/postgen/introspection/commandFacts.js +293 -0
- package/lib/stages/postgen/introspection/emit.js +192 -0
- package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
- package/lib/stages/postgen/introspection/markers.js +227 -0
- package/lib/stages/postgen/introspection/reflect.js +1214 -0
- package/lib/stages/postgen/introspection/report.js +1208 -0
- package/lib/stages/postgen/introspection/score.js +1067 -0
- package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
- package/lib/stages/postgen/parentEdge.js +156 -0
- package/lib/stages/postgen/postgen.js +115 -2
- package/lib/stages/postgen/ruleEngine.js +17 -0
- package/lib/stages/pregen/envAudit.js +42 -2
- package/lib/stages/pregen/pregen.js +1171 -6
- package/lib/validator/bomValidator.js +11 -2
- package/package.json +14 -14
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/scoring.d.ts +2 -8
- package/types/lib/audit/scoring.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +14 -0
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +2 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +8 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/buildLedger.d.ts +272 -0
- package/types/lib/core/buildLedger.d.ts.map +1 -0
- package/types/lib/core/deferredExit.d.ts +84 -0
- package/types/lib/core/deferredExit.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +22 -1
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +21 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/logger.d.ts +14 -1
- package/types/lib/core/logger.d.ts.map +1 -1
- package/types/lib/core/severity.d.ts +11 -0
- package/types/lib/core/severity.d.ts.map +1 -0
- package/types/lib/core/syncFileWriter.d.ts +33 -0
- package/types/lib/core/syncFileWriter.d.ts.map +1 -0
- package/types/lib/core/ui.d.ts +1 -1
- package/types/lib/core/ui.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +33 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/pypiClassifiers.d.ts +80 -0
- package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +20 -0
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/utils.d.ts +4 -4
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +5 -1
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/remote/dependency-track.d.ts +45 -4
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +8 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +20 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +9 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +189 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
- package/types/lib/inventory/osinfo.d.ts +83 -2
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/table.d.ts +4 -1
- package/types/lib/inventory/table.d.ts.map +1 -1
- package/types/lib/inventory/toolRequirements.d.ts +216 -0
- package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +71 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotationBuilder.d.ts +53 -0
- package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
- package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +57 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
- package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
- package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
- package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
- package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +2 -2
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +131 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
|
@@ -10,6 +10,14 @@ import {
|
|
|
10
10
|
isSecureMode,
|
|
11
11
|
readEnvironmentVariable,
|
|
12
12
|
} from "../core/activity.js";
|
|
13
|
+
import {
|
|
14
|
+
commandOutputText,
|
|
15
|
+
isLedgerEnabled,
|
|
16
|
+
LEDGER_EVENT_IMPACTS,
|
|
17
|
+
LEDGER_EVENT_KINDS,
|
|
18
|
+
recordDegradation,
|
|
19
|
+
recordLedgerEvent,
|
|
20
|
+
} from "../core/buildLedger.js";
|
|
13
21
|
import {
|
|
14
22
|
CARGO_CMD,
|
|
15
23
|
DOTNET_CMD,
|
|
@@ -31,8 +39,14 @@ import {
|
|
|
31
39
|
safeExistsSync,
|
|
32
40
|
safeSpawnSync,
|
|
33
41
|
} from "../core/fs.js";
|
|
42
|
+
import { diagnosticStream } from "../core/logger.js";
|
|
34
43
|
import { dirNameStr, isMac, isWin } from "../core/paths.js";
|
|
44
|
+
import { isValidSdkmanVersion } from "./jvmToolEnv.js";
|
|
35
45
|
import { applyPurl, tryBuildPurl } from "./purl.js";
|
|
46
|
+
import {
|
|
47
|
+
classifySpawnRestriction,
|
|
48
|
+
describeSpawnRestriction,
|
|
49
|
+
} from "./toolRequirements.js";
|
|
36
50
|
|
|
37
51
|
/**
|
|
38
52
|
* Git executable name used for all git invocations, overridable via GIT_CMD.
|
|
@@ -204,12 +218,17 @@ export function execGitCommand(dir, args) {
|
|
|
204
218
|
// Prepend hardening `-c` overrides before the subcommand and run git with a
|
|
205
219
|
// hardened environment so a scanned/untrusted repo cannot execute hooks,
|
|
206
220
|
// fsmonitor commands, or config-driven scripts.
|
|
221
|
+
// git is a real executable, so Windows can spawn it without a shell. Doing
|
|
222
|
+
// so keeps the pretty-format arguments intact: `%H`, `%n` and `%x1e` are
|
|
223
|
+
// cmd.exe variable-expansion syntax, which the shell-execution guard rightly
|
|
224
|
+
// refuses to pass through a shell.
|
|
207
225
|
return getCommandOutput(
|
|
208
226
|
GIT_COMMAND,
|
|
209
227
|
dir,
|
|
210
228
|
[...GIT_HARDENING_CONFIG_ARGS, ...args],
|
|
211
229
|
{
|
|
212
230
|
env: hardenedGitEnv(),
|
|
231
|
+
shell: false,
|
|
213
232
|
},
|
|
214
233
|
);
|
|
215
234
|
}
|
|
@@ -291,6 +310,60 @@ export function gitLogTrailers(dir, maxCount = 20) {
|
|
|
291
310
|
return commits;
|
|
292
311
|
}
|
|
293
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Record a successful version probe with the version the tool reported.
|
|
315
|
+
*
|
|
316
|
+
* @param {string} tool Tool name
|
|
317
|
+
* @param {string} ecosystem Ecosystem the tool belongs to
|
|
318
|
+
* @param {string} found Version string reported by the tool
|
|
319
|
+
*/
|
|
320
|
+
function recordToolProbeResolved(tool, ecosystem, found) {
|
|
321
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_RESOLVED, {
|
|
322
|
+
ecosystem,
|
|
323
|
+
tool,
|
|
324
|
+
found,
|
|
325
|
+
source: "PATH",
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Record a failed version probe. A probe the environment did not let run is
|
|
331
|
+
* degraded evidence — the tool may well be present — while only a probe that
|
|
332
|
+
* ran and found nothing counts as a missing tool.
|
|
333
|
+
*
|
|
334
|
+
* @param {string} tool Tool name
|
|
335
|
+
* @param {string} ecosystem Ecosystem the tool belongs to
|
|
336
|
+
* @param {string} command Probe command that failed
|
|
337
|
+
*/
|
|
338
|
+
function recordToolProbeFailure(tool, ecosystem, command) {
|
|
339
|
+
if (!isLedgerEnabled()) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const restriction = describeSpawnRestriction(command);
|
|
343
|
+
if (restriction) {
|
|
344
|
+
// A dry-run block is the user's own request, so it costs nothing; any
|
|
345
|
+
// other policy restriction hides a version the tool may well have.
|
|
346
|
+
const isDryRunBlock = classifySpawnRestriction(command) === "dry-run";
|
|
347
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.EVIDENCE_DEGRADED, {
|
|
348
|
+
ecosystem,
|
|
349
|
+
tool,
|
|
350
|
+
source: "PATH",
|
|
351
|
+
remediationId: isDryRunBlock ? "policy.dry-run" : "policy.secure-mode",
|
|
352
|
+
impact: isDryRunBlock
|
|
353
|
+
? LEDGER_EVENT_IMPACTS.NONE
|
|
354
|
+
: LEDGER_EVENT_IMPACTS.VERSIONS,
|
|
355
|
+
detail: `The ${tool} version probe could not run: ${restriction}.`,
|
|
356
|
+
});
|
|
357
|
+
} else {
|
|
358
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_MISSING, {
|
|
359
|
+
ecosystem,
|
|
360
|
+
tool,
|
|
361
|
+
source: "PATH",
|
|
362
|
+
detail: `No ${tool} command answered the version probe.`,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
294
367
|
/**
|
|
295
368
|
* Collect Java version and installed modules
|
|
296
369
|
*
|
|
@@ -302,7 +375,7 @@ export function collectJavaInfo(dir) {
|
|
|
302
375
|
const moduleDesc =
|
|
303
376
|
getCommandOutput(getJavaCommand(), dir, ["--list-modules"]) || "";
|
|
304
377
|
if (versionDesc) {
|
|
305
|
-
|
|
378
|
+
const info = {
|
|
306
379
|
type: "platform",
|
|
307
380
|
name: "java",
|
|
308
381
|
version: versionDesc.split("\n")[0].replace("java ", ""),
|
|
@@ -314,7 +387,10 @@ export function collectJavaInfo(dir) {
|
|
|
314
387
|
},
|
|
315
388
|
],
|
|
316
389
|
};
|
|
390
|
+
recordToolProbeResolved("java", "java", info.version);
|
|
391
|
+
return info;
|
|
317
392
|
}
|
|
393
|
+
recordToolProbeFailure("java", "java", getJavaCommand());
|
|
318
394
|
return undefined;
|
|
319
395
|
}
|
|
320
396
|
|
|
@@ -329,13 +405,16 @@ export function collectDotnetInfo(dir) {
|
|
|
329
405
|
const moduleDesc =
|
|
330
406
|
getCommandOutput(DOTNET_CMD, dir, ["--list-runtimes"]) || "";
|
|
331
407
|
if (versionDesc) {
|
|
332
|
-
|
|
408
|
+
const info = {
|
|
333
409
|
type: "platform",
|
|
334
410
|
name: "dotnet",
|
|
335
411
|
version: versionDesc.trim(),
|
|
336
412
|
description: moduleDesc.replaceAll("\n", "\\n"),
|
|
337
413
|
};
|
|
414
|
+
recordToolProbeResolved("dotnet", "csharp", info.version);
|
|
415
|
+
return info;
|
|
338
416
|
}
|
|
417
|
+
recordToolProbeFailure("dotnet", "csharp", DOTNET_CMD);
|
|
339
418
|
return undefined;
|
|
340
419
|
}
|
|
341
420
|
|
|
@@ -346,25 +425,21 @@ export function collectDotnetInfo(dir) {
|
|
|
346
425
|
* @returns Object containing python details
|
|
347
426
|
*/
|
|
348
427
|
export function collectPythonInfo(dir) {
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
"--version",
|
|
352
|
-
]);
|
|
428
|
+
const pythonCmd = getPythonCommand();
|
|
429
|
+
const versionDesc = getCommandOutput(pythonCmd, dir, ["-S", "--version"]);
|
|
353
430
|
const moduleDesc =
|
|
354
|
-
getCommandOutput(
|
|
355
|
-
"-I",
|
|
356
|
-
"-m",
|
|
357
|
-
"pip",
|
|
358
|
-
"--version",
|
|
359
|
-
]) || "";
|
|
431
|
+
getCommandOutput(pythonCmd, dir, ["-I", "-m", "pip", "--version"]) || "";
|
|
360
432
|
if (versionDesc) {
|
|
361
|
-
|
|
433
|
+
const info = {
|
|
362
434
|
type: "platform",
|
|
363
435
|
name: "python",
|
|
364
436
|
version: versionDesc.replace("Python ", ""),
|
|
365
437
|
description: moduleDesc.replaceAll("\n", "\\n"),
|
|
366
438
|
};
|
|
439
|
+
recordToolProbeResolved("python", "python", info.version);
|
|
440
|
+
return info;
|
|
367
441
|
}
|
|
442
|
+
recordToolProbeFailure("python", "python", pythonCmd);
|
|
368
443
|
return undefined;
|
|
369
444
|
}
|
|
370
445
|
|
|
@@ -385,6 +460,9 @@ export function collectNodeInfo(dir) {
|
|
|
385
460
|
const moduleDesc = getCommandOutput(NPM_CMD, dir, ["--version"]);
|
|
386
461
|
if (moduleDesc) {
|
|
387
462
|
nodeInfo.description = `npm: ${moduleDesc}`;
|
|
463
|
+
recordToolProbeResolved("npm", "npm", moduleDesc);
|
|
464
|
+
} else {
|
|
465
|
+
recordToolProbeFailure("npm", "npm", NPM_CMD);
|
|
388
466
|
}
|
|
389
467
|
return nodeInfo;
|
|
390
468
|
}
|
|
@@ -399,13 +477,16 @@ export function collectGccInfo(dir) {
|
|
|
399
477
|
const versionDesc = getCommandOutput(GCC_CMD, dir, ["--version"]);
|
|
400
478
|
const moduleDesc = getCommandOutput(GCC_CMD, dir, ["-print-search-dirs"]);
|
|
401
479
|
if (versionDesc) {
|
|
402
|
-
|
|
480
|
+
const info = {
|
|
403
481
|
type: "platform",
|
|
404
482
|
name: "gcc",
|
|
405
483
|
version: versionDesc.split("\n")[0],
|
|
406
484
|
description: (moduleDesc || "").replaceAll("\n", "\\n"),
|
|
407
485
|
};
|
|
486
|
+
recordToolProbeResolved("gcc", "c", info.version);
|
|
487
|
+
return info;
|
|
408
488
|
}
|
|
489
|
+
recordToolProbeFailure("gcc", "c", GCC_CMD);
|
|
409
490
|
return undefined;
|
|
410
491
|
}
|
|
411
492
|
|
|
@@ -419,13 +500,16 @@ export function collectRustInfo(dir) {
|
|
|
419
500
|
const versionDesc = getCommandOutput(RUSTC_CMD, dir, ["--version"]);
|
|
420
501
|
const moduleDesc = getCommandOutput(CARGO_CMD, dir, ["--version"]);
|
|
421
502
|
if (versionDesc) {
|
|
422
|
-
|
|
503
|
+
const info = {
|
|
423
504
|
type: "platform",
|
|
424
505
|
name: "rustc",
|
|
425
506
|
version: versionDesc.trim(),
|
|
426
507
|
description: (moduleDesc || "").trim(),
|
|
427
508
|
};
|
|
509
|
+
recordToolProbeResolved("rustc", "rust", info.version);
|
|
510
|
+
return info;
|
|
428
511
|
}
|
|
512
|
+
recordToolProbeFailure("rustc", "rust", RUSTC_CMD);
|
|
429
513
|
return undefined;
|
|
430
514
|
}
|
|
431
515
|
|
|
@@ -438,12 +522,15 @@ export function collectRustInfo(dir) {
|
|
|
438
522
|
export function collectGoInfo(dir) {
|
|
439
523
|
const versionDesc = getCommandOutput(GO_CMD, dir, ["version"]);
|
|
440
524
|
if (versionDesc) {
|
|
441
|
-
|
|
525
|
+
const info = {
|
|
442
526
|
type: "platform",
|
|
443
527
|
name: "go",
|
|
444
528
|
version: versionDesc.trim(),
|
|
445
529
|
};
|
|
530
|
+
recordToolProbeResolved("go", "go", info.version);
|
|
531
|
+
return info;
|
|
446
532
|
}
|
|
533
|
+
recordToolProbeFailure("go", "go", GO_CMD);
|
|
447
534
|
return undefined;
|
|
448
535
|
}
|
|
449
536
|
|
|
@@ -456,12 +543,15 @@ export function collectGoInfo(dir) {
|
|
|
456
543
|
export function collectSwiftInfo(dir) {
|
|
457
544
|
const versionDesc = getCommandOutput(SWIFT_CMD, dir, ["--version"]);
|
|
458
545
|
if (versionDesc) {
|
|
459
|
-
|
|
546
|
+
const info = {
|
|
460
547
|
type: "platform",
|
|
461
548
|
name: "swift",
|
|
462
549
|
version: versionDesc.trim(),
|
|
463
550
|
};
|
|
551
|
+
recordToolProbeResolved("swift", "swift", info.version);
|
|
552
|
+
return info;
|
|
464
553
|
}
|
|
554
|
+
recordToolProbeFailure("swift", "swift", SWIFT_CMD);
|
|
465
555
|
return undefined;
|
|
466
556
|
}
|
|
467
557
|
|
|
@@ -474,12 +564,15 @@ export function collectSwiftInfo(dir) {
|
|
|
474
564
|
export function collectRubyInfo(dir) {
|
|
475
565
|
const versionDesc = getCommandOutput(RUBY_CMD, dir, ["--version"]);
|
|
476
566
|
if (versionDesc) {
|
|
477
|
-
|
|
567
|
+
const info = {
|
|
478
568
|
type: "platform",
|
|
479
569
|
name: "ruby",
|
|
480
570
|
version: versionDesc.trim(),
|
|
481
571
|
};
|
|
572
|
+
recordToolProbeResolved("ruby", "ruby", info.version);
|
|
573
|
+
return info;
|
|
482
574
|
}
|
|
575
|
+
recordToolProbeFailure("ruby", "ruby", RUBY_CMD);
|
|
483
576
|
return undefined;
|
|
484
577
|
}
|
|
485
578
|
|
|
@@ -666,6 +759,17 @@ export function installSdkmanTool(toolType, toolName) {
|
|
|
666
759
|
return false;
|
|
667
760
|
}
|
|
668
761
|
toolName = getSdkmanToolFullname(toolName);
|
|
762
|
+
// The version is interpolated into an interactive shell command, so reject
|
|
763
|
+
// any identifier that is not a single safe token before spawning.
|
|
764
|
+
if (
|
|
765
|
+
!/^[a-z][a-z0-9-]*$/.test(`${toolType}`) ||
|
|
766
|
+
!isValidSdkmanVersion(toolName)
|
|
767
|
+
) {
|
|
768
|
+
console.log(
|
|
769
|
+
`Unable to install ${toolType} with an invalid version identifier '${toolName}'. Use exact sdkman version identifiers such as '3.9.9' or '21.0.7-tem'.`,
|
|
770
|
+
);
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
669
773
|
let result;
|
|
670
774
|
if (!isSdkmanToolAvailable(toolType, toolName)) {
|
|
671
775
|
let installDir = "";
|
|
@@ -919,9 +1023,12 @@ export function bundleInstallWithDocker(rubyVersion, cdxgenGemHome, filePath) {
|
|
|
919
1023
|
"install",
|
|
920
1024
|
];
|
|
921
1025
|
console.log(`Performing bundle install with: ${ociCmd}`);
|
|
1026
|
+
// The container's own output is a diagnostic. Inheriting stdout would let it
|
|
1027
|
+
// interleave with the BOM document when the caller asked for `-o -`, so the
|
|
1028
|
+
// child's stdout is pointed at the diagnostic stream instead.
|
|
922
1029
|
const result = safeSpawnSync(ociCmd, ociArgs, {
|
|
923
1030
|
shell: isWin,
|
|
924
|
-
stdio: "inherit",
|
|
1031
|
+
stdio: ["inherit", diagnosticStream, "inherit"],
|
|
925
1032
|
});
|
|
926
1033
|
if (result.error || result.status !== 0) {
|
|
927
1034
|
return false;
|
|
@@ -964,6 +1071,14 @@ export function installRubyVersion(rubyVersion, filePath) {
|
|
|
964
1071
|
if (result.stderr) {
|
|
965
1072
|
console.log(result.stderr);
|
|
966
1073
|
}
|
|
1074
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.COMMAND_FAILED, {
|
|
1075
|
+
ecosystem: "ruby",
|
|
1076
|
+
tool: "rbenv",
|
|
1077
|
+
command: `rbenv local ${rubyVersion}`,
|
|
1078
|
+
exitCode: typeof result.status === "number" ? result.status : undefined,
|
|
1079
|
+
detail: `Selecting Ruby ${rubyVersion} with rbenv local did not succeed.`,
|
|
1080
|
+
outputExcerpt: commandOutputText(result),
|
|
1081
|
+
});
|
|
967
1082
|
}
|
|
968
1083
|
if (result.status === 0) {
|
|
969
1084
|
return { fullToolBinDir, status: true };
|
|
@@ -971,6 +1086,14 @@ export function installRubyVersion(rubyVersion, filePath) {
|
|
|
971
1086
|
}
|
|
972
1087
|
// Check if we're trying to install Ruby 1.x or 2.x
|
|
973
1088
|
if (rubyVersion.startsWith("1.")) {
|
|
1089
|
+
recordDegradation("ruby.version.unsupported-host", {
|
|
1090
|
+
ecosystem: "ruby",
|
|
1091
|
+
tool: "ruby",
|
|
1092
|
+
impact: "transitive-deps",
|
|
1093
|
+
wanted: rubyVersion,
|
|
1094
|
+
detail:
|
|
1095
|
+
"Building Ruby 1.x on this host is unsupported; a matching legacy container image is required to build this project.",
|
|
1096
|
+
});
|
|
974
1097
|
console.log(
|
|
975
1098
|
`Ruby version ${rubyVersion} requires very old versions of Linux such as debian:8. Consider using the container image "ghcr.io/cdxgen/debian-ruby18:master" to build the application first and then invoke cdxgen with the arguments "--lifecycle pre-build".`,
|
|
976
1099
|
);
|
|
@@ -979,6 +1102,14 @@ export function installRubyVersion(rubyVersion, filePath) {
|
|
|
979
1102
|
rubyVersion.startsWith("2.") &&
|
|
980
1103
|
readEnvironmentVariable("CDXGEN_IN_CONTAINER") !== "true"
|
|
981
1104
|
) {
|
|
1105
|
+
recordDegradation("ruby.build-deps.missing", {
|
|
1106
|
+
ecosystem: "ruby",
|
|
1107
|
+
tool: "ruby",
|
|
1108
|
+
impact: "transitive-deps",
|
|
1109
|
+
wanted: rubyVersion,
|
|
1110
|
+
detail:
|
|
1111
|
+
"Building Ruby 2.x on this host requires development libraries that are not installed.",
|
|
1112
|
+
});
|
|
982
1113
|
console.log(
|
|
983
1114
|
`Installing Ruby version ${rubyVersion} requires specific development libraries. Consider using the custom container image "ghcr.io/cdxgen/cdxgen-debian-ruby26:v13" instead.`,
|
|
984
1115
|
);
|
|
@@ -992,6 +1123,12 @@ export function installRubyVersion(rubyVersion, filePath) {
|
|
|
992
1123
|
`To speed up this step, use bind mounts. Example: "--mount type=bind,src=/tmp/rbenv,dst=/root/.rbenv/versions/${rubyVersion}"`,
|
|
993
1124
|
);
|
|
994
1125
|
}
|
|
1126
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.COMMAND_ATTEMPTED, {
|
|
1127
|
+
ecosystem: "ruby",
|
|
1128
|
+
tool: "rbenv",
|
|
1129
|
+
command: `rbenv install ${rubyVersion}`,
|
|
1130
|
+
detail: `Installing Ruby ${rubyVersion} with rbenv.`,
|
|
1131
|
+
});
|
|
995
1132
|
const result = safeSpawnSync(
|
|
996
1133
|
readEnvironmentVariable("RBENV_CMD") || "rbenv",
|
|
997
1134
|
["install", rubyVersion],
|
|
@@ -1000,6 +1137,14 @@ export function installRubyVersion(rubyVersion, filePath) {
|
|
|
1000
1137
|
},
|
|
1001
1138
|
);
|
|
1002
1139
|
if (result.error || result.status !== 0) {
|
|
1140
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.COMMAND_FAILED, {
|
|
1141
|
+
ecosystem: "ruby",
|
|
1142
|
+
tool: "rbenv",
|
|
1143
|
+
command: `rbenv install ${rubyVersion}`,
|
|
1144
|
+
exitCode: typeof result.status === "number" ? result.status : undefined,
|
|
1145
|
+
detail: `The rbenv install of Ruby ${rubyVersion} did not succeed.`,
|
|
1146
|
+
outputExcerpt: commandOutputText(result),
|
|
1147
|
+
});
|
|
1003
1148
|
if (result.stdout) {
|
|
1004
1149
|
console.log(result.stdout);
|
|
1005
1150
|
}
|
|
@@ -1125,6 +1270,13 @@ export function performBundleInstall(
|
|
|
1125
1270
|
console.log(
|
|
1126
1271
|
`Invoking ${bundleCommand} ${installArgs.join(" ")} from ${basePath} with GEM_HOME ${cdxgenGemHome}. Please wait ...`,
|
|
1127
1272
|
);
|
|
1273
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.COMMAND_ATTEMPTED, {
|
|
1274
|
+
ecosystem: "ruby",
|
|
1275
|
+
tool: "bundler",
|
|
1276
|
+
command: `${bundleCommand} ${installArgs.join(" ")}`,
|
|
1277
|
+
path: basePath,
|
|
1278
|
+
detail: "Installing the project dependencies with bundler.",
|
|
1279
|
+
});
|
|
1128
1280
|
let result = safeSpawnSync(bundleCommand, installArgs, {
|
|
1129
1281
|
shell: isWin,
|
|
1130
1282
|
cwd: basePath,
|
|
@@ -1134,6 +1286,15 @@ export function performBundleInstall(
|
|
|
1134
1286
|
},
|
|
1135
1287
|
});
|
|
1136
1288
|
if (result.error || result.status !== 0) {
|
|
1289
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.COMMAND_FAILED, {
|
|
1290
|
+
ecosystem: "ruby",
|
|
1291
|
+
tool: "bundler",
|
|
1292
|
+
command: `${bundleCommand} ${installArgs.join(" ")}`,
|
|
1293
|
+
path: basePath,
|
|
1294
|
+
exitCode: typeof result.status === "number" ? result.status : undefined,
|
|
1295
|
+
detail: "The bundle install did not succeed.",
|
|
1296
|
+
outputExcerpt: commandOutputText(result),
|
|
1297
|
+
});
|
|
1137
1298
|
let pythonWarningShown = false;
|
|
1138
1299
|
let rubyVersionWarningShown = false;
|
|
1139
1300
|
if (
|
|
@@ -1180,6 +1341,13 @@ export function performBundleInstall(
|
|
|
1180
1341
|
);
|
|
1181
1342
|
}
|
|
1182
1343
|
console.log(`${bundleCommand}`);
|
|
1344
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.COMMAND_ATTEMPTED, {
|
|
1345
|
+
ecosystem: "ruby",
|
|
1346
|
+
tool: "bundler",
|
|
1347
|
+
command: `${bundleCommand} ${updateArgs.join(" ")}`,
|
|
1348
|
+
path: basePath,
|
|
1349
|
+
detail: "Updating the outdated lockfile with bundler.",
|
|
1350
|
+
});
|
|
1183
1351
|
result = safeSpawnSync(bundleCommand, updateArgs, {
|
|
1184
1352
|
shell: isWin,
|
|
1185
1353
|
cwd: basePath,
|
|
@@ -1189,6 +1357,16 @@ export function performBundleInstall(
|
|
|
1189
1357
|
},
|
|
1190
1358
|
});
|
|
1191
1359
|
if (result.error || result.status !== 0) {
|
|
1360
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.COMMAND_FAILED, {
|
|
1361
|
+
ecosystem: "ruby",
|
|
1362
|
+
tool: "bundler",
|
|
1363
|
+
command: `${bundleCommand} ${updateArgs.join(" ")}`,
|
|
1364
|
+
path: basePath,
|
|
1365
|
+
exitCode:
|
|
1366
|
+
typeof result.status === "number" ? result.status : undefined,
|
|
1367
|
+
detail: "The bundle update did not succeed.",
|
|
1368
|
+
outputExcerpt: commandOutputText(result),
|
|
1369
|
+
});
|
|
1192
1370
|
console.log("------------");
|
|
1193
1371
|
if (result.stdout) {
|
|
1194
1372
|
console.log(result.stdout);
|
|
@@ -1736,6 +1914,20 @@ export function getRuntimeInformation() {
|
|
|
1736
1914
|
runtimeInfo.version = "N/A";
|
|
1737
1915
|
}
|
|
1738
1916
|
|
|
1917
|
+
// The runtime hosting the cdxgen run is part of the report's
|
|
1918
|
+
// reproducibility context, so it is recorded alongside the scanned tools.
|
|
1919
|
+
const runtimeTools = { Deno: "deno", Bun: "bun", "Node.js": "node" };
|
|
1920
|
+
const runtimeTool = runtimeTools[runtimeInfo.runtime];
|
|
1921
|
+
if (runtimeTool && runtimeInfo.version && runtimeInfo.version !== "N/A") {
|
|
1922
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_RESOLVED, {
|
|
1923
|
+
ecosystem: "generic",
|
|
1924
|
+
tool: runtimeTool,
|
|
1925
|
+
found: runtimeInfo.version,
|
|
1926
|
+
source: "runtime",
|
|
1927
|
+
detail:
|
|
1928
|
+
"The runtime hosting this cdxgen run, recorded for report reproducibility.",
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1739
1931
|
return runtimeInfo;
|
|
1740
1932
|
}
|
|
1741
1933
|
|
|
@@ -34,10 +34,13 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import {
|
|
37
|
+
BLOCKED_HOST_ERROR_CODE,
|
|
37
38
|
cdxgenAgent,
|
|
38
39
|
DEBUG_MODE,
|
|
40
|
+
DRY_RUN_ERROR_CODE,
|
|
39
41
|
readEnvironmentVariable,
|
|
40
42
|
} from "../core/activity.js";
|
|
43
|
+
import { isLedgerEnabled, recordDegradation } from "../core/buildLedger.js";
|
|
41
44
|
import { resolveCacheDir } from "./cacheDir.js";
|
|
42
45
|
import { cdxrsAvailable, runCdxrs } from "./cdxrs.js";
|
|
43
46
|
import {
|
|
@@ -545,6 +548,59 @@ async function issueWithRetries(request, limiter) {
|
|
|
545
548
|
}
|
|
546
549
|
}
|
|
547
550
|
|
|
551
|
+
/** Policy and connectivity degradations already recorded this run; these describe the run, not one URL. */
|
|
552
|
+
const recordedPolicyDegradations = new Set();
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Record a run-level policy or connectivity degradation once per process. A
|
|
556
|
+
* batch touches many URLs, but "dry-run blocked the network", "a host was not
|
|
557
|
+
* permitted" and "the network is unreachable" are properties of the run, so
|
|
558
|
+
* only the first occurrence is recorded and the report reads one line, not
|
|
559
|
+
* one per URL.
|
|
560
|
+
*
|
|
561
|
+
* @param {string} remediationId Remediation id from data/remediations.json
|
|
562
|
+
* @param {Object} fields Ledger event fields
|
|
563
|
+
* @returns {void}
|
|
564
|
+
*/
|
|
565
|
+
function recordPolicyDegradationOnce(remediationId, fields) {
|
|
566
|
+
if (!isLedgerEnabled() || recordedPolicyDegradations.has(remediationId)) {
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
recordedPolicyDegradations.add(remediationId);
|
|
570
|
+
recordDegradation(remediationId, fields);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Record the run-level policy or connectivity condition carried by a caught
|
|
575
|
+
* fetch error, if it names one. Shared by the batch retry loop and by the
|
|
576
|
+
* serial enrichment paths that catch and swallow the same typed errors.
|
|
577
|
+
*
|
|
578
|
+
* @param {Error} err Error caught from a `cdxgenAgent` request.
|
|
579
|
+
* @returns {void}
|
|
580
|
+
*/
|
|
581
|
+
export function recordPolicyDegradationFromError(err) {
|
|
582
|
+
if (err?.code === DRY_RUN_ERROR_CODE) {
|
|
583
|
+
recordPolicyDegradationOnce("policy.dry-run", {
|
|
584
|
+
ecosystem: "generic",
|
|
585
|
+
impact: "none",
|
|
586
|
+
detail: `Dry-run mode blocked a registry request: ${err.message}`,
|
|
587
|
+
});
|
|
588
|
+
} else if (err?.code === BLOCKED_HOST_ERROR_CODE) {
|
|
589
|
+
recordPolicyDegradationOnce("policy.host-blocked", {
|
|
590
|
+
ecosystem: "generic",
|
|
591
|
+
impact: "licenses",
|
|
592
|
+
detail: `A registry request was blocked by policy: ${err.message}`,
|
|
593
|
+
});
|
|
594
|
+
} else if (err?.name === "RequestError") {
|
|
595
|
+
recordPolicyDegradationOnce("policy.offline", {
|
|
596
|
+
ecosystem: "generic",
|
|
597
|
+
impact: "licenses",
|
|
598
|
+
detail:
|
|
599
|
+
"A registry request failed with a transport error, so registry metadata and license enrichment may be missing.",
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
548
604
|
/**
|
|
549
605
|
* Classify an error thrown by `cdxgenAgent.get` into a retry decision and the
|
|
550
606
|
* batch entry to record when retries are exhausted.
|
|
@@ -553,6 +609,7 @@ async function issueWithRetries(request, limiter) {
|
|
|
553
609
|
* @returns {{retryable: boolean, delay: number|null, entry: BatchEntry}}
|
|
554
610
|
*/
|
|
555
611
|
function classifyAgentError(err) {
|
|
612
|
+
recordPolicyDegradationFromError(err);
|
|
556
613
|
// Secure-mode and dry-run blocks must not be retried, and the caller's own
|
|
557
614
|
// fallback `cdxgenAgent.get` will surface the same error. Recording this as
|
|
558
615
|
// non-definite lets the caller's catch run exactly as it would on the serial
|