@cyclonedx/cdxgen 12.1.3 → 12.1.5
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 +1 -1
- package/bin/cdxgen.js +12 -0
- package/bin/repl.js +2 -2
- package/lib/cli/index.js +164 -71
- package/lib/evinser/evinser.js +3 -4
- package/lib/evinser/swiftsem.js +1 -1
- package/lib/helpers/caxa.js +1 -1
- package/lib/helpers/display.js +6 -10
- package/lib/helpers/envcontext.js +5 -5
- package/lib/helpers/pythonutils.js +296 -0
- package/lib/helpers/pythonutils.poku.js +469 -0
- package/lib/helpers/utils.js +303 -95
- package/lib/helpers/utils.poku.js +84 -1
- package/lib/managers/piptree.js +1 -1
- package/lib/parsers/npmrc.js +88 -0
- package/lib/parsers/npmrc.poku.js +492 -0
- package/lib/server/openapi.yaml +0 -9
- package/lib/server/server.js +18 -5
- package/lib/stages/pregen/env-audit.js +34 -0
- package/lib/stages/pregen/env-audit.poku.js +290 -0
- package/lib/third-party/arborist/lib/deepest-nesting-target.js +1 -1
- package/lib/third-party/arborist/lib/node.js +3 -3
- package/lib/third-party/arborist/lib/shrinkwrap.js +1 -1
- package/lib/third-party/arborist/lib/tree-check.js +1 -1
- package/package.json +6 -6
- package/types/lib/cli/index.d.ts +39 -39
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +19 -19
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +14 -14
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/cbomutils.d.ts +1 -1
- package/types/lib/helpers/cbomutils.d.ts.map +1 -1
- package/types/lib/helpers/db.d.ts +2 -2
- package/types/lib/helpers/db.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts +2 -2
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/envcontext.d.ts +14 -14
- package/types/lib/helpers/envcontext.d.ts.map +1 -1
- package/types/lib/helpers/logger.d.ts +1 -1
- package/types/lib/helpers/logger.d.ts.map +1 -1
- package/types/lib/helpers/protobom.d.ts +4 -2
- package/types/lib/helpers/protobom.d.ts.map +1 -1
- package/types/lib/helpers/pythonutils.d.ts +9 -0
- package/types/lib/helpers/pythonutils.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +103 -88
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +2 -2
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +2 -2
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +1 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/managers/piptree.d.ts +1 -1
- package/types/lib/managers/piptree.d.ts.map +1 -1
- package/types/lib/parsers/iri.d.ts +6 -6
- package/types/lib/parsers/iri.d.ts.map +1 -1
- package/types/lib/parsers/npmrc.d.ts +23 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -0
- package/types/lib/server/server.d.ts +1 -1
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts +3 -3
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +5 -5
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/pregen/env-audit.d.ts +2 -0
- package/types/lib/stages/pregen/env-audit.d.ts.map +1 -0
- package/types/lib/stages/pregen/pregen.d.ts +6 -6
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/arborist/index.d.ts +4 -3
- package/types/lib/third-party/arborist/lib/arborist/index.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/can-place-dep.d.ts +5 -5
- package/types/lib/third-party/arborist/lib/can-place-dep.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts +4 -4
- package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/diff.d.ts +3 -3
- package/types/lib/third-party/arborist/lib/diff.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/edge.d.ts +2 -2
- package/types/lib/third-party/arborist/lib/edge.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts +1 -1
- package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/inventory.d.ts +3 -2
- package/types/lib/third-party/arborist/lib/inventory.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/link.d.ts +10 -7
- package/types/lib/third-party/arborist/lib/link.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/node.d.ts +8 -8
- package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/optional-set.d.ts +1 -1
- package/types/lib/third-party/arborist/lib/optional-set.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/override-set.d.ts +3 -3
- package/types/lib/third-party/arborist/lib/override-set.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/peer-entry-sets.d.ts +1 -1
- package/types/lib/third-party/arborist/lib/peer-entry-sets.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/place-dep.d.ts +3 -3
- package/types/lib/third-party/arborist/lib/place-dep.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts +7 -7
- package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts +1 -1
- package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/yarn-lock.d.ts +4 -3
- package/types/lib/third-party/arborist/lib/yarn-lock.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts +0 -34
- package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts.map +0 -1
- package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts +0 -24
- package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts.map +0 -1
- package/types/lib/third-party/arborist/lib/tracker.d.ts +0 -13
- package/types/lib/third-party/arborist/lib/tracker.d.ts.map +0 -1
package/lib/helpers/utils.js
CHANGED
|
@@ -57,6 +57,7 @@ import { IriValidationStrategy, validateIri } from "../parsers/iri.js";
|
|
|
57
57
|
import Arborist from "../third-party/arborist/lib/index.js";
|
|
58
58
|
import { extractPackageInfoFromHintPath } from "./dotnetutils.js";
|
|
59
59
|
import { thoughtLog, traceLog } from "./logger.js";
|
|
60
|
+
import { get_python_command_from_env, getVenvMetadata } from "./pythonutils.js";
|
|
60
61
|
|
|
61
62
|
let url = import.meta?.url;
|
|
62
63
|
if (url && !url.startsWith("file://")) {
|
|
@@ -175,6 +176,40 @@ export function safeSpawnSync(command, args, options) {
|
|
|
175
176
|
);
|
|
176
177
|
}
|
|
177
178
|
}
|
|
179
|
+
let isPyPackageInstall = false;
|
|
180
|
+
if (command.includes("pip") && args?.includes("install")) {
|
|
181
|
+
isPyPackageInstall = true;
|
|
182
|
+
} else if (
|
|
183
|
+
command.includes("python") &&
|
|
184
|
+
args?.includes("pip") &&
|
|
185
|
+
args?.includes("install")
|
|
186
|
+
) {
|
|
187
|
+
isPyPackageInstall = true;
|
|
188
|
+
} else if (
|
|
189
|
+
command.includes("uv") &&
|
|
190
|
+
args?.includes("pip") &&
|
|
191
|
+
args?.includes("install")
|
|
192
|
+
) {
|
|
193
|
+
isPyPackageInstall = true;
|
|
194
|
+
}
|
|
195
|
+
if (isPyPackageInstall) {
|
|
196
|
+
const hasOnlyBinary = args?.some(
|
|
197
|
+
(arg) => arg === "--only-binary" || arg.startsWith("--only-binary="),
|
|
198
|
+
);
|
|
199
|
+
if (!hasOnlyBinary) {
|
|
200
|
+
if (isSecureMode) {
|
|
201
|
+
console.warn(
|
|
202
|
+
"\x1b[1;31mSecurity Alert: pip/uv install invoked without '--only-binary' argument in secure mode. This is a bug in cdxgen and introduces Arbitrary Code Execution (ACE) risks. Please report with an example repo here https://github.com/cdxgen/cdxgen/issues.\x1b[0m",
|
|
203
|
+
);
|
|
204
|
+
} else if (process.env?.CDXGEN_IN_CONTAINER === "true") {
|
|
205
|
+
console.log("Running pip/uv install without '--only-binary' argument.");
|
|
206
|
+
} else {
|
|
207
|
+
console.warn(
|
|
208
|
+
"\x1b[1;35mNotice: pip/uv install invoked without '--only-binary'. This allows executing untrusted setup.py scripts. Only run cdxgen in trusted directories.\x1b",
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
178
213
|
traceLog("spawn", { command, args, ...options });
|
|
179
214
|
commandsExecuted.add(command);
|
|
180
215
|
// Fix for DEP0190 warning
|
|
@@ -471,7 +506,11 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
471
506
|
"poetry",
|
|
472
507
|
"uv",
|
|
473
508
|
"pdm",
|
|
509
|
+
"rye",
|
|
474
510
|
"hatch",
|
|
511
|
+
"conda",
|
|
512
|
+
"miniconda",
|
|
513
|
+
"pyenv",
|
|
475
514
|
],
|
|
476
515
|
go: ["go", "golang", "gomod", "gopkg"],
|
|
477
516
|
rust: ["rust", "rust-lang", "cargo"],
|
|
@@ -1462,6 +1501,40 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
1462
1501
|
value: "true",
|
|
1463
1502
|
});
|
|
1464
1503
|
}
|
|
1504
|
+
// Detect version spoofing by comparing the version in the lockfile with the version in package.json
|
|
1505
|
+
if (node.path && safeExistsSync(join(node.path, "package.json"))) {
|
|
1506
|
+
try {
|
|
1507
|
+
const diskPkgStr = readFileSync(
|
|
1508
|
+
join(node.path, "package.json"),
|
|
1509
|
+
"utf8",
|
|
1510
|
+
);
|
|
1511
|
+
const diskPkg = JSON.parse(diskPkgStr);
|
|
1512
|
+
if (!diskPkg.name || diskPkg.name !== node.packageName) {
|
|
1513
|
+
console.warn(
|
|
1514
|
+
`\x1b[1;35mWARNING: Package name spoofing detected for ${node.packageName}! Lockfile says ${node.packageName}, but disk says ${diskPkg.name}.\x1b[0m`,
|
|
1515
|
+
);
|
|
1516
|
+
if (diskPkg.name) {
|
|
1517
|
+
pkg.properties.push({
|
|
1518
|
+
name: "cdx:npm:nameMismatchError",
|
|
1519
|
+
value: `${diskPkg.name} used instead of ${node.packageName}`,
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
if (!diskPkg.version || diskPkg.version !== node.version) {
|
|
1524
|
+
console.warn(
|
|
1525
|
+
`\x1b[1;35mWARNING: Package version spoofing detected for ${node.packageName}! Lockfile says ${node.version}, but disk says ${diskPkg.version}.\x1b[0m`,
|
|
1526
|
+
);
|
|
1527
|
+
if (diskPkg.version) {
|
|
1528
|
+
pkg.properties.push({
|
|
1529
|
+
name: "cdx:npm:versionMismatchError",
|
|
1530
|
+
value: `${diskPkg.version} used instead of ${node.version}`,
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
} catch (_err) {
|
|
1535
|
+
// ignore
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1465
1538
|
if (node?.inBundle) {
|
|
1466
1539
|
pkg.properties.push({
|
|
1467
1540
|
name: "cdx:npm:inBundle",
|
|
@@ -2796,7 +2869,7 @@ export function findPnpmPackagePath(baseDir, packageName, version) {
|
|
|
2796
2869
|
* @returns {Array} Enhanced package list
|
|
2797
2870
|
*/
|
|
2798
2871
|
export async function pnpmMetadata(pkgList, lockFilePath) {
|
|
2799
|
-
if (!pkgList
|
|
2872
|
+
if (!pkgList?.length || !lockFilePath) {
|
|
2800
2873
|
return pkgList;
|
|
2801
2874
|
}
|
|
2802
2875
|
|
|
@@ -5062,7 +5135,7 @@ export async function getMvnMetadata(
|
|
|
5062
5135
|
const ANDROID_MAVEN_URL =
|
|
5063
5136
|
process.env.ANDROID_MAVEN_URL || "https://maven.google.com/";
|
|
5064
5137
|
const cdepList = [];
|
|
5065
|
-
if (!pkgList
|
|
5138
|
+
if (!pkgList?.length) {
|
|
5066
5139
|
return pkgList;
|
|
5067
5140
|
}
|
|
5068
5141
|
if (DEBUG_MODE && shouldFetchLicense()) {
|
|
@@ -5356,7 +5429,7 @@ export async function getPyMetadata(pkgList, fetchDepsInfo) {
|
|
|
5356
5429
|
const PYPI_URL = process.env.PYPI_URL || "https://pypi.org/pypi/";
|
|
5357
5430
|
const cdepList = [];
|
|
5358
5431
|
for (const p of pkgList) {
|
|
5359
|
-
if (!p
|
|
5432
|
+
if (!p?.name) {
|
|
5360
5433
|
continue;
|
|
5361
5434
|
}
|
|
5362
5435
|
try {
|
|
@@ -5440,7 +5513,7 @@ export async function getPyMetadata(pkgList, fetchDepsInfo) {
|
|
|
5440
5513
|
}
|
|
5441
5514
|
}
|
|
5442
5515
|
// Use the latest version if none specified
|
|
5443
|
-
if (!p.version
|
|
5516
|
+
if (!p.version?.trim().length) {
|
|
5444
5517
|
let versionSpecifiers;
|
|
5445
5518
|
if (p.properties?.length) {
|
|
5446
5519
|
for (const pprop of p.properties) {
|
|
@@ -5595,6 +5668,9 @@ export function parseBdistMetadata(mDataFile, rawMetadata = undefined) {
|
|
|
5595
5668
|
externalReferences: [],
|
|
5596
5669
|
properties: [],
|
|
5597
5670
|
};
|
|
5671
|
+
if (mDataFile) {
|
|
5672
|
+
pkg.properties.push({ name: "SrcFile", value: mDataFile });
|
|
5673
|
+
}
|
|
5598
5674
|
const lines = mData.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
5599
5675
|
let isBody = false;
|
|
5600
5676
|
for (const line of lines) {
|
|
@@ -6433,7 +6509,8 @@ async function parseReqData(reqFile, reqData = null, fetchDepsInfo = false) {
|
|
|
6433
6509
|
},
|
|
6434
6510
|
}
|
|
6435
6511
|
: undefined;
|
|
6436
|
-
const
|
|
6512
|
+
const normalizedData = reqData.replace(/\r/g, "").replace(/\\\n/g, " ");
|
|
6513
|
+
const lines = normalizedData.split("\n");
|
|
6437
6514
|
for (const line of lines) {
|
|
6438
6515
|
let l = line.trim();
|
|
6439
6516
|
if (l.includes("# Basic requirements")) {
|
|
@@ -6461,7 +6538,25 @@ async function parseReqData(reqFile, reqData = null, fetchDepsInfo = false) {
|
|
|
6461
6538
|
},
|
|
6462
6539
|
]
|
|
6463
6540
|
: [];
|
|
6464
|
-
|
|
6541
|
+
const hashes = [];
|
|
6542
|
+
const hashRegex = /--hash=([a-zA-Z0-9\-]+):([a-fA-F0-9]+)/g;
|
|
6543
|
+
let hashMatch;
|
|
6544
|
+
while ((hashMatch = hashRegex.exec(l)) !== null) {
|
|
6545
|
+
let alg = hashMatch[1].toUpperCase();
|
|
6546
|
+
if (alg === "SHA256") alg = "SHA-256";
|
|
6547
|
+
else if (alg === "SHA384") alg = "SHA-384";
|
|
6548
|
+
else if (alg === "SHA512") alg = "SHA-512";
|
|
6549
|
+
else if (alg === "SHA1") alg = "SHA-1";
|
|
6550
|
+
hashes.push({
|
|
6551
|
+
alg: alg,
|
|
6552
|
+
content: hashMatch[2],
|
|
6553
|
+
});
|
|
6554
|
+
}
|
|
6555
|
+
// Strip the hash flags and any residual backslashes
|
|
6556
|
+
l = l
|
|
6557
|
+
.replace(/--hash=[a-zA-Z0-9\-]+:[a-fA-F0-9]+/g, "")
|
|
6558
|
+
.replace(/\\/g, "")
|
|
6559
|
+
.trim();
|
|
6465
6560
|
// Handle markers
|
|
6466
6561
|
let markers = null;
|
|
6467
6562
|
let structuredMarkers = null;
|
|
@@ -6501,6 +6596,9 @@ async function parseReqData(reqFile, reqData = null, fetchDepsInfo = false) {
|
|
|
6501
6596
|
scope: compScope,
|
|
6502
6597
|
evidence,
|
|
6503
6598
|
};
|
|
6599
|
+
if (hashes.length > 0) {
|
|
6600
|
+
apkg.hashes = hashes;
|
|
6601
|
+
}
|
|
6504
6602
|
if (comment) {
|
|
6505
6603
|
apkg.licenses = comment
|
|
6506
6604
|
.split("/")
|
|
@@ -10492,10 +10590,7 @@ export function parseConanLockData(conanLockData) {
|
|
|
10492
10590
|
}
|
|
10493
10591
|
|
|
10494
10592
|
const lockFile = JSON.parse(conanLockData);
|
|
10495
|
-
if (
|
|
10496
|
-
(!lockFile || !lockFile.graph_lock || !lockFile.graph_lock.nodes) &&
|
|
10497
|
-
!lockFile.requires
|
|
10498
|
-
) {
|
|
10593
|
+
if (!lockFile?.graph_lock?.nodes && !lockFile.requires) {
|
|
10499
10594
|
return { pkgList, dependencies, parentComponentDependencies };
|
|
10500
10595
|
}
|
|
10501
10596
|
|
|
@@ -11878,7 +11973,7 @@ export function parseCsPkgLockData(csLockData, pkgLockFile) {
|
|
|
11878
11973
|
};
|
|
11879
11974
|
}
|
|
11880
11975
|
const assetData = JSON.parse(csLockData);
|
|
11881
|
-
if (!assetData
|
|
11976
|
+
if (!assetData?.dependencies) {
|
|
11882
11977
|
return {
|
|
11883
11978
|
pkgList,
|
|
11884
11979
|
dependenciesList,
|
|
@@ -12181,7 +12276,7 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
12181
12276
|
for (const i in packages[compScope]) {
|
|
12182
12277
|
const pkg = packages[compScope][i];
|
|
12183
12278
|
// Be extra cautious. Potential fix for #236
|
|
12184
|
-
if (!pkg
|
|
12279
|
+
if (!pkg?.name || !pkg.version) {
|
|
12185
12280
|
continue;
|
|
12186
12281
|
}
|
|
12187
12282
|
|
|
@@ -12279,7 +12374,7 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
12279
12374
|
for (const compScope in packages) {
|
|
12280
12375
|
for (const i in packages[compScope]) {
|
|
12281
12376
|
const pkg = packages[compScope][i];
|
|
12282
|
-
if (!pkg
|
|
12377
|
+
if (!pkg?.name || !pkg.version) {
|
|
12283
12378
|
continue;
|
|
12284
12379
|
}
|
|
12285
12380
|
if (!pkg.require || !Object.keys(pkg.require).length) {
|
|
@@ -15232,36 +15327,6 @@ function flattenDeps(dependenciesMap, pkgList, reqOrSetupFile, t) {
|
|
|
15232
15327
|
.sort();
|
|
15233
15328
|
}
|
|
15234
15329
|
|
|
15235
|
-
function get_python_command_from_env(env) {
|
|
15236
|
-
// Virtual environments needs special treatment to use the correct python executable
|
|
15237
|
-
// Without this step, the default python is always used resulting in false positives
|
|
15238
|
-
const python_exe_name = isWin ? "python.exe" : "python";
|
|
15239
|
-
const python3_exe_name = isWin ? "python3.exe" : "python3";
|
|
15240
|
-
let python_cmd_to_use = PYTHON_CMD;
|
|
15241
|
-
if (env.VIRTUAL_ENV) {
|
|
15242
|
-
const bin_dir = isWin ? "Scripts" : "bin";
|
|
15243
|
-
if (safeExistsSync(join(env.VIRTUAL_ENV, bin_dir, python_exe_name))) {
|
|
15244
|
-
python_cmd_to_use = join(env.VIRTUAL_ENV, bin_dir, python_exe_name);
|
|
15245
|
-
} else if (
|
|
15246
|
-
safeExistsSync(join(env.VIRTUAL_ENV, bin_dir, python3_exe_name))
|
|
15247
|
-
) {
|
|
15248
|
-
python_cmd_to_use = join(env.VIRTUAL_ENV, bin_dir, python3_exe_name);
|
|
15249
|
-
}
|
|
15250
|
-
} else if (env.CONDA_PREFIX) {
|
|
15251
|
-
const bin_dir = isWin ? "" : "bin";
|
|
15252
|
-
if (safeExistsSync(join(env.CONDA_PREFIX, bin_dir, python_exe_name))) {
|
|
15253
|
-
python_cmd_to_use = join(env.CONDA_PREFIX, bin_dir, python_exe_name);
|
|
15254
|
-
} else if (
|
|
15255
|
-
safeExistsSync(join(env.CONDA_PREFIX, bin_dir, python3_exe_name))
|
|
15256
|
-
) {
|
|
15257
|
-
python_cmd_to_use = join(env.CONDA_PREFIX, bin_dir, python3_exe_name);
|
|
15258
|
-
}
|
|
15259
|
-
} else if (env.CONDA_PYTHON_EXE) {
|
|
15260
|
-
python_cmd_to_use = env.CONDA_PYTHON_EXE;
|
|
15261
|
-
}
|
|
15262
|
-
return python_cmd_to_use;
|
|
15263
|
-
}
|
|
15264
|
-
|
|
15265
15330
|
/**
|
|
15266
15331
|
* Create uv.lock file with uv sync command.
|
|
15267
15332
|
*
|
|
@@ -15344,14 +15409,12 @@ export async function getPipFrozenTree(
|
|
|
15344
15409
|
...process.env,
|
|
15345
15410
|
};
|
|
15346
15411
|
|
|
15347
|
-
// FIX: Create a set of explicit dependencies from requirements.txt to identify root packages.
|
|
15348
15412
|
const explicitDeps = new Set();
|
|
15349
15413
|
if (reqOrSetupFile?.endsWith(".txt") && safeExistsSync(reqOrSetupFile)) {
|
|
15350
15414
|
// We only need the package names, so we pass `false` to avoid fetching full metadata.
|
|
15351
15415
|
const tempPkgList = await parseReqFile(reqOrSetupFile, null, false);
|
|
15352
15416
|
for (const pkg of tempPkgList) {
|
|
15353
15417
|
if (pkg.name) {
|
|
15354
|
-
// Normalize the name (lowercase, hyphenated) for accurate lookups.
|
|
15355
15418
|
explicitDeps.add(pkg.name.replace(/_/g, "-").toLowerCase());
|
|
15356
15419
|
}
|
|
15357
15420
|
}
|
|
@@ -15407,17 +15470,26 @@ export async function getPipFrozenTree(
|
|
|
15407
15470
|
}
|
|
15408
15471
|
}
|
|
15409
15472
|
}
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
|
|
15413
|
-
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15473
|
+
const venvMeta = getVenvMetadata(env);
|
|
15474
|
+
const python_cmd_for_tree = get_python_command_from_env(env);
|
|
15475
|
+
// Check if pyproject.toml is actually a uv-configured workspace
|
|
15476
|
+
let hasToolUv = false;
|
|
15477
|
+
let hasToolPoetry = false;
|
|
15478
|
+
if (
|
|
15479
|
+
reqOrSetupFile?.endsWith("pyproject.toml") &&
|
|
15480
|
+
safeExistsSync(reqOrSetupFile)
|
|
15481
|
+
) {
|
|
15482
|
+
try {
|
|
15483
|
+
const content = readFileSync(reqOrSetupFile, "utf-8");
|
|
15484
|
+
hasToolUv = content.includes("[tool.uv]");
|
|
15485
|
+
hasToolPoetry = content.includes('build-backend = "poetry.core');
|
|
15486
|
+
} catch (_err) {
|
|
15487
|
+
// Ignore read error
|
|
15488
|
+
}
|
|
15489
|
+
}
|
|
15418
15490
|
if (reqOrSetupFile) {
|
|
15419
15491
|
// We have a poetry.lock file
|
|
15420
|
-
if (reqOrSetupFile.endsWith("poetry.lock")) {
|
|
15492
|
+
if (reqOrSetupFile.endsWith("poetry.lock") || hasToolPoetry) {
|
|
15421
15493
|
const poetryConfigArgs = [
|
|
15422
15494
|
"-m",
|
|
15423
15495
|
"poetry",
|
|
@@ -15504,20 +15576,78 @@ export async function getPipFrozenTree(
|
|
|
15504
15576
|
)}${_delimiter}${process.env.PATH || ""}`;
|
|
15505
15577
|
}
|
|
15506
15578
|
}
|
|
15579
|
+
} else if (reqOrSetupFile.endsWith("pdm.lock") || venvMeta.type === "pdm") {
|
|
15580
|
+
thoughtLog("Performing pdm install");
|
|
15581
|
+
result = safeSpawnSync("pdm", ["install"], {
|
|
15582
|
+
cwd: basePath,
|
|
15583
|
+
shell: isWin,
|
|
15584
|
+
env,
|
|
15585
|
+
});
|
|
15586
|
+
if (result.status !== 0 || result.error) {
|
|
15587
|
+
frozen = false;
|
|
15588
|
+
}
|
|
15589
|
+
} else if (
|
|
15590
|
+
reqOrSetupFile.endsWith("pixi.lock") ||
|
|
15591
|
+
venvMeta.type === "pixi"
|
|
15592
|
+
) {
|
|
15593
|
+
thoughtLog("Performing pixi install");
|
|
15594
|
+
result = safeSpawnSync("pixi", ["install"], {
|
|
15595
|
+
cwd: basePath,
|
|
15596
|
+
shell: isWin,
|
|
15597
|
+
env,
|
|
15598
|
+
});
|
|
15599
|
+
if (result.status !== 0 || result.error) {
|
|
15600
|
+
frozen = false;
|
|
15601
|
+
}
|
|
15602
|
+
} else if (
|
|
15603
|
+
reqOrSetupFile.endsWith("uv.lock") ||
|
|
15604
|
+
(venvMeta.type === "uv" && hasToolUv)
|
|
15605
|
+
) {
|
|
15606
|
+
thoughtLog("Performing uv sync");
|
|
15607
|
+
result = safeSpawnSync("uv", ["sync"], {
|
|
15608
|
+
cwd: basePath,
|
|
15609
|
+
shell: isWin,
|
|
15610
|
+
env,
|
|
15611
|
+
});
|
|
15612
|
+
if (result.status !== 0 || result.error) {
|
|
15613
|
+
frozen = false;
|
|
15614
|
+
}
|
|
15615
|
+
} else if (
|
|
15616
|
+
venvMeta.type === "rye" ||
|
|
15617
|
+
reqOrSetupFile.endsWith("requirements.lock")
|
|
15618
|
+
) {
|
|
15619
|
+
thoughtLog("Performing rye sync");
|
|
15620
|
+
result = safeSpawnSync("rye", ["sync"], {
|
|
15621
|
+
cwd: basePath,
|
|
15622
|
+
shell: isWin,
|
|
15623
|
+
env,
|
|
15624
|
+
});
|
|
15625
|
+
if (result.status !== 0 || result.error) {
|
|
15626
|
+
frozen = false;
|
|
15627
|
+
}
|
|
15507
15628
|
} else {
|
|
15508
|
-
//
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
"
|
|
15513
|
-
"pip",
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15629
|
+
// General package installation (Handling pip, or uv pip)
|
|
15630
|
+
let installCmd = python_cmd_for_tree;
|
|
15631
|
+
let pipInstallArgs = [];
|
|
15632
|
+
if (venvMeta.type === "uv") {
|
|
15633
|
+
installCmd = "uv";
|
|
15634
|
+
pipInstallArgs = ["pip", "install"];
|
|
15635
|
+
if (isSecureMode) {
|
|
15636
|
+
pipInstallArgs.push("--only-binary");
|
|
15637
|
+
pipInstallArgs.push(":all:");
|
|
15638
|
+
}
|
|
15639
|
+
} else {
|
|
15640
|
+
pipInstallArgs = [
|
|
15641
|
+
"-m",
|
|
15642
|
+
"pip",
|
|
15643
|
+
"install",
|
|
15644
|
+
"--disable-pip-version-check",
|
|
15645
|
+
];
|
|
15646
|
+
if (isSecureMode) {
|
|
15647
|
+
pipInstallArgs.push("--only-binary=:all:");
|
|
15648
|
+
pipInstallArgs.unshift("-S");
|
|
15649
|
+
}
|
|
15519
15650
|
}
|
|
15520
|
-
// Requirements.txt could be called with any name so best to check for not setup.py and not pyproject.toml
|
|
15521
15651
|
if (
|
|
15522
15652
|
!reqOrSetupFile.endsWith("setup.py") &&
|
|
15523
15653
|
!reqOrSetupFile.endsWith("pyproject.toml")
|
|
@@ -15532,20 +15662,17 @@ export async function getPipFrozenTree(
|
|
|
15532
15662
|
} else {
|
|
15533
15663
|
pipInstallArgs.push(resolve(basePath));
|
|
15534
15664
|
}
|
|
15535
|
-
// Support for passing additional arguments to pip
|
|
15536
|
-
// Eg: --python-version 3.10 --ignore-requires-python --no-warn-conflicts --only-binary=:all:
|
|
15537
15665
|
if (process?.env?.PIP_INSTALL_ARGS) {
|
|
15538
15666
|
const addArgs = process.env.PIP_INSTALL_ARGS.split(" ");
|
|
15539
15667
|
pipInstallArgs = pipInstallArgs.concat(addArgs);
|
|
15540
15668
|
}
|
|
15541
15669
|
thoughtLog(
|
|
15542
|
-
`**
|
|
15670
|
+
`**INSTALL**: Trying package install using the arguments: ${installCmd} ${pipInstallArgs.join(" ")}`,
|
|
15543
15671
|
);
|
|
15544
15672
|
if (DEBUG_MODE) {
|
|
15545
|
-
console.log("Executing",
|
|
15673
|
+
console.log("Executing", installCmd);
|
|
15546
15674
|
}
|
|
15547
|
-
|
|
15548
|
-
result = safeSpawnSync(python_cmd_for_tree, pipInstallArgs, {
|
|
15675
|
+
result = safeSpawnSync(installCmd, pipInstallArgs, {
|
|
15549
15676
|
cwd: basePath,
|
|
15550
15677
|
shell: isWin,
|
|
15551
15678
|
env,
|
|
@@ -15570,6 +15697,10 @@ export async function getPipFrozenTree(
|
|
|
15570
15697
|
);
|
|
15571
15698
|
}
|
|
15572
15699
|
console.log(result.stderr);
|
|
15700
|
+
} else if (result?.stderr?.includes("No module named pip")) {
|
|
15701
|
+
console.log(
|
|
15702
|
+
"Using uv? Ensure 'uv' is in your PATH to allow cdxgen to use `uv pip install` automatically.",
|
|
15703
|
+
);
|
|
15573
15704
|
} else if (
|
|
15574
15705
|
process.env.PIP_INSTALL_ARGS &&
|
|
15575
15706
|
result.stderr?.includes("Cannot set --home and --prefix together")
|
|
@@ -15700,18 +15831,42 @@ export async function getPipFrozenTree(
|
|
|
15700
15831
|
}
|
|
15701
15832
|
// Bug #375. Attempt pip freeze on existing and new virtual environments
|
|
15702
15833
|
if (env.VIRTUAL_ENV?.length || env.CONDA_PREFIX?.length) {
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
15706
|
-
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
|
|
15711
|
-
|
|
15834
|
+
const venvRoot = env.VIRTUAL_ENV || env.CONDA_PREFIX;
|
|
15835
|
+
const binDir = platform() === "win32" ? "Scripts" : "bin";
|
|
15836
|
+
const pipExe = join(
|
|
15837
|
+
venvRoot,
|
|
15838
|
+
binDir,
|
|
15839
|
+
platform() === "win32" ? "pip.exe" : "pip",
|
|
15840
|
+
);
|
|
15841
|
+
if (!safeExistsSync(pipExe)) {
|
|
15842
|
+
thoughtLog(
|
|
15843
|
+
"The 'pip' module is missing in this environment. Bootstrapping it to support piptree extraction.",
|
|
15844
|
+
);
|
|
15845
|
+
if (venvMeta.type === "uv") {
|
|
15846
|
+
safeSpawnSync("uv", ["pip", "install", "pip"], {
|
|
15847
|
+
cwd: basePath,
|
|
15848
|
+
shell: isWin,
|
|
15849
|
+
env,
|
|
15850
|
+
});
|
|
15851
|
+
} else if (venvMeta.type === "rye") {
|
|
15852
|
+
safeSpawnSync("rye", ["run", "pip", "install", "pip"], {
|
|
15853
|
+
cwd: basePath,
|
|
15854
|
+
shell: isWin,
|
|
15855
|
+
env,
|
|
15856
|
+
});
|
|
15857
|
+
} else {
|
|
15858
|
+
safeSpawnSync(python_cmd_for_tree, ["-m", "ensurepip", "--upgrade"], {
|
|
15859
|
+
cwd: basePath,
|
|
15860
|
+
shell: isWin,
|
|
15861
|
+
env,
|
|
15862
|
+
});
|
|
15712
15863
|
}
|
|
15713
15864
|
}
|
|
15714
|
-
|
|
15865
|
+
if (DEBUG_MODE && reqOrSetupFile) {
|
|
15866
|
+
console.log(
|
|
15867
|
+
`About to construct the dependency tree based on ${reqOrSetupFile}. Please wait ...`,
|
|
15868
|
+
);
|
|
15869
|
+
}
|
|
15715
15870
|
// This is a slow step that ideally needs to be invoked only once per venv
|
|
15716
15871
|
const tree = getTreeWithPlugin(env, python_cmd_for_tree, basePath);
|
|
15717
15872
|
if (DEBUG_MODE && !tree.length) {
|
|
@@ -15862,10 +16017,23 @@ export function getPipTreeForPackages(
|
|
|
15862
16017
|
env.PYTHONPATH = undefined;
|
|
15863
16018
|
}
|
|
15864
16019
|
}
|
|
16020
|
+
const venvMeta = getVenvMetadata(env);
|
|
15865
16021
|
const python_cmd_for_tree = get_python_command_from_env(env);
|
|
15866
|
-
let
|
|
15867
|
-
|
|
15868
|
-
|
|
16022
|
+
let installCmd = python_cmd_for_tree;
|
|
16023
|
+
let pipInstallArgs = [];
|
|
16024
|
+
if (venvMeta.type === "uv") {
|
|
16025
|
+
installCmd = "uv";
|
|
16026
|
+
pipInstallArgs = ["pip", "install"];
|
|
16027
|
+
if (isSecureMode) {
|
|
16028
|
+
pipInstallArgs.push("--only-binary");
|
|
16029
|
+
pipInstallArgs.push(":all:");
|
|
16030
|
+
}
|
|
16031
|
+
} else {
|
|
16032
|
+
pipInstallArgs = ["-m", "pip", "install", "--disable-pip-version-check"];
|
|
16033
|
+
if (isSecureMode) {
|
|
16034
|
+
pipInstallArgs.push("--only-binary=:all:");
|
|
16035
|
+
pipInstallArgs.unshift("-S");
|
|
16036
|
+
}
|
|
15869
16037
|
}
|
|
15870
16038
|
// Support for passing additional arguments to pip
|
|
15871
16039
|
// Eg: --python-version 3.10 --ignore-requires-python --no-warn-conflicts
|
|
@@ -15873,19 +16041,23 @@ export function getPipTreeForPackages(
|
|
|
15873
16041
|
const addArgs = process.env.PIP_INSTALL_ARGS.split(" ");
|
|
15874
16042
|
pipInstallArgs = pipInstallArgs.concat(addArgs);
|
|
15875
16043
|
} else {
|
|
15876
|
-
|
|
15877
|
-
|
|
15878
|
-
|
|
15879
|
-
|
|
15880
|
-
|
|
15881
|
-
|
|
16044
|
+
if (venvMeta.type !== "uv") {
|
|
16045
|
+
pipInstallArgs = pipInstallArgs.concat([
|
|
16046
|
+
"--ignore-requires-python",
|
|
16047
|
+
"--no-compile",
|
|
16048
|
+
"--no-warn-script-location",
|
|
16049
|
+
"--no-warn-conflicts",
|
|
16050
|
+
]);
|
|
16051
|
+
} else {
|
|
16052
|
+
pipInstallArgs.push("--no-compile");
|
|
16053
|
+
}
|
|
15882
16054
|
}
|
|
15883
16055
|
if (DEBUG_MODE) {
|
|
15884
16056
|
console.log(
|
|
15885
16057
|
"Installing",
|
|
15886
16058
|
pkgList.length,
|
|
15887
16059
|
"packages using the command",
|
|
15888
|
-
|
|
16060
|
+
installCmd,
|
|
15889
16061
|
pipInstallArgs.join(" "),
|
|
15890
16062
|
);
|
|
15891
16063
|
}
|
|
@@ -15915,7 +16087,7 @@ export function getPipTreeForPackages(
|
|
|
15915
16087
|
}
|
|
15916
16088
|
// Attempt to perform pip install for pkgSpecifier
|
|
15917
16089
|
const result = safeSpawnSync(
|
|
15918
|
-
|
|
16090
|
+
installCmd,
|
|
15919
16091
|
[...pipInstallArgs, pkgSpecifier],
|
|
15920
16092
|
{
|
|
15921
16093
|
cwd: basePath,
|
|
@@ -15932,6 +16104,30 @@ export function getPipTreeForPackages(
|
|
|
15932
16104
|
}
|
|
15933
16105
|
// Did any package get installed successfully?
|
|
15934
16106
|
if (failedPkgList.length < pkgList.length) {
|
|
16107
|
+
const venvRoot = env.VIRTUAL_ENV || env.CONDA_PREFIX;
|
|
16108
|
+
if (venvRoot) {
|
|
16109
|
+
const binDir = platform() === "win32" ? "Scripts" : "bin";
|
|
16110
|
+
const pipExe = join(
|
|
16111
|
+
venvRoot,
|
|
16112
|
+
binDir,
|
|
16113
|
+
platform() === "win32" ? "pip.exe" : "pip",
|
|
16114
|
+
);
|
|
16115
|
+
if (!safeExistsSync(pipExe)) {
|
|
16116
|
+
if (venvMeta.type === "uv") {
|
|
16117
|
+
safeSpawnSync("uv", ["pip", "install", "pip"], {
|
|
16118
|
+
cwd: basePath,
|
|
16119
|
+
shell: isWin,
|
|
16120
|
+
env,
|
|
16121
|
+
});
|
|
16122
|
+
} else {
|
|
16123
|
+
safeSpawnSync(python_cmd_for_tree, ["-m", "ensurepip", "--upgrade"], {
|
|
16124
|
+
cwd: basePath,
|
|
16125
|
+
shell: isWin,
|
|
16126
|
+
env,
|
|
16127
|
+
});
|
|
16128
|
+
}
|
|
16129
|
+
}
|
|
16130
|
+
}
|
|
15935
16131
|
const dependenciesMap = {};
|
|
15936
16132
|
const tree = getTreeWithPlugin(env, python_cmd_for_tree, basePath);
|
|
15937
16133
|
for (const t of tree) {
|
|
@@ -16048,6 +16244,7 @@ export async function addEvidenceForImports(
|
|
|
16048
16244
|
const aliases = group?.length
|
|
16049
16245
|
? [name, `${group}/${name}`, `@${group}/${name}`]
|
|
16050
16246
|
: [name];
|
|
16247
|
+
let isImported = false;
|
|
16051
16248
|
for (const alias of aliases) {
|
|
16052
16249
|
const all_includes = impPkgs.filter(
|
|
16053
16250
|
(find_pkg) =>
|
|
@@ -16096,6 +16293,7 @@ export async function addEvidenceForImports(
|
|
|
16096
16293
|
}
|
|
16097
16294
|
// Identify all the imported modules of a component
|
|
16098
16295
|
if (impPkgs.includes(alias) || all_includes.length) {
|
|
16296
|
+
isImported = true;
|
|
16099
16297
|
let importedModules = new Set();
|
|
16100
16298
|
pkg.scope = "required";
|
|
16101
16299
|
for (const subevidence of all_includes) {
|
|
@@ -16133,6 +16331,16 @@ export async function addEvidenceForImports(
|
|
|
16133
16331
|
}
|
|
16134
16332
|
break;
|
|
16135
16333
|
}
|
|
16334
|
+
if (
|
|
16335
|
+
impPkgs?.length > 0 &&
|
|
16336
|
+
!isImported &&
|
|
16337
|
+
DEBUG_MODE &&
|
|
16338
|
+
pkg?.scope !== "optional"
|
|
16339
|
+
) {
|
|
16340
|
+
console.debug(
|
|
16341
|
+
`\x1b[1;35mNotice: Package ${pkg.name} has no usage in code. Check if it is needed.\x1b[0m`,
|
|
16342
|
+
);
|
|
16343
|
+
}
|
|
16136
16344
|
// Capture metadata such as description from local node_modules in deep mode
|
|
16137
16345
|
if (deep && !pkg.description && pkg.properties) {
|
|
16138
16346
|
let localNodeModulesPath;
|
|
@@ -16719,7 +16927,7 @@ export function getCppModules(src, options, osPkgsList, epkgList) {
|
|
|
16719
16927
|
// Normalize windows separator
|
|
16720
16928
|
afile = afile.replace("..\\", "").replace(/\\/g, "/");
|
|
16721
16929
|
const fileName = basename(afile);
|
|
16722
|
-
if (!fileName
|
|
16930
|
+
if (!fileName?.length) {
|
|
16723
16931
|
continue;
|
|
16724
16932
|
}
|
|
16725
16933
|
const extn = extname(fileName);
|
|
@@ -16956,7 +17164,7 @@ async function queryNuget(p, NUGET_URL) {
|
|
|
16956
17164
|
{ responseType: "json" },
|
|
16957
17165
|
);
|
|
16958
17166
|
const items = res.body.items;
|
|
16959
|
-
if (!items
|
|
17167
|
+
if (!items?.[0]) {
|
|
16960
17168
|
return [np, newBody, body];
|
|
16961
17169
|
}
|
|
16962
17170
|
if (items[0] && !items[0].items) {
|