@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
|
@@ -5,6 +5,7 @@ import { build } from "@cdxgen/cdx-purl";
|
|
|
5
5
|
import { parseEDNString } from "edn-data";
|
|
6
6
|
|
|
7
7
|
import { readEnvironmentVariable } from "../core/activity.js";
|
|
8
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
8
9
|
import { includeMavenTestScope } from "../core/env.js";
|
|
9
10
|
import { thoughtLog } from "../core/logger.js";
|
|
10
11
|
import { applyPurl } from "../inventory/purl.js";
|
|
@@ -309,6 +310,14 @@ export function parseMavenTreeJson(rawOutput, pomFile) {
|
|
|
309
310
|
try {
|
|
310
311
|
rootNode = JSON.parse(rawOutput);
|
|
311
312
|
} catch (_err) {
|
|
313
|
+
recordDegradation("jvm.maven.tree-unparseable", {
|
|
314
|
+
ecosystem: "java",
|
|
315
|
+
tool: "maven",
|
|
316
|
+
impact: "transitive-deps",
|
|
317
|
+
path: pomFile,
|
|
318
|
+
detail:
|
|
319
|
+
"The maven dependency:tree JSON output could not be parsed, so the text output is parsed instead.",
|
|
320
|
+
});
|
|
312
321
|
thoughtLog("Unable to parse Maven dependency:tree JSON output");
|
|
313
322
|
return {};
|
|
314
323
|
}
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
relative,
|
|
9
9
|
resolve,
|
|
10
10
|
} from "node:path";
|
|
11
|
-
import process from "node:process";
|
|
12
11
|
|
|
13
12
|
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
14
13
|
import { parse as _load } from "yaml";
|
|
@@ -18,6 +17,8 @@ import {
|
|
|
18
17
|
DEBUG_MODE,
|
|
19
18
|
readEnvironmentVariable,
|
|
20
19
|
} from "../core/activity.js";
|
|
20
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
21
|
+
import { deferFailOnError } from "../core/deferredExit.js";
|
|
21
22
|
import { shouldFetchLicense } from "../core/env.js";
|
|
22
23
|
import {
|
|
23
24
|
safeExistsSync,
|
|
@@ -2652,6 +2653,15 @@ export function executePodCommand(parameters, path, options) {
|
|
|
2652
2653
|
);
|
|
2653
2654
|
}
|
|
2654
2655
|
if (readEnvironmentVariable("CDXGEN_IN_CONTAINER") !== "true") {
|
|
2656
|
+
recordDegradation("cocoa.pods.missing", {
|
|
2657
|
+
ecosystem: "cocoa",
|
|
2658
|
+
tool: "pod",
|
|
2659
|
+
impact: "transitive-deps",
|
|
2660
|
+
command: `pod ${parameters.join(" ")}`,
|
|
2661
|
+
exitCode: typeof result.status === "number" ? result.status : undefined,
|
|
2662
|
+
detail:
|
|
2663
|
+
"The pod command did not succeed, so the cocoapods dependency graph was not resolved.",
|
|
2664
|
+
});
|
|
2655
2665
|
console.log(
|
|
2656
2666
|
"Consider using the cdxgen container image (`ghcr.io/cdxgen/cdxgen`), which includes cocoapods and additional build tools.",
|
|
2657
2667
|
);
|
|
@@ -2667,7 +2677,11 @@ export function executePodCommand(parameters, path, options) {
|
|
|
2667
2677
|
if (result.stderr) {
|
|
2668
2678
|
console.log(result.stderr);
|
|
2669
2679
|
}
|
|
2670
|
-
options
|
|
2680
|
+
deferFailOnError(options, {
|
|
2681
|
+
ecosystem: "cocoa",
|
|
2682
|
+
tool: "pod",
|
|
2683
|
+
detail: "the pod command failed",
|
|
2684
|
+
});
|
|
2671
2685
|
}
|
|
2672
2686
|
}
|
|
2673
2687
|
return result;
|
|
@@ -8,6 +8,7 @@ import toml from "@iarna/toml";
|
|
|
8
8
|
import { parse as _load } from "yaml";
|
|
9
9
|
|
|
10
10
|
import { DEBUG_MODE } from "../core/activity.js";
|
|
11
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
11
12
|
import { getAllFiles, safeExistsSync, safeSpawnSync } from "../core/fs.js";
|
|
12
13
|
import { PYTHON_STD_MODULES } from "../core/state.js";
|
|
13
14
|
import { tryBuildPurl } from "../inventory/purl.js";
|
|
@@ -27,6 +28,41 @@ import {
|
|
|
27
28
|
normalizePyLockRegistry,
|
|
28
29
|
} from "./pylockutils.js";
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* TOML lock files a specific Python manager owns, mapped to the manager and
|
|
33
|
+
* the remediation id that ranks its repair. The failing file itself is the
|
|
34
|
+
* detection: the lock command that regenerates it belongs to the manager
|
|
35
|
+
* that wrote it.
|
|
36
|
+
*
|
|
37
|
+
* @type {Record<string, {manager: string, remediationId: string}>}
|
|
38
|
+
*/
|
|
39
|
+
const PY_LOCKFILE_DEGRADATIONS = {
|
|
40
|
+
"uv.lock": {
|
|
41
|
+
manager: "uv",
|
|
42
|
+
remediationId: "python.lockfile-unparseable.uv",
|
|
43
|
+
},
|
|
44
|
+
"poetry.lock": {
|
|
45
|
+
manager: "poetry",
|
|
46
|
+
remediationId: "python.lockfile-unparseable.poetry",
|
|
47
|
+
},
|
|
48
|
+
"pdm.lock": {
|
|
49
|
+
manager: "pdm",
|
|
50
|
+
remediationId: "python.lockfile-unparseable.pdm",
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The repair for a TOML lock file that names no single manager, such as
|
|
56
|
+
* PEP 751's `pylock.toml`. Its lock command carries the manager variable, so
|
|
57
|
+
* the shaping fills in whichever manager the project turns out to use.
|
|
58
|
+
*
|
|
59
|
+
* @type {{manager: string, remediationId: string}}
|
|
60
|
+
*/
|
|
61
|
+
const GENERIC_PY_LOCKFILE_DEGRADATION = {
|
|
62
|
+
manager: "python",
|
|
63
|
+
remediationId: "python.lockfile-unparseable",
|
|
64
|
+
};
|
|
65
|
+
|
|
30
66
|
/**
|
|
31
67
|
* Method to parse python requires_dist attribute found in pypi setup.py
|
|
32
68
|
*
|
|
@@ -444,7 +480,12 @@ export function parsePyProjectTomlFile(tomlFile) {
|
|
|
444
480
|
for (const agroup of Object.keys(tomlData["dependency-groups"])) {
|
|
445
481
|
tomlData["dependency-groups"][agroup].forEach((p) => {
|
|
446
482
|
if (typeof p === "string" || p instanceof String) {
|
|
447
|
-
const pname =
|
|
483
|
+
const pname = normalizePythonDependencyKey(
|
|
484
|
+
p.split(/(==|<=|~=|>=)/)[0].split(" ")[0],
|
|
485
|
+
);
|
|
486
|
+
if (!pname) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
448
489
|
if (!groupDepsKeys[pname]) {
|
|
449
490
|
groupDepsKeys[pname] = [];
|
|
450
491
|
}
|
|
@@ -466,8 +507,13 @@ export function parsePyProjectTomlFile(tomlFile) {
|
|
|
466
507
|
}
|
|
467
508
|
if (tomlData?.tool?.poetry?.dependencies) {
|
|
468
509
|
for (const adep of Object.keys(tomlData?.tool?.poetry?.dependencies)) {
|
|
510
|
+
// poetry keys keep the author's casing (Click = "^8.1"), while every
|
|
511
|
+
// consumer of directDepsKeys looks entries up with the normalised
|
|
512
|
+
// (lowercase, hyphenated) package name, so normalise at the source.
|
|
513
|
+
const poetryDepKey = normalizePythonDependencyKey(adep);
|
|
469
514
|
if (
|
|
470
|
-
!
|
|
515
|
+
!poetryDepKey ||
|
|
516
|
+
[
|
|
471
517
|
"python",
|
|
472
518
|
"py",
|
|
473
519
|
"pytest",
|
|
@@ -475,43 +521,50 @@ export function parsePyProjectTomlFile(tomlFile) {
|
|
|
475
521
|
"ruff",
|
|
476
522
|
"setuptools",
|
|
477
523
|
"bandit",
|
|
478
|
-
].includes(
|
|
524
|
+
].includes(poetryDepKey)
|
|
479
525
|
) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
directDepsKeys[poetryDepKey] = true;
|
|
529
|
+
const poetryDependency = tomlData.tool.poetry.dependencies[adep];
|
|
530
|
+
if (poetryDependency?.git) {
|
|
531
|
+
recordPythonDependencySource(
|
|
532
|
+
dependencySourceMap,
|
|
533
|
+
adep,
|
|
534
|
+
"git",
|
|
535
|
+
poetryDependency.git,
|
|
536
|
+
);
|
|
537
|
+
} else if (poetryDependency?.url) {
|
|
538
|
+
recordPythonDependencySource(
|
|
539
|
+
dependencySourceMap,
|
|
540
|
+
adep,
|
|
541
|
+
"url",
|
|
542
|
+
poetryDependency.url,
|
|
543
|
+
);
|
|
544
|
+
} else if (poetryDependency?.path) {
|
|
545
|
+
recordPythonDependencySource(
|
|
546
|
+
dependencySourceMap,
|
|
547
|
+
adep,
|
|
548
|
+
"path",
|
|
549
|
+
poetryDependency.path,
|
|
550
|
+
);
|
|
504
551
|
}
|
|
505
552
|
} // for
|
|
553
|
+
// Group keys are matched against the normalised package names from the
|
|
554
|
+
// lock file, so they are normalised here too.
|
|
506
555
|
if (tomlData?.tool?.poetry?.group) {
|
|
507
556
|
for (const agroup of Object.keys(tomlData.tool.poetry.group)) {
|
|
508
557
|
for (const adep of Object.keys(
|
|
509
558
|
tomlData.tool.poetry.group[agroup]?.dependencies,
|
|
510
559
|
)) {
|
|
511
|
-
|
|
512
|
-
|
|
560
|
+
const poetryGroupDepKey = normalizePythonDependencyKey(adep);
|
|
561
|
+
if (!poetryGroupDepKey) {
|
|
562
|
+
continue;
|
|
513
563
|
}
|
|
514
|
-
groupDepsKeys[
|
|
564
|
+
if (!groupDepsKeys[poetryGroupDepKey]) {
|
|
565
|
+
groupDepsKeys[poetryGroupDepKey] = [];
|
|
566
|
+
}
|
|
567
|
+
groupDepsKeys[poetryGroupDepKey].push(agroup);
|
|
515
568
|
const poetryDependency =
|
|
516
569
|
tomlData.tool.poetry.group[agroup]?.dependencies?.[adep];
|
|
517
570
|
if (poetryDependency?.git) {
|
|
@@ -780,7 +833,27 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
|
|
|
780
833
|
dependencySourceMap = pyProjMap.dependencySourceMap || {};
|
|
781
834
|
parentComponent = pyProjMap.parentComponent;
|
|
782
835
|
workspacePaths = pyProjMap.workspacePaths;
|
|
783
|
-
|
|
836
|
+
// `parsePyProjectTomlFile` reports a parse failure and returns an empty map, so a
|
|
837
|
+
// pyproject.toml this parser cannot read leaves `parentComponent` undefined while
|
|
838
|
+
// the lock file still declares workspaces. Building the workspace tree then throws
|
|
839
|
+
// on the first dereference and takes the whole SBOM with it. Degrade to a flattened
|
|
840
|
+
// tree instead - the caller supplies its own parent component, so the components
|
|
841
|
+
// themselves are still reported.
|
|
842
|
+
if (workspacePaths?.length && !parentComponent) {
|
|
843
|
+
console.log(
|
|
844
|
+
`Unable to read a parent component from ${pyProjectFile}, so the workspace structure declared in ${basename(lockFile)} cannot be reconstructed.`,
|
|
845
|
+
);
|
|
846
|
+
console.log(
|
|
847
|
+
"The dependency tree in the generated SBOM will be flattened and therefore incorrect.",
|
|
848
|
+
);
|
|
849
|
+
recordDegradation("python.pyproject-unparseable", {
|
|
850
|
+
ecosystem: "python",
|
|
851
|
+
impact: "transitive-deps",
|
|
852
|
+
path: pyProjectFile,
|
|
853
|
+
detail: `${basename(pyProjectFile)} could not be parsed, so the workspace structure declared in ${basename(lockFile)} was not reconstructed.`,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
if (workspacePaths?.length && parentComponent) {
|
|
784
857
|
if (!hasWorkspaces) {
|
|
785
858
|
hasWorkspaces = true;
|
|
786
859
|
}
|
|
@@ -840,7 +913,12 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
|
|
|
840
913
|
] = awpyproj;
|
|
841
914
|
}
|
|
842
915
|
}
|
|
843
|
-
|
|
916
|
+
// The block above already treats an unreadable workspace pyproject.toml as
|
|
917
|
+
// tolerable, so this must not dereference the component it skipped. A
|
|
918
|
+
// workspace member whose manifest cannot be parsed contributes no parent
|
|
919
|
+
// ref, and recording `undefined` as the parent of its dependencies would
|
|
920
|
+
// corrupt the tree for the members that did parse.
|
|
921
|
+
const wparentComponentRef = wcompMap?.parentComponent?.["bom-ref"];
|
|
844
922
|
if (wcompMap?.dependencySourceMap) {
|
|
845
923
|
Object.assign(
|
|
846
924
|
workspaceDependencySourceMap,
|
|
@@ -848,7 +926,7 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
|
|
|
848
926
|
);
|
|
849
927
|
}
|
|
850
928
|
// Track the parents of workspace direct dependencies
|
|
851
|
-
if (wcompMap?.directDepsKeys) {
|
|
929
|
+
if (wcompMap?.directDepsKeys && wparentComponentRef) {
|
|
852
930
|
for (const wdd of Object.keys(wcompMap?.directDepsKeys)) {
|
|
853
931
|
if (!pkgParentMap[wdd]) {
|
|
854
932
|
pkgParentMap[wdd] = [];
|
|
@@ -865,6 +943,20 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
|
|
|
865
943
|
lockTomlObj = toml.parse(lockData);
|
|
866
944
|
} catch (err) {
|
|
867
945
|
if (lockFile) {
|
|
946
|
+
// The failing file names the manager whose lock command regenerates
|
|
947
|
+
// it. A standards-named lock file (PEP 751 `pylock.toml`) belongs to
|
|
948
|
+
// no single manager, so it falls back to the generic repair, whose
|
|
949
|
+
// lock command the shaping resolves from whichever manager the
|
|
950
|
+
// project turns out to use.
|
|
951
|
+
const degradation =
|
|
952
|
+
PY_LOCKFILE_DEGRADATIONS[basename(lockFile)] ||
|
|
953
|
+
GENERIC_PY_LOCKFILE_DEGRADATION;
|
|
954
|
+
recordDegradation(degradation.remediationId, {
|
|
955
|
+
ecosystem: "python",
|
|
956
|
+
impact: "transitive-deps",
|
|
957
|
+
path: lockFile,
|
|
958
|
+
detail: `The ${degradation.manager} lock file could not be parsed, so no locked versions were captured from it.`,
|
|
959
|
+
});
|
|
868
960
|
console.log(`Error while parsing the lock file ${lockFile}.`, err);
|
|
869
961
|
} else {
|
|
870
962
|
console.log("Error while parsing the lock data as toml", err);
|
|
@@ -1218,6 +1310,38 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
|
|
|
1218
1310
|
}
|
|
1219
1311
|
}
|
|
1220
1312
|
}
|
|
1313
|
+
// Seed the parent component's first-level edges from the manifest. Lock
|
|
1314
|
+
// files describe the resolved closure but do not say which of the locked
|
|
1315
|
+
// packages the root project directly depends on; parsePyProjectTomlFile
|
|
1316
|
+
// derived that set into directDepsKeys from [project.dependencies] /
|
|
1317
|
+
// [tool.poetry.dependencies]. Only packages that are locked resolve to a
|
|
1318
|
+
// bom-ref here, so the seeded edge never dangles and dev-group or
|
|
1319
|
+
// unresolved dependencies stay out of the first level. Seeding
|
|
1320
|
+
// depsMap (instead of pushing into dependenciesList) lets the
|
|
1321
|
+
// materialisation below apply its usual ref resolution and sorting.
|
|
1322
|
+
if (parentComponent?.["bom-ref"] && Object.keys(directDepsKeys).length) {
|
|
1323
|
+
const rootRef = parentComponent["bom-ref"];
|
|
1324
|
+
const rootDeps = new Set();
|
|
1325
|
+
for (const depKey of Object.keys(directDepsKeys)) {
|
|
1326
|
+
const depRef =
|
|
1327
|
+
existingPkgMap[depKey] ||
|
|
1328
|
+
existingPkgMap[depKey.replaceAll("-", "_")] ||
|
|
1329
|
+
existingPkgMap[`py${depKey}`];
|
|
1330
|
+
if (depRef && depRef !== rootRef) {
|
|
1331
|
+
rootDeps.add(depRef);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
if (rootDeps.size) {
|
|
1335
|
+
const existingRootDeps = depsMap[rootRef];
|
|
1336
|
+
if (existingRootDeps) {
|
|
1337
|
+
for (const adep of rootDeps) {
|
|
1338
|
+
existingRootDeps.add(adep);
|
|
1339
|
+
}
|
|
1340
|
+
} else {
|
|
1341
|
+
depsMap[rootRef] = rootDeps;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1221
1345
|
for (const key of Object.keys(depsMap)) {
|
|
1222
1346
|
const dependsOnList = new Set();
|
|
1223
1347
|
const parentPkg = pkgBomRefMap[key];
|
|
@@ -1921,8 +2045,20 @@ export function parsePixiTomlFile(pixiToml) {
|
|
|
1921
2045
|
pkg.name = projectData.name;
|
|
1922
2046
|
pkg.version = projectData.version;
|
|
1923
2047
|
// pkg.authors = tomlData['project']['authors'];
|
|
1924
|
-
|
|
1925
|
-
|
|
2048
|
+
// The parent component only gets `cleanParentComponent`, which converts the
|
|
2049
|
+
// transient `{ url }` shape — raw strings would be dropped silently, so
|
|
2050
|
+
// emit external references directly instead.
|
|
2051
|
+
if (projectData.homepage) {
|
|
2052
|
+
pkg.externalReferences = pkg.externalReferences || [];
|
|
2053
|
+
pkg.externalReferences.push({
|
|
2054
|
+
type: "website",
|
|
2055
|
+
url: projectData.homepage,
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
if (projectData.repository) {
|
|
2059
|
+
pkg.externalReferences = pkg.externalReferences || [];
|
|
2060
|
+
pkg.externalReferences.push({ type: "vcs", url: projectData.repository });
|
|
2061
|
+
}
|
|
1926
2062
|
return pkg;
|
|
1927
2063
|
}
|
|
1928
2064
|
|
|
@@ -62,7 +62,15 @@ function parseCargoDependencyFromPackageNode(packageNode) {
|
|
|
62
62
|
const isExtendFromWorkspace = (attribute) => {
|
|
63
63
|
return attribute?.workspace;
|
|
64
64
|
};
|
|
65
|
-
|
|
65
|
+
// A missing name, or a name/version that only exists as an unresolved
|
|
66
|
+
// `*.workspace = true` reference, cannot identify a package. An absent
|
|
67
|
+
// version is fine though: cargo itself records unversioned local packages
|
|
68
|
+
// such as unpublished workspace members as version 0.0.0 (issue #4327).
|
|
69
|
+
if (
|
|
70
|
+
!pkgName ||
|
|
71
|
+
isExtendFromWorkspace(pkgName) ||
|
|
72
|
+
isExtendFromWorkspace(pkgVersion)
|
|
73
|
+
) {
|
|
66
74
|
throw new Error("name or version is not defined");
|
|
67
75
|
}
|
|
68
76
|
|
|
@@ -76,8 +84,10 @@ function parseCargoDependencyFromPackageNode(packageNode) {
|
|
|
76
84
|
pkg.group = group;
|
|
77
85
|
pkg.name = basename(pkgName.toString());
|
|
78
86
|
}
|
|
79
|
-
if (
|
|
87
|
+
if (pkgVersion) {
|
|
80
88
|
pkg.version = pkgVersion;
|
|
89
|
+
} else {
|
|
90
|
+
pkg.version = "0.0.0";
|
|
81
91
|
}
|
|
82
92
|
if (!isExtendFromWorkspace(pkgAuthors) && pkgAuthors) {
|
|
83
93
|
if (Array.isArray(pkgAuthors)) {
|