@cyclonedx/cdxgen 12.4.2 → 12.4.4
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 +6 -0
- package/bin/audit.js +7 -0
- package/bin/cdxgen.js +48 -2
- package/bin/evinse.js +7 -0
- package/lib/audit/index.js +165 -2
- package/lib/audit/index.poku.js +462 -0
- package/lib/cli/index.js +320 -172
- package/lib/cli/index.poku.js +81 -0
- package/lib/evinser/evinser.js +31 -9
- package/lib/helpers/analyzer.js +890 -0
- package/lib/helpers/analyzer.poku.js +341 -0
- package/lib/helpers/atomUtils.js +445 -0
- package/lib/helpers/atomUtils.poku.js +137 -0
- package/lib/helpers/bomUtils.js +71 -0
- package/lib/helpers/bomUtils.poku.js +45 -0
- package/lib/helpers/depsUtils.js +146 -0
- package/lib/helpers/depsUtils.poku.js +183 -0
- package/lib/helpers/display.js +12 -6
- package/lib/helpers/display.poku.js +38 -0
- package/lib/helpers/utils.js +653 -191
- package/lib/helpers/utils.poku.js +414 -4
- package/lib/managers/binary.js +18 -9
- package/lib/stages/postgen/postgen.js +215 -0
- package/lib/stages/postgen/postgen.poku.js +218 -3
- package/lib/validator/bomValidator.js +11 -2
- package/package.json +8 -8
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/atomUtils.d.ts +18 -0
- package/types/lib/helpers/atomUtils.d.ts.map +1 -0
- package/types/lib/helpers/bomUtils.d.ts +10 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -1
- package/types/lib/helpers/depsUtils.d.ts +9 -0
- package/types/lib/helpers/depsUtils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/dosaiParsers.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +19 -0
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +2 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/helpers/utils.js
CHANGED
|
@@ -56,6 +56,7 @@ import { getTreeWithPlugin } from "../managers/piptree.js";
|
|
|
56
56
|
import { IriValidationStrategy, validateIri } from "../parsers/iri.js";
|
|
57
57
|
import Arborist from "../third-party/arborist/lib/index.js";
|
|
58
58
|
import { analyzeSuspiciousJsFile } from "./analyzer.js";
|
|
59
|
+
import { buildAtomCommandEnv } from "./atomUtils.js";
|
|
59
60
|
import { DEFAULT_HBOM_AUDIT_CATEGORIES } from "./auditCategories.js";
|
|
60
61
|
import { parseWorkflowFile } from "./ciParsers/githubActions.js";
|
|
61
62
|
import {
|
|
@@ -1115,6 +1116,50 @@ function isWindowsShellHijackRisk(command, options) {
|
|
|
1115
1116
|
|
|
1116
1117
|
const VERSION_PROBE_ARGS = new Set(["--version", "-version", "version"]);
|
|
1117
1118
|
|
|
1119
|
+
const POSIX_SHELL_METACHARACTERS = /[;&|<>$`\\\n\r]/;
|
|
1120
|
+
const WINDOWS_SHELL_METACHARACTERS = /[&|<>^%\n\r]/;
|
|
1121
|
+
|
|
1122
|
+
function hasShellMetacharacters(value) {
|
|
1123
|
+
if (value === undefined || value === null) {
|
|
1124
|
+
return false;
|
|
1125
|
+
}
|
|
1126
|
+
const stringValue = String(value);
|
|
1127
|
+
return isWin
|
|
1128
|
+
? WINDOWS_SHELL_METACHARACTERS.test(stringValue)
|
|
1129
|
+
: POSIX_SHELL_METACHARACTERS.test(stringValue);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
function getUnsafeShellToken(command, args) {
|
|
1133
|
+
if (hasShellMetacharacters(command)) {
|
|
1134
|
+
return command;
|
|
1135
|
+
}
|
|
1136
|
+
const argList = Array.isArray(args)
|
|
1137
|
+
? args
|
|
1138
|
+
: args === undefined || args === null
|
|
1139
|
+
? []
|
|
1140
|
+
: [args];
|
|
1141
|
+
return argList.find((arg) => hasShellMetacharacters(arg));
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function recordSuspiciousShellPathActivities(files, metadata = {}) {
|
|
1145
|
+
for (const file of files) {
|
|
1146
|
+
if (!hasShellMetacharacters(file)) {
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
recordActivity({
|
|
1150
|
+
classification: "suspicious-path",
|
|
1151
|
+
discoveryType: metadata.discoveryType,
|
|
1152
|
+
kind: "inspect",
|
|
1153
|
+
pattern: metadata.pattern,
|
|
1154
|
+
reason:
|
|
1155
|
+
"Suspicious path contains shell metacharacters. cdxgen passes direct process arguments as argv values, but review this path before invoking external build tools on untrusted projects.",
|
|
1156
|
+
risk: "shell-metacharacters",
|
|
1157
|
+
status: "completed",
|
|
1158
|
+
target: file,
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1118
1163
|
function detectProbeType(command, args = []) {
|
|
1119
1164
|
const normalizedCommand = basename(String(command || "")).toLowerCase();
|
|
1120
1165
|
const normalizedArgs = (args || []).map((arg) => String(arg).toLowerCase());
|
|
@@ -1297,6 +1342,26 @@ export function safeSpawnSync(command, args, options) {
|
|
|
1297
1342
|
if (options.cdxgenActivity) {
|
|
1298
1343
|
delete options.cdxgenActivity;
|
|
1299
1344
|
}
|
|
1345
|
+
if (options.shell === true) {
|
|
1346
|
+
const unsafeShellToken = getUnsafeShellToken(command, args);
|
|
1347
|
+
if (unsafeShellToken !== undefined) {
|
|
1348
|
+
const blockedReason = `Blocked shell execution for ${command}: command or argument contains shell metacharacters.`;
|
|
1349
|
+
console.warn(`\x1b[1;31mSecurity Alert: ${blockedReason}\x1b[0m`);
|
|
1350
|
+
recordActivity({
|
|
1351
|
+
kind: activityDescriptor.kind,
|
|
1352
|
+
...activityDescriptor.metadata,
|
|
1353
|
+
reason: blockedReason,
|
|
1354
|
+
status: "blocked",
|
|
1355
|
+
target: activityDescriptor.target,
|
|
1356
|
+
});
|
|
1357
|
+
return {
|
|
1358
|
+
status: 1,
|
|
1359
|
+
stdout: undefined,
|
|
1360
|
+
stderr: undefined,
|
|
1361
|
+
error: new Error(blockedReason),
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1300
1365
|
// Inject maxBuffer
|
|
1301
1366
|
if (!options.maxBuffer) {
|
|
1302
1367
|
options.maxBuffer = MAX_BUFFER;
|
|
@@ -1496,6 +1561,55 @@ export const PREFER_MAVEN_DEPS_TREE = !["false", "0"].includes(
|
|
|
1496
1561
|
process.env?.PREFER_MAVEN_DEPS_TREE,
|
|
1497
1562
|
);
|
|
1498
1563
|
|
|
1564
|
+
export function parseMavenArgs(argsString) {
|
|
1565
|
+
if (!argsString) {
|
|
1566
|
+
return [];
|
|
1567
|
+
}
|
|
1568
|
+
const args = [];
|
|
1569
|
+
let currentArg = "";
|
|
1570
|
+
let quoteChar = "";
|
|
1571
|
+
for (let i = 0; i < argsString.length; i++) {
|
|
1572
|
+
const char = argsString[i];
|
|
1573
|
+
if (char === "\\") {
|
|
1574
|
+
const nextChar = argsString[i + 1];
|
|
1575
|
+
if (
|
|
1576
|
+
nextChar &&
|
|
1577
|
+
(/\s/.test(nextChar) || nextChar === '"' || nextChar === "'")
|
|
1578
|
+
) {
|
|
1579
|
+
currentArg += nextChar;
|
|
1580
|
+
i++;
|
|
1581
|
+
} else {
|
|
1582
|
+
currentArg += char;
|
|
1583
|
+
}
|
|
1584
|
+
continue;
|
|
1585
|
+
}
|
|
1586
|
+
if (quoteChar) {
|
|
1587
|
+
if (char === quoteChar) {
|
|
1588
|
+
quoteChar = "";
|
|
1589
|
+
} else {
|
|
1590
|
+
currentArg += char;
|
|
1591
|
+
}
|
|
1592
|
+
continue;
|
|
1593
|
+
}
|
|
1594
|
+
if (char === '"' || char === "'") {
|
|
1595
|
+
quoteChar = char;
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
if (/\s/.test(char)) {
|
|
1599
|
+
if (currentArg) {
|
|
1600
|
+
args.push(currentArg);
|
|
1601
|
+
currentArg = "";
|
|
1602
|
+
}
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
currentArg += char;
|
|
1606
|
+
}
|
|
1607
|
+
if (currentArg) {
|
|
1608
|
+
args.push(currentArg);
|
|
1609
|
+
}
|
|
1610
|
+
return args;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1499
1613
|
/**
|
|
1500
1614
|
* Determines whether license information should be fetched from remote sources,
|
|
1501
1615
|
* based on the FETCH_LICENSE environment variable.
|
|
@@ -2376,6 +2490,10 @@ export function getAllFilesWithIgnore(
|
|
|
2376
2490
|
reasonBuilder: (count) =>
|
|
2377
2491
|
`Scanned ${dirPath} with glob '${patternValue}' for ${discoveryMetadata.label}; matched ${files.length} path(s)${buildReadCountSuffix(count)}.`,
|
|
2378
2492
|
});
|
|
2493
|
+
recordSuspiciousShellPathActivities(files, {
|
|
2494
|
+
discoveryType: discoveryMetadata.discoveryType,
|
|
2495
|
+
pattern: patternValue,
|
|
2496
|
+
});
|
|
2379
2497
|
if (files.length > 1) {
|
|
2380
2498
|
thoughtLog(
|
|
2381
2499
|
`Found ${files.length} files for the pattern '${pattern}' at '${dirPath}'.`,
|
|
@@ -3519,6 +3637,12 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
3519
3637
|
if (isDevelopmentNode) {
|
|
3520
3638
|
_setNpmDevelopmentProperty(pkg);
|
|
3521
3639
|
}
|
|
3640
|
+
if (node.optional === true) {
|
|
3641
|
+
_setNpmOptionalProperty(pkg);
|
|
3642
|
+
}
|
|
3643
|
+
if (node.peer === true) {
|
|
3644
|
+
_setNpmPeerProperty(pkg);
|
|
3645
|
+
}
|
|
3522
3646
|
if (node.resolved) {
|
|
3523
3647
|
if (node.resolved.startsWith("file:")) {
|
|
3524
3648
|
pkg.properties.push({
|
|
@@ -4101,6 +4225,177 @@ function _parseYarnLine(l) {
|
|
|
4101
4225
|
return { group, name };
|
|
4102
4226
|
}
|
|
4103
4227
|
|
|
4228
|
+
function _resolveYarnDependencyBomRef(
|
|
4229
|
+
packageName,
|
|
4230
|
+
versionRange,
|
|
4231
|
+
identMap,
|
|
4232
|
+
workspacePackages = [],
|
|
4233
|
+
) {
|
|
4234
|
+
if (!packageName || !versionRange) {
|
|
4235
|
+
return undefined;
|
|
4236
|
+
}
|
|
4237
|
+
let packageNameToUse = packageName;
|
|
4238
|
+
let versionRangeToUse = versionRange;
|
|
4239
|
+
if (versionRange.startsWith("npm:")) {
|
|
4240
|
+
if (versionRange.includes("@")) {
|
|
4241
|
+
versionRangeToUse = versionRange.split("@").splice(-1)[0];
|
|
4242
|
+
packageNameToUse = versionRange
|
|
4243
|
+
.replace("npm:", "")
|
|
4244
|
+
.replace(`@${versionRangeToUse}`, "");
|
|
4245
|
+
} else {
|
|
4246
|
+
versionRangeToUse = versionRange.replace("npm:", "");
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
let resolvedVersion =
|
|
4250
|
+
identMap[`${packageName}|${versionRangeToUse}`] ||
|
|
4251
|
+
identMap[`${packageNameToUse}|${versionRangeToUse}`];
|
|
4252
|
+
if (!resolvedVersion) {
|
|
4253
|
+
const packageKeys = Object.keys(identMap).filter((key) => {
|
|
4254
|
+
const [pkg] = key.split("|");
|
|
4255
|
+
return pkg === packageName || pkg === packageNameToUse;
|
|
4256
|
+
});
|
|
4257
|
+
resolvedVersion = identMap[packageKeys[0]];
|
|
4258
|
+
}
|
|
4259
|
+
if (!resolvedVersion && workspacePackages?.length) {
|
|
4260
|
+
const matchingWorkspace = findMatchingWorkspace(
|
|
4261
|
+
workspacePackages,
|
|
4262
|
+
packageNameToUse,
|
|
4263
|
+
);
|
|
4264
|
+
if (matchingWorkspace) {
|
|
4265
|
+
const versionMatch = matchingWorkspace.match(/@([^@]+)$/);
|
|
4266
|
+
if (versionMatch) {
|
|
4267
|
+
resolvedVersion = versionMatch[1];
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
let purlPart = `pkg:npm/${encodeURIComponent(packageNameToUse).replace(/%2F/g, "/")}`;
|
|
4272
|
+
if (resolvedVersion?.length) {
|
|
4273
|
+
purlPart = `${purlPart}@${resolvedVersion}`;
|
|
4274
|
+
}
|
|
4275
|
+
return decodeURIComponent(PackageURL.fromString(purlPart).toString());
|
|
4276
|
+
}
|
|
4277
|
+
|
|
4278
|
+
function _collectYarnRootDependencyRefs(
|
|
4279
|
+
yarnLockFile,
|
|
4280
|
+
identMap,
|
|
4281
|
+
workspacePackages = [],
|
|
4282
|
+
) {
|
|
4283
|
+
const rootRefs = _createYarnRootDependencyRefs();
|
|
4284
|
+
const packageJsonFile = join(dirname(yarnLockFile), "package.json");
|
|
4285
|
+
if (!safeExistsSync(packageJsonFile)) {
|
|
4286
|
+
return rootRefs;
|
|
4287
|
+
}
|
|
4288
|
+
let packageJson;
|
|
4289
|
+
try {
|
|
4290
|
+
packageJson = JSON.parse(readFileSync(packageJsonFile, "utf-8"));
|
|
4291
|
+
} catch {
|
|
4292
|
+
return rootRefs;
|
|
4293
|
+
}
|
|
4294
|
+
for (const dependencyType of Object.keys(rootRefs)) {
|
|
4295
|
+
for (const [packageName, versionRange] of Object.entries(
|
|
4296
|
+
packageJson[dependencyType] || {},
|
|
4297
|
+
)) {
|
|
4298
|
+
const bomRef = _resolveYarnDependencyBomRef(
|
|
4299
|
+
packageName,
|
|
4300
|
+
String(versionRange),
|
|
4301
|
+
identMap,
|
|
4302
|
+
workspacePackages,
|
|
4303
|
+
);
|
|
4304
|
+
if (bomRef) {
|
|
4305
|
+
rootRefs[dependencyType].add(bomRef);
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
return rootRefs;
|
|
4310
|
+
}
|
|
4311
|
+
|
|
4312
|
+
function _createYarnRootDependencyRefs() {
|
|
4313
|
+
return {
|
|
4314
|
+
dependencies: new Set(),
|
|
4315
|
+
devDependencies: new Set(),
|
|
4316
|
+
optionalDependencies: new Set(),
|
|
4317
|
+
peerDependencies: new Set(),
|
|
4318
|
+
};
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
function _buildDependencyClosure(rootRefs, dependenciesMap) {
|
|
4322
|
+
const closure = new Set();
|
|
4323
|
+
const stack = [...rootRefs];
|
|
4324
|
+
while (stack.length) {
|
|
4325
|
+
const ref = stack.pop();
|
|
4326
|
+
if (!ref || closure.has(ref)) {
|
|
4327
|
+
continue;
|
|
4328
|
+
}
|
|
4329
|
+
closure.add(ref);
|
|
4330
|
+
for (const childRef of dependenciesMap[ref] || []) {
|
|
4331
|
+
stack.push(childRef);
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
return closure;
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
function _markYarnDependencyScopeClosures(
|
|
4338
|
+
pkgList,
|
|
4339
|
+
dependenciesList,
|
|
4340
|
+
yarnRootRefs,
|
|
4341
|
+
yarnOptionalDependencyRefs,
|
|
4342
|
+
) {
|
|
4343
|
+
const dependenciesMap = {};
|
|
4344
|
+
for (const dependency of dependenciesList || []) {
|
|
4345
|
+
if (!dependenciesMap[dependency.ref]) {
|
|
4346
|
+
dependenciesMap[dependency.ref] = [];
|
|
4347
|
+
}
|
|
4348
|
+
dependenciesMap[dependency.ref] = Array.from(
|
|
4349
|
+
new Set([
|
|
4350
|
+
...dependenciesMap[dependency.ref],
|
|
4351
|
+
...(dependency.dependsOn || []),
|
|
4352
|
+
]),
|
|
4353
|
+
);
|
|
4354
|
+
}
|
|
4355
|
+
const runtimeClosure = _buildDependencyClosure(
|
|
4356
|
+
yarnRootRefs.dependencies,
|
|
4357
|
+
dependenciesMap,
|
|
4358
|
+
);
|
|
4359
|
+
const devClosure = _buildDependencyClosure(
|
|
4360
|
+
yarnRootRefs.devDependencies,
|
|
4361
|
+
dependenciesMap,
|
|
4362
|
+
);
|
|
4363
|
+
const optionalClosure = _buildDependencyClosure(
|
|
4364
|
+
new Set([
|
|
4365
|
+
...yarnRootRefs.optionalDependencies,
|
|
4366
|
+
...yarnOptionalDependencyRefs,
|
|
4367
|
+
]),
|
|
4368
|
+
dependenciesMap,
|
|
4369
|
+
);
|
|
4370
|
+
const peerClosure = _buildDependencyClosure(
|
|
4371
|
+
yarnRootRefs.peerDependencies,
|
|
4372
|
+
dependenciesMap,
|
|
4373
|
+
);
|
|
4374
|
+
for (const pkg of pkgList) {
|
|
4375
|
+
const ref = pkg["bom-ref"];
|
|
4376
|
+
if (!ref) {
|
|
4377
|
+
continue;
|
|
4378
|
+
}
|
|
4379
|
+
if (
|
|
4380
|
+
optionalClosure.has(ref) &&
|
|
4381
|
+
(!runtimeClosure.has(ref) ||
|
|
4382
|
+
yarnRootRefs.optionalDependencies.has(ref) ||
|
|
4383
|
+
yarnOptionalDependencyRefs.has(ref))
|
|
4384
|
+
) {
|
|
4385
|
+
pkg.scope = "optional";
|
|
4386
|
+
_setNpmOptionalProperty(pkg);
|
|
4387
|
+
}
|
|
4388
|
+
if (peerClosure.has(ref) && !runtimeClosure.has(ref)) {
|
|
4389
|
+
pkg.scope = "optional";
|
|
4390
|
+
_setNpmPeerProperty(pkg);
|
|
4391
|
+
}
|
|
4392
|
+
if (devClosure.has(ref) && !runtimeClosure.has(ref)) {
|
|
4393
|
+
pkg.scope = "optional";
|
|
4394
|
+
_setNpmDevelopmentProperty(pkg);
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
|
|
4104
4399
|
/**
|
|
4105
4400
|
* Parse nodejs yarn lock file
|
|
4106
4401
|
*
|
|
@@ -4122,6 +4417,8 @@ export async function parseYarnLock(
|
|
|
4122
4417
|
let pkgList = [];
|
|
4123
4418
|
const dependenciesList = [];
|
|
4124
4419
|
const depKeys = {};
|
|
4420
|
+
let yarnRootRefs = _createYarnRootDependencyRefs();
|
|
4421
|
+
const yarnOptionalDependencyRefs = new Set();
|
|
4125
4422
|
if (safeExistsSync(yarnLockFile)) {
|
|
4126
4423
|
const lockData = readFileSync(yarnLockFile, "utf8");
|
|
4127
4424
|
let name = "";
|
|
@@ -4138,6 +4435,11 @@ export async function parseYarnLock(
|
|
|
4138
4435
|
const workspacePurlMap = {};
|
|
4139
4436
|
// This would have the keys and the resolved version required to solve the dependency tree
|
|
4140
4437
|
const identMap = yarnLockToIdentMap(lockData);
|
|
4438
|
+
yarnRootRefs = _collectYarnRootDependencyRefs(
|
|
4439
|
+
yarnLockFile,
|
|
4440
|
+
identMap,
|
|
4441
|
+
workspacePackages,
|
|
4442
|
+
);
|
|
4141
4443
|
lockData.split("\n").forEach((l) => {
|
|
4142
4444
|
l = l.replace("\r", "");
|
|
4143
4445
|
if (l.startsWith("#")) {
|
|
@@ -4335,66 +4637,19 @@ export async function parseYarnLock(
|
|
|
4335
4637
|
if (dgroupname.endsWith(":")) {
|
|
4336
4638
|
dgroupname = dgroupname.substring(0, dgroupname.length - 1);
|
|
4337
4639
|
}
|
|
4338
|
-
let dgroupnameToUse = dgroupname;
|
|
4339
4640
|
const range = tmpA[1].replace(/["']/g, "");
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
.replace(`@${versionRange}`, "");
|
|
4349
|
-
} else {
|
|
4350
|
-
// Case like npm:^5.1.1 - just a version range with npm: prefix
|
|
4351
|
-
versionRange = range.replace("npm:", "");
|
|
4352
|
-
dgroupnameToUse = dgroupname;
|
|
4353
|
-
}
|
|
4354
|
-
}
|
|
4355
|
-
let resolvedVersion =
|
|
4356
|
-
identMap[`${dgroupname}|${versionRange}`] ||
|
|
4357
|
-
identMap[`${dgroupnameToUse}|${versionRange}`];
|
|
4358
|
-
// If we couldn't resolve the version directly, try to find any resolved version for this package
|
|
4359
|
-
if (!resolvedVersion) {
|
|
4360
|
-
const packageKeys = Object.keys(identMap).filter((key) => {
|
|
4361
|
-
const [pkg] = key.split("|");
|
|
4362
|
-
return pkg === dgroupname || pkg === dgroupnameToUse;
|
|
4363
|
-
});
|
|
4364
|
-
|
|
4365
|
-
if (packageKeys.length > 0) {
|
|
4366
|
-
// Use the first available resolved version for this package
|
|
4367
|
-
resolvedVersion = identMap[packageKeys[0]];
|
|
4368
|
-
}
|
|
4369
|
-
}
|
|
4370
|
-
// If we couldn't resolve the version from yarn.lock, check if it's a workspace package
|
|
4371
|
-
if (!resolvedVersion && workspacePackages?.length) {
|
|
4372
|
-
// Use helper function for more efficient workspace matching
|
|
4373
|
-
const matchingWorkspace = findMatchingWorkspace(
|
|
4374
|
-
workspacePackages,
|
|
4375
|
-
dgroupnameToUse,
|
|
4376
|
-
);
|
|
4377
|
-
|
|
4378
|
-
if (matchingWorkspace) {
|
|
4379
|
-
// Extract version from workspace PURL like "npm:@swc/helpers@0.4.14"
|
|
4380
|
-
const versionMatch = matchingWorkspace.match(/@([^@]+)$/);
|
|
4381
|
-
if (versionMatch) {
|
|
4382
|
-
resolvedVersion = versionMatch[1];
|
|
4383
|
-
}
|
|
4384
|
-
}
|
|
4385
|
-
}
|
|
4386
|
-
// If we still can't resolve the version, use "" as fallback
|
|
4387
|
-
if (!resolvedVersion) {
|
|
4388
|
-
resolvedVersion = "";
|
|
4641
|
+
const depBomRef = _resolveYarnDependencyBomRef(
|
|
4642
|
+
dgroupname,
|
|
4643
|
+
range,
|
|
4644
|
+
identMap,
|
|
4645
|
+
workspacePackages,
|
|
4646
|
+
);
|
|
4647
|
+
if (depBomRef) {
|
|
4648
|
+
deplist.add(depBomRef);
|
|
4389
4649
|
}
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
let purlPart = `pkg:npm/${encodeURIComponent(dgroupnameToUse).replace(/%2F/g, "/")}`;
|
|
4393
|
-
if (resolvedVersion?.length) {
|
|
4394
|
-
purlPart = `${purlPart}@${resolvedVersion}`;
|
|
4650
|
+
if (optionalDepsMode && depBomRef) {
|
|
4651
|
+
yarnOptionalDependencyRefs.add(depBomRef);
|
|
4395
4652
|
}
|
|
4396
|
-
const depPurlString = PackageURL.fromString(purlPart).toString();
|
|
4397
|
-
deplist.add(decodeURIComponent(depPurlString));
|
|
4398
4653
|
}
|
|
4399
4654
|
} else if (name !== "") {
|
|
4400
4655
|
if (!l.startsWith(" ")) {
|
|
@@ -4604,6 +4859,13 @@ export async function parseYarnLock(
|
|
|
4604
4859
|
}
|
|
4605
4860
|
}
|
|
4606
4861
|
|
|
4862
|
+
_markYarnDependencyScopeClosures(
|
|
4863
|
+
pkgList,
|
|
4864
|
+
dependenciesList,
|
|
4865
|
+
yarnRootRefs,
|
|
4866
|
+
yarnOptionalDependencyRefs,
|
|
4867
|
+
);
|
|
4868
|
+
|
|
4607
4869
|
if (shouldFetchPackageMetadata() && pkgList?.length) {
|
|
4608
4870
|
if (DEBUG_MODE) {
|
|
4609
4871
|
console.log(
|
|
@@ -4762,6 +5024,14 @@ function _setNpmDevelopmentProperty(pkg) {
|
|
|
4762
5024
|
}
|
|
4763
5025
|
}
|
|
4764
5026
|
|
|
5027
|
+
function _setNpmOptionalProperty(pkg) {
|
|
5028
|
+
addComponentProperty(pkg, "cdx:npm:package:optional", "true");
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
function _setNpmPeerProperty(pkg) {
|
|
5032
|
+
addComponentProperty(pkg, "cdx:npm:package:peer", "true");
|
|
5033
|
+
}
|
|
5034
|
+
|
|
4765
5035
|
function _setTreeWorkspaceRef(
|
|
4766
5036
|
dependenciesMap,
|
|
4767
5037
|
depref,
|
|
@@ -4863,11 +5133,25 @@ export function parsePnpmWorkspace(workspaceFile) {
|
|
|
4863
5133
|
if (!yamlObj) {
|
|
4864
5134
|
return {};
|
|
4865
5135
|
}
|
|
4866
|
-
const
|
|
4867
|
-
.
|
|
4868
|
-
|
|
5136
|
+
const workspacePackages = Array.isArray(yamlObj.packages)
|
|
5137
|
+
? yamlObj.packages
|
|
5138
|
+
: typeof yamlObj.packages === "string"
|
|
5139
|
+
? [yamlObj.packages]
|
|
5140
|
+
: [];
|
|
5141
|
+
const excludePackages = workspacePackages
|
|
5142
|
+
.filter((n) => typeof n === "string")
|
|
5143
|
+
.filter((n) => n.startsWith("!"))
|
|
5144
|
+
.map((n) => n.replace(/^!/, ""));
|
|
5145
|
+
const packagePatterns = workspacePackages
|
|
5146
|
+
.filter((n) => typeof n === "string")
|
|
5147
|
+
.filter((n) => !/^(!|\.|__)/.test(n));
|
|
5148
|
+
const packages = packagePatterns.map((n) =>
|
|
5149
|
+
n.replaceAll("/**", "").replaceAll("/*", ""),
|
|
5150
|
+
);
|
|
4869
5151
|
const catalogs = yamlObj.catalog || {};
|
|
4870
5152
|
return {
|
|
5153
|
+
excludePackages,
|
|
5154
|
+
packagePatterns,
|
|
4871
5155
|
packages,
|
|
4872
5156
|
catalogs,
|
|
4873
5157
|
};
|
|
@@ -4990,12 +5274,20 @@ export function findPnpmPackagePath(baseDir, packageName, version) {
|
|
|
4990
5274
|
if (safeExistsSync(pnpmDir)) {
|
|
4991
5275
|
// pnpm stores packages as {name}@{version} in .pnpm directory
|
|
4992
5276
|
const encodedName = packageName.replace("/", "%2f");
|
|
5277
|
+
const virtualStoreName = packageName.replace("/", "+");
|
|
4993
5278
|
let pnpmPackagePath;
|
|
4994
5279
|
|
|
4995
5280
|
// Try different formats that pnpm might use
|
|
4996
5281
|
const possiblePaths = [
|
|
5282
|
+
join(
|
|
5283
|
+
pnpmDir,
|
|
5284
|
+
`${virtualStoreName}@${version}`,
|
|
5285
|
+
"node_modules",
|
|
5286
|
+
packageName,
|
|
5287
|
+
),
|
|
4997
5288
|
join(pnpmDir, `${encodedName}@${version}`, "node_modules", packageName),
|
|
4998
5289
|
join(pnpmDir, `${packageName}@${version}`, "node_modules", packageName),
|
|
5290
|
+
join(pnpmDir, `${virtualStoreName}@${version}`),
|
|
4999
5291
|
join(pnpmDir, `${encodedName}@${version}`),
|
|
5000
5292
|
join(pnpmDir, `${packageName}@${version}`),
|
|
5001
5293
|
];
|
|
@@ -5041,7 +5333,8 @@ export async function pnpmMetadata(pkgList, lockFilePath) {
|
|
|
5041
5333
|
}
|
|
5042
5334
|
let enhancedCount = 0;
|
|
5043
5335
|
for (const pkg of pkgList) {
|
|
5044
|
-
const
|
|
5336
|
+
const packageName = pkg.group ? `${pkg.group}/${pkg.name}` : pkg.name;
|
|
5337
|
+
const packagePath = findPnpmPackagePath(baseDir, packageName, pkg.version);
|
|
5045
5338
|
if (!packagePath) {
|
|
5046
5339
|
continue;
|
|
5047
5340
|
}
|
|
@@ -5118,9 +5411,10 @@ export async function pnpmMetadata(pkgList, lockFilePath) {
|
|
|
5118
5411
|
* @param {Object} parentComponent parent component
|
|
5119
5412
|
* @param {Array[String]} workspacePackages Workspace packages
|
|
5120
5413
|
* @param {Object} workspaceSrcFiles Workspace package.json files
|
|
5121
|
-
* @param {Object}
|
|
5122
|
-
* @param {Object}
|
|
5414
|
+
* @param {Object} _workspaceCatalogs Workspace catalogs
|
|
5415
|
+
* @param {Object} _workspaceDirectDeps Direct dependencies of each workspace
|
|
5123
5416
|
* @param {Object} depsWorkspaceRefs Workspace references for each dependency
|
|
5417
|
+
* @param {string} projectRoot Root path used to relativize pnpm-lock evidence paths
|
|
5124
5418
|
*/
|
|
5125
5419
|
export async function parsePnpmLock(
|
|
5126
5420
|
pnpmLock,
|
|
@@ -5130,9 +5424,15 @@ export async function parsePnpmLock(
|
|
|
5130
5424
|
_workspaceCatalogs = {},
|
|
5131
5425
|
_workspaceDirectDeps = {},
|
|
5132
5426
|
depsWorkspaceRefs = {},
|
|
5427
|
+
projectRoot = undefined,
|
|
5133
5428
|
) {
|
|
5134
5429
|
let pkgList = [];
|
|
5135
5430
|
const dependenciesList = [];
|
|
5431
|
+
const pnpmLockDir = dirname(pnpmLock);
|
|
5432
|
+
const pnpmEvidenceRoot = projectRoot ? resolve(projectRoot) : pnpmLockDir;
|
|
5433
|
+
const pnpmLockSrcFile = path.isAbsolute(pnpmLock)
|
|
5434
|
+
? relative(pnpmEvidenceRoot, pnpmLock)
|
|
5435
|
+
: pnpmLock;
|
|
5136
5436
|
// For lockfile >= 9, we need to track dev and optional packages manually
|
|
5137
5437
|
// See: #1163
|
|
5138
5438
|
// Moreover, we have changed >= 9 for >= 6
|
|
@@ -5234,7 +5534,9 @@ export async function parsePnpmLock(
|
|
|
5234
5534
|
}
|
|
5235
5535
|
// Find the root optional and peer dependencies
|
|
5236
5536
|
for (const rdk of Object.keys({ ...rootOptionalDeps, ...rootPeerDeps })) {
|
|
5237
|
-
const version = await getVersionNumPnpm(
|
|
5537
|
+
const version = await getVersionNumPnpm(
|
|
5538
|
+
rootOptionalDeps[rdk] || rootPeerDeps[rdk],
|
|
5539
|
+
);
|
|
5238
5540
|
let specifier;
|
|
5239
5541
|
if (
|
|
5240
5542
|
typeof rootOptionalDeps[rdk] === "object" &&
|
|
@@ -5303,8 +5605,12 @@ export async function parsePnpmLock(
|
|
|
5303
5605
|
let compPurl;
|
|
5304
5606
|
let pkgSrcFile;
|
|
5305
5607
|
let fallbackMode = true;
|
|
5306
|
-
if (
|
|
5307
|
-
|
|
5608
|
+
if (
|
|
5609
|
+
safeExistsSync(
|
|
5610
|
+
join(pnpmLockDir, importedComponentName, "package.json"),
|
|
5611
|
+
)
|
|
5612
|
+
) {
|
|
5613
|
+
pkgSrcFile = join(pnpmLockDir, importedComponentName, "package.json");
|
|
5308
5614
|
const importedComponentObj = await parsePkgJson(pkgSrcFile, true);
|
|
5309
5615
|
if (importedComponentObj.length) {
|
|
5310
5616
|
const version = importedComponentObj[0].version;
|
|
@@ -5436,7 +5742,9 @@ export async function parsePnpmLock(
|
|
|
5436
5742
|
...componentOptionalDeps,
|
|
5437
5743
|
...componentPeerDeps,
|
|
5438
5744
|
})) {
|
|
5439
|
-
const version = await getVersionNumPnpm(
|
|
5745
|
+
const version = await getVersionNumPnpm(
|
|
5746
|
+
componentOptionalDeps[cdk] || componentPeerDeps[cdk],
|
|
5747
|
+
);
|
|
5440
5748
|
const dpurl = new PackageURL(
|
|
5441
5749
|
"npm",
|
|
5442
5750
|
"",
|
|
@@ -5727,7 +6035,7 @@ export async function parsePnpmLock(
|
|
|
5727
6035
|
const properties = [
|
|
5728
6036
|
{
|
|
5729
6037
|
name: "SrcFile",
|
|
5730
|
-
value:
|
|
6038
|
+
value: pnpmLockSrcFile,
|
|
5731
6039
|
},
|
|
5732
6040
|
];
|
|
5733
6041
|
if (hasBin) {
|
|
@@ -5838,7 +6146,7 @@ export async function parsePnpmLock(
|
|
|
5838
6146
|
{
|
|
5839
6147
|
technique: "manifest-analysis",
|
|
5840
6148
|
confidence: 1,
|
|
5841
|
-
value:
|
|
6149
|
+
value: pnpmLockSrcFile,
|
|
5842
6150
|
},
|
|
5843
6151
|
],
|
|
5844
6152
|
},
|
|
@@ -5852,6 +6160,9 @@ export async function parsePnpmLock(
|
|
|
5852
6160
|
},
|
|
5853
6161
|
];
|
|
5854
6162
|
}
|
|
6163
|
+
if (possibleOptionalDeps[thePkg["bom-ref"]]) {
|
|
6164
|
+
_setNpmOptionalProperty(thePkg);
|
|
6165
|
+
}
|
|
5855
6166
|
// Don't add internal workspace packages to the components list
|
|
5856
6167
|
if (thePkg.type !== "application") {
|
|
5857
6168
|
pkgList.push(thePkg);
|
|
@@ -6190,6 +6501,15 @@ export function parsePom(pomFile) {
|
|
|
6190
6501
|
attributesKey: "$",
|
|
6191
6502
|
commentKey: "value",
|
|
6192
6503
|
}).project;
|
|
6504
|
+
if (project?.modelVersion?._) {
|
|
6505
|
+
properties.modelVersion = project.modelVersion._;
|
|
6506
|
+
}
|
|
6507
|
+
if (project?.$?.root) {
|
|
6508
|
+
properties.mavenRoot = project.$.root;
|
|
6509
|
+
}
|
|
6510
|
+
if (project?.$?.["preserve.model.version"]) {
|
|
6511
|
+
properties.preserveModelVersion = project.$["preserve.model.version"];
|
|
6512
|
+
}
|
|
6193
6513
|
for (const aprop of [
|
|
6194
6514
|
"groupId",
|
|
6195
6515
|
"artifactId",
|
|
@@ -6224,14 +6544,20 @@ export function parsePom(pomFile) {
|
|
|
6224
6544
|
null,
|
|
6225
6545
|
).toString();
|
|
6226
6546
|
}
|
|
6547
|
+
const moduleEntries = [];
|
|
6227
6548
|
if (project?.modules?.module) {
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6549
|
+
moduleEntries.push(project.modules.module);
|
|
6550
|
+
}
|
|
6551
|
+
if (project?.subprojects?.subproject) {
|
|
6552
|
+
moduleEntries.push(project.subprojects.subproject);
|
|
6553
|
+
}
|
|
6554
|
+
if (moduleEntries.length) {
|
|
6555
|
+
modules = moduleEntries.flatMap((entry) => {
|
|
6556
|
+
if (Array.isArray(entry)) {
|
|
6557
|
+
return entry.map((m) => m?._).filter(Boolean);
|
|
6558
|
+
}
|
|
6559
|
+
return entry?._ ? [entry._] : [];
|
|
6560
|
+
});
|
|
6235
6561
|
}
|
|
6236
6562
|
if (project?.properties) {
|
|
6237
6563
|
for (const aprop of Object.keys(project.properties)) {
|
|
@@ -6276,18 +6602,28 @@ export function parsePom(pomFile) {
|
|
|
6276
6602
|
if (versionStr?.includes("$")) {
|
|
6277
6603
|
versionStr = properties[versionStr?.replace(/[${}]/g, "")];
|
|
6278
6604
|
}
|
|
6279
|
-
|
|
6605
|
+
const scope = adep.scope?._;
|
|
6606
|
+
const type = adep.type?._ || "jar";
|
|
6607
|
+
const classifier = adep.classifier?._;
|
|
6608
|
+
const optional = adep.optional?._;
|
|
6609
|
+
const dependencyProperties = [
|
|
6610
|
+
{
|
|
6611
|
+
name: "SrcFile",
|
|
6612
|
+
value: pomFile,
|
|
6613
|
+
},
|
|
6614
|
+
];
|
|
6615
|
+
const qualifiers = { type };
|
|
6616
|
+
if (classifier) {
|
|
6617
|
+
qualifiers.classifier = classifier;
|
|
6618
|
+
}
|
|
6619
|
+
if (includeMavenTestScope || scope !== "test") {
|
|
6280
6620
|
deps.push({
|
|
6281
6621
|
group: adep.groupId ? adep.groupId._ : "",
|
|
6282
6622
|
name: adep.artifactId ? adep.artifactId._ : "",
|
|
6283
6623
|
version: versionStr,
|
|
6284
|
-
qualifiers
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
name: "SrcFile",
|
|
6288
|
-
value: pomFile,
|
|
6289
|
-
},
|
|
6290
|
-
],
|
|
6624
|
+
qualifiers,
|
|
6625
|
+
scope: mapMavenScope(scope, optional === "true"),
|
|
6626
|
+
properties: dependencyProperties,
|
|
6291
6627
|
evidence: {
|
|
6292
6628
|
identity: {
|
|
6293
6629
|
field: "purl",
|
|
@@ -6308,6 +6644,194 @@ export function parsePom(pomFile) {
|
|
|
6308
6644
|
return { isQuarkus, pomPurl, modules, properties, dependencies: deps };
|
|
6309
6645
|
}
|
|
6310
6646
|
|
|
6647
|
+
function mapMavenScope(componentScope, isOptional = false) {
|
|
6648
|
+
if (isOptional || componentScope === "test") {
|
|
6649
|
+
return "optional";
|
|
6650
|
+
}
|
|
6651
|
+
if (["compile", "runtime", "import"].includes(componentScope)) {
|
|
6652
|
+
return "required";
|
|
6653
|
+
}
|
|
6654
|
+
if (["provided", "system"].includes(componentScope)) {
|
|
6655
|
+
return "excluded";
|
|
6656
|
+
}
|
|
6657
|
+
return undefined;
|
|
6658
|
+
}
|
|
6659
|
+
|
|
6660
|
+
function createMavenComponentFromCoordinateParts(pkgArr, pomFile, isOptional) {
|
|
6661
|
+
let versionStr = pkgArr[pkgArr.length - 2];
|
|
6662
|
+
const componentScope = pkgArr[pkgArr.length - 1];
|
|
6663
|
+
let classifier;
|
|
6664
|
+
if (
|
|
6665
|
+
pkgArr.length >= 6 &&
|
|
6666
|
+
pkgArr[3] !== versionStr &&
|
|
6667
|
+
!pkgArr[3].includes(".jar")
|
|
6668
|
+
) {
|
|
6669
|
+
classifier = pkgArr[3];
|
|
6670
|
+
}
|
|
6671
|
+
if (pkgArr.length === 4) {
|
|
6672
|
+
versionStr = pkgArr[pkgArr.length - 1];
|
|
6673
|
+
}
|
|
6674
|
+
const qualifiers = { type: pkgArr[2] };
|
|
6675
|
+
if (classifier) {
|
|
6676
|
+
qualifiers.classifier = classifier;
|
|
6677
|
+
}
|
|
6678
|
+
const purlString = new PackageURL(
|
|
6679
|
+
"maven",
|
|
6680
|
+
pkgArr[0],
|
|
6681
|
+
pkgArr[1],
|
|
6682
|
+
versionStr,
|
|
6683
|
+
qualifiers,
|
|
6684
|
+
null,
|
|
6685
|
+
).toString();
|
|
6686
|
+
const bomRef = decodeURIComponent(purlString);
|
|
6687
|
+
const scope = mapMavenScope(componentScope, isOptional);
|
|
6688
|
+
const properties = [];
|
|
6689
|
+
const apkg = {
|
|
6690
|
+
group: pkgArr[0],
|
|
6691
|
+
name: pkgArr[1],
|
|
6692
|
+
version: versionStr,
|
|
6693
|
+
qualifiers,
|
|
6694
|
+
scope,
|
|
6695
|
+
properties,
|
|
6696
|
+
purl: purlString,
|
|
6697
|
+
"bom-ref": bomRef,
|
|
6698
|
+
};
|
|
6699
|
+
if (pomFile) {
|
|
6700
|
+
properties.push({
|
|
6701
|
+
name: "SrcFile",
|
|
6702
|
+
value: pomFile,
|
|
6703
|
+
});
|
|
6704
|
+
apkg.evidence = {
|
|
6705
|
+
identity: {
|
|
6706
|
+
field: "purl",
|
|
6707
|
+
confidence: 0.5,
|
|
6708
|
+
methods: [
|
|
6709
|
+
{
|
|
6710
|
+
technique: "manifest-analysis",
|
|
6711
|
+
confidence: 0.5,
|
|
6712
|
+
value: pomFile,
|
|
6713
|
+
},
|
|
6714
|
+
],
|
|
6715
|
+
},
|
|
6716
|
+
};
|
|
6717
|
+
}
|
|
6718
|
+
return apkg;
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
function createMavenComponentFromTreeNode(node, pomFile) {
|
|
6722
|
+
const type = node.type || "jar";
|
|
6723
|
+
const qualifiers = { type };
|
|
6724
|
+
if (node.classifier) {
|
|
6725
|
+
qualifiers.classifier = node.classifier;
|
|
6726
|
+
}
|
|
6727
|
+
const purlString = new PackageURL(
|
|
6728
|
+
"maven",
|
|
6729
|
+
node.groupId,
|
|
6730
|
+
node.artifactId,
|
|
6731
|
+
node.version,
|
|
6732
|
+
qualifiers,
|
|
6733
|
+
null,
|
|
6734
|
+
).toString();
|
|
6735
|
+
const bomRef = decodeURIComponent(purlString);
|
|
6736
|
+
const isOptional = node.optional === true || node.optional === "true";
|
|
6737
|
+
const properties = [];
|
|
6738
|
+
const apkg = {
|
|
6739
|
+
group: node.groupId,
|
|
6740
|
+
name: node.artifactId,
|
|
6741
|
+
version: node.version,
|
|
6742
|
+
qualifiers,
|
|
6743
|
+
scope: mapMavenScope(node.scope, isOptional),
|
|
6744
|
+
properties,
|
|
6745
|
+
purl: purlString,
|
|
6746
|
+
"bom-ref": bomRef,
|
|
6747
|
+
};
|
|
6748
|
+
if (pomFile) {
|
|
6749
|
+
properties.push({
|
|
6750
|
+
name: "SrcFile",
|
|
6751
|
+
value: pomFile,
|
|
6752
|
+
});
|
|
6753
|
+
apkg.evidence = {
|
|
6754
|
+
identity: {
|
|
6755
|
+
field: "purl",
|
|
6756
|
+
confidence: 0.5,
|
|
6757
|
+
methods: [
|
|
6758
|
+
{
|
|
6759
|
+
technique: "manifest-analysis",
|
|
6760
|
+
confidence: 0.5,
|
|
6761
|
+
value: pomFile,
|
|
6762
|
+
},
|
|
6763
|
+
],
|
|
6764
|
+
},
|
|
6765
|
+
};
|
|
6766
|
+
}
|
|
6767
|
+
return apkg;
|
|
6768
|
+
}
|
|
6769
|
+
|
|
6770
|
+
/**
|
|
6771
|
+
* Parse maven dependency:tree json output
|
|
6772
|
+
*
|
|
6773
|
+
* @param rawOutput
|
|
6774
|
+
* @param pomFile
|
|
6775
|
+
* @returns {{parentComponent: {}, pkgList: *[], dependenciesList: *[]}|{}|{}|*|{parentComponent: {[p: string]: *}|{}, pkgList: [], dependenciesList: []}}
|
|
6776
|
+
*/
|
|
6777
|
+
export function parseMavenTreeJson(rawOutput, pomFile) {
|
|
6778
|
+
if (!rawOutput) {
|
|
6779
|
+
return {};
|
|
6780
|
+
}
|
|
6781
|
+
let rootNode = rawOutput;
|
|
6782
|
+
if (typeof rawOutput === "string") {
|
|
6783
|
+
try {
|
|
6784
|
+
rootNode = JSON.parse(rawOutput);
|
|
6785
|
+
} catch (_err) {
|
|
6786
|
+
thoughtLog("Unable to parse Maven dependency:tree JSON output");
|
|
6787
|
+
return {};
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6790
|
+
const deps = [];
|
|
6791
|
+
const dependenciesList = [];
|
|
6792
|
+
const keysCache = new Set();
|
|
6793
|
+
const levelTrees = {};
|
|
6794
|
+
let parentComponent = {};
|
|
6795
|
+
const visitNode = (node, parentRef) => {
|
|
6796
|
+
if (!node?.groupId || !node?.artifactId || !node?.version) {
|
|
6797
|
+
return undefined;
|
|
6798
|
+
}
|
|
6799
|
+
const component = createMavenComponentFromTreeNode(node, pomFile);
|
|
6800
|
+
const bomRef = component["bom-ref"];
|
|
6801
|
+
if (!Object.keys(parentComponent).length) {
|
|
6802
|
+
parentComponent = { ...component, type: "application" };
|
|
6803
|
+
}
|
|
6804
|
+
if (!keysCache.has(bomRef)) {
|
|
6805
|
+
keysCache.add(bomRef);
|
|
6806
|
+
deps.push(component);
|
|
6807
|
+
}
|
|
6808
|
+
if (!levelTrees[bomRef]) {
|
|
6809
|
+
levelTrees[bomRef] = [];
|
|
6810
|
+
}
|
|
6811
|
+
if (parentRef) {
|
|
6812
|
+
const cnodes = levelTrees[parentRef] || [];
|
|
6813
|
+
cnodes.push(bomRef);
|
|
6814
|
+
levelTrees[parentRef] = cnodes;
|
|
6815
|
+
}
|
|
6816
|
+
for (const child of node.children || []) {
|
|
6817
|
+
visitNode(child, bomRef);
|
|
6818
|
+
}
|
|
6819
|
+
return bomRef;
|
|
6820
|
+
};
|
|
6821
|
+
visitNode(rootNode);
|
|
6822
|
+
for (const lk of Object.keys(levelTrees)) {
|
|
6823
|
+
dependenciesList.push({
|
|
6824
|
+
ref: lk,
|
|
6825
|
+
dependsOn: [...new Set(levelTrees[lk])].sort(),
|
|
6826
|
+
});
|
|
6827
|
+
}
|
|
6828
|
+
return {
|
|
6829
|
+
parentComponent,
|
|
6830
|
+
pkgList: deps,
|
|
6831
|
+
dependenciesList,
|
|
6832
|
+
};
|
|
6833
|
+
}
|
|
6834
|
+
|
|
6311
6835
|
/**
|
|
6312
6836
|
* Parse maven tree output
|
|
6313
6837
|
* @param {string} rawOutput Raw string output
|
|
@@ -6319,6 +6843,9 @@ export function parseMavenTree(rawOutput, pomFile) {
|
|
|
6319
6843
|
if (!rawOutput) {
|
|
6320
6844
|
return {};
|
|
6321
6845
|
}
|
|
6846
|
+
if (rawOutput.trim().startsWith("{")) {
|
|
6847
|
+
return parseMavenTreeJson(rawOutput, pomFile);
|
|
6848
|
+
}
|
|
6322
6849
|
const deps = [];
|
|
6323
6850
|
const dependenciesList = [];
|
|
6324
6851
|
const keys_cache = {};
|
|
@@ -6330,6 +6857,8 @@ export function parseMavenTree(rawOutput, pomFile) {
|
|
|
6330
6857
|
const stack = [];
|
|
6331
6858
|
tmpA.forEach((l) => {
|
|
6332
6859
|
l = l.replace("\r", "");
|
|
6860
|
+
const isOptional = /\s+\(optional\)\s*$/.test(l);
|
|
6861
|
+
l = l.replace(/\s+\(optional\)\s*$/, "");
|
|
6333
6862
|
if (!includeMavenTestScope && l.trim().endsWith(":test")) {
|
|
6334
6863
|
return;
|
|
6335
6864
|
}
|
|
@@ -6341,118 +6870,50 @@ export function parseMavenTree(rawOutput, pomFile) {
|
|
|
6341
6870
|
}
|
|
6342
6871
|
l = tmpline[tmpline.length - 1];
|
|
6343
6872
|
const pkgArr = l.split(":");
|
|
6344
|
-
// Support for classifiers
|
|
6345
|
-
// com.github.jnr:jffi:jar:1.3.11:compile
|
|
6346
|
-
// com.github.jnr:jffi:jar:native:1.3.11:runtime
|
|
6347
|
-
let classifier;
|
|
6348
6873
|
if (pkgArr && pkgArr.length > 2) {
|
|
6349
|
-
let versionStr = pkgArr[pkgArr.length - 2];
|
|
6350
6874
|
const componentScope = pkgArr[pkgArr.length - 1];
|
|
6351
|
-
if (
|
|
6352
|
-
pkgArr.length >= 6 &&
|
|
6353
|
-
pkgArr[3] !== versionStr &&
|
|
6354
|
-
!pkgArr[3].includes(".jar")
|
|
6355
|
-
) {
|
|
6356
|
-
classifier = pkgArr[3];
|
|
6357
|
-
}
|
|
6358
6875
|
// Ignore test scope
|
|
6359
6876
|
if (!includeMavenTestScope && componentScope === "test") {
|
|
6360
6877
|
return;
|
|
6361
6878
|
}
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
scope = "excluded";
|
|
6369
|
-
}
|
|
6370
|
-
if (pkgArr.length === 4) {
|
|
6371
|
-
versionStr = pkgArr[pkgArr.length - 1];
|
|
6372
|
-
}
|
|
6373
|
-
const qualifiers = { type: pkgArr[2] };
|
|
6374
|
-
if (classifier) {
|
|
6375
|
-
qualifiers.classifier = classifier;
|
|
6376
|
-
}
|
|
6377
|
-
const purlString = new PackageURL(
|
|
6378
|
-
"maven",
|
|
6379
|
-
pkgArr[0],
|
|
6380
|
-
pkgArr[1],
|
|
6381
|
-
versionStr,
|
|
6382
|
-
qualifiers,
|
|
6383
|
-
null,
|
|
6384
|
-
).toString();
|
|
6385
|
-
const bomRef = decodeURIComponent(purlString);
|
|
6879
|
+
const apkg = createMavenComponentFromCoordinateParts(
|
|
6880
|
+
pkgArr,
|
|
6881
|
+
pomFile,
|
|
6882
|
+
isOptional,
|
|
6883
|
+
);
|
|
6884
|
+
const bomRef = apkg["bom-ref"];
|
|
6386
6885
|
const key = bomRef;
|
|
6387
6886
|
if (!first_ref) {
|
|
6388
6887
|
first_ref = bomRef;
|
|
6389
6888
|
}
|
|
6390
6889
|
if (!keys_cache[key]) {
|
|
6391
6890
|
keys_cache[key] = key;
|
|
6392
|
-
const properties = [];
|
|
6393
|
-
if (scope) {
|
|
6394
|
-
properties.push({
|
|
6395
|
-
name: "cdx:maven:component_scope",
|
|
6396
|
-
value: componentScope,
|
|
6397
|
-
});
|
|
6398
|
-
}
|
|
6399
|
-
const apkg = {
|
|
6400
|
-
group: pkgArr[0],
|
|
6401
|
-
name: pkgArr[1],
|
|
6402
|
-
version: versionStr,
|
|
6403
|
-
qualifiers,
|
|
6404
|
-
scope,
|
|
6405
|
-
properties,
|
|
6406
|
-
purl: purlString,
|
|
6407
|
-
"bom-ref": bomRef,
|
|
6408
|
-
};
|
|
6409
|
-
if (pomFile) {
|
|
6410
|
-
properties.push({
|
|
6411
|
-
name: "SrcFile",
|
|
6412
|
-
value: pomFile,
|
|
6413
|
-
});
|
|
6414
|
-
apkg.evidence = {
|
|
6415
|
-
identity: {
|
|
6416
|
-
field: "purl",
|
|
6417
|
-
confidence: 0.5,
|
|
6418
|
-
methods: [
|
|
6419
|
-
{
|
|
6420
|
-
technique: "manifest-analysis",
|
|
6421
|
-
confidence: 0.5,
|
|
6422
|
-
value: pomFile,
|
|
6423
|
-
},
|
|
6424
|
-
],
|
|
6425
|
-
},
|
|
6426
|
-
};
|
|
6427
|
-
}
|
|
6428
6891
|
deps.push(apkg);
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
}
|
|
6441
|
-
} else {
|
|
6442
|
-
for (let i = level; i <= last_level; i++) {
|
|
6443
|
-
stack.pop();
|
|
6444
|
-
}
|
|
6445
|
-
const last_stack = stack.length
|
|
6446
|
-
? stack[stack.length - 1]
|
|
6447
|
-
: first_ref;
|
|
6448
|
-
const cnodes = level_trees[last_stack] || [];
|
|
6449
|
-
cnodes.push(bomRef);
|
|
6450
|
-
level_trees[last_stack] = cnodes;
|
|
6892
|
+
}
|
|
6893
|
+
if (!level_trees[bomRef]) {
|
|
6894
|
+
level_trees[bomRef] = [];
|
|
6895
|
+
}
|
|
6896
|
+
if (level === 0 || last_purl === "") {
|
|
6897
|
+
stack.push(bomRef);
|
|
6898
|
+
} else if (level > last_level) {
|
|
6899
|
+
const cnodes = level_trees[last_purl] || [];
|
|
6900
|
+
cnodes.push(bomRef);
|
|
6901
|
+
level_trees[last_purl] = cnodes;
|
|
6902
|
+
if (stack[stack.length - 1] !== bomRef) {
|
|
6451
6903
|
stack.push(bomRef);
|
|
6452
6904
|
}
|
|
6453
|
-
|
|
6454
|
-
|
|
6905
|
+
} else {
|
|
6906
|
+
for (let i = level; i <= last_level; i++) {
|
|
6907
|
+
stack.pop();
|
|
6908
|
+
}
|
|
6909
|
+
const last_stack = stack.length ? stack[stack.length - 1] : first_ref;
|
|
6910
|
+
const cnodes = level_trees[last_stack] || [];
|
|
6911
|
+
cnodes.push(bomRef);
|
|
6912
|
+
level_trees[last_stack] = cnodes;
|
|
6913
|
+
stack.push(bomRef);
|
|
6455
6914
|
}
|
|
6915
|
+
last_level = level;
|
|
6916
|
+
last_purl = bomRef;
|
|
6456
6917
|
}
|
|
6457
6918
|
}
|
|
6458
6919
|
});
|
|
@@ -9641,7 +10102,7 @@ export async function getPyModules(src, epkgList, options) {
|
|
|
9641
10102
|
modList = slicesData;
|
|
9642
10103
|
}
|
|
9643
10104
|
} else {
|
|
9644
|
-
modList = findAppModules(src, "python", "parsedeps", slicesFile);
|
|
10105
|
+
modList = findAppModules(src, "python", "parsedeps", slicesFile, options);
|
|
9645
10106
|
}
|
|
9646
10107
|
const pyDefaultModules = new Set(PYTHON_STD_MODULES);
|
|
9647
10108
|
modList = modList.filter(
|
|
@@ -17064,7 +17525,7 @@ export async function collectMvnDependencies(
|
|
|
17064
17525
|
`-Dmdep.stripVersion=${process.env.MAVEN_STRIP_VERSION || "false"}`,
|
|
17065
17526
|
];
|
|
17066
17527
|
if (process.env.MVN_ARGS) {
|
|
17067
|
-
const addArgs = process.env.MVN_ARGS
|
|
17528
|
+
const addArgs = parseMavenArgs(process.env.MVN_ARGS);
|
|
17068
17529
|
copyArgs = copyArgs.concat(addArgs);
|
|
17069
17530
|
}
|
|
17070
17531
|
if (basePath && basePath !== MAVEN_CACHE_DIR) {
|
|
@@ -17548,11 +18009,11 @@ export function parsePomProperties(pomProperties) {
|
|
|
17548
18009
|
return properties;
|
|
17549
18010
|
}
|
|
17550
18011
|
pomProperties.split("\n").forEach((l) => {
|
|
17551
|
-
l = l.
|
|
18012
|
+
l = l.replaceAll("\r", "");
|
|
17552
18013
|
if (l.includes("=")) {
|
|
17553
|
-
const
|
|
17554
|
-
if (
|
|
17555
|
-
properties[
|
|
18014
|
+
const separatorIndex = l.indexOf("=");
|
|
18015
|
+
if (separatorIndex !== -1) {
|
|
18016
|
+
properties[l.slice(0, separatorIndex)] = l.slice(separatorIndex + 1);
|
|
17556
18017
|
}
|
|
17557
18018
|
}
|
|
17558
18019
|
});
|
|
@@ -19224,11 +19685,9 @@ export function getMavenCommand(srcPath, rootPath) {
|
|
|
19224
19685
|
}
|
|
19225
19686
|
if (isWrapperFound) {
|
|
19226
19687
|
if (DEBUG_MODE) {
|
|
19227
|
-
console.log(
|
|
19228
|
-
"Testing the wrapper script by invoking wrapper:wrapper task",
|
|
19229
|
-
);
|
|
19688
|
+
console.log("Testing the wrapper script by invoking --version");
|
|
19230
19689
|
}
|
|
19231
|
-
const result = safeSpawnSync(mavenWrapperCmd, ["
|
|
19690
|
+
const result = safeSpawnSync(mavenWrapperCmd, ["--version"], {
|
|
19232
19691
|
cdxgenActivity: {
|
|
19233
19692
|
kind: "probe",
|
|
19234
19693
|
metadata: {
|
|
@@ -19449,6 +19908,7 @@ export function executeAtom(src, args, extra_env = {}) {
|
|
|
19449
19908
|
* @param {string} language
|
|
19450
19909
|
* @param {string} methodology
|
|
19451
19910
|
* @param {string} slicesFile
|
|
19911
|
+
* @param {Object} options CLI options
|
|
19452
19912
|
* @returns List of imported modules
|
|
19453
19913
|
*/
|
|
19454
19914
|
export function findAppModules(
|
|
@@ -19456,6 +19916,7 @@ export function findAppModules(
|
|
|
19456
19916
|
language,
|
|
19457
19917
|
methodology = "usages",
|
|
19458
19918
|
slicesFile = undefined,
|
|
19919
|
+
options = {},
|
|
19459
19920
|
) {
|
|
19460
19921
|
const tempDir = safeMkdtempSync(join(tmpdir(), "atom-deps-"));
|
|
19461
19922
|
const atomFile = join(tempDir, `${language}-app.atom`);
|
|
@@ -19473,7 +19934,7 @@ export function findAppModules(
|
|
|
19473
19934
|
resolve(slicesFile),
|
|
19474
19935
|
resolve(src),
|
|
19475
19936
|
];
|
|
19476
|
-
executeAtom(src, args);
|
|
19937
|
+
executeAtom(src, args, buildAtomCommandEnv(options, language));
|
|
19477
19938
|
if (safeExistsSync(slicesFile)) {
|
|
19478
19939
|
const slicesData = JSON.parse(readFileSync(slicesFile, "utf-8"), {
|
|
19479
19940
|
encoding: "utf-8",
|
|
@@ -21313,6 +21774,7 @@ export function getCppModules(src, options, osPkgsList, epkgList) {
|
|
|
21313
21774
|
options.deep ? "c" : "h",
|
|
21314
21775
|
"usages",
|
|
21315
21776
|
options.usagesSlicesFile,
|
|
21777
|
+
options,
|
|
21316
21778
|
);
|
|
21317
21779
|
}
|
|
21318
21780
|
const usageData = parseCUsageSlice(sliceData);
|