@cyclonedx/cdxgen 12.4.3 → 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.
Files changed (38) hide show
  1. package/README.md +6 -0
  2. package/bin/audit.js +7 -0
  3. package/bin/cdxgen.js +48 -2
  4. package/bin/evinse.js +7 -0
  5. package/lib/audit/index.js +165 -2
  6. package/lib/audit/index.poku.js +462 -0
  7. package/lib/cli/index.js +317 -169
  8. package/lib/evinser/evinser.js +31 -9
  9. package/lib/helpers/analyzer.js +890 -0
  10. package/lib/helpers/analyzer.poku.js +341 -0
  11. package/lib/helpers/atomUtils.js +445 -0
  12. package/lib/helpers/atomUtils.poku.js +137 -0
  13. package/lib/helpers/bomUtils.js +71 -0
  14. package/lib/helpers/bomUtils.poku.js +45 -0
  15. package/lib/helpers/depsUtils.js +146 -0
  16. package/lib/helpers/depsUtils.poku.js +183 -0
  17. package/lib/helpers/utils.js +585 -191
  18. package/lib/helpers/utils.poku.js +357 -4
  19. package/lib/managers/binary.js +18 -9
  20. package/lib/stages/postgen/postgen.js +215 -0
  21. package/lib/stages/postgen/postgen.poku.js +218 -3
  22. package/lib/validator/bomValidator.js +11 -2
  23. package/package.json +8 -8
  24. package/types/lib/audit/index.d.ts.map +1 -1
  25. package/types/lib/cli/index.d.ts.map +1 -1
  26. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  27. package/types/lib/helpers/atomUtils.d.ts +18 -0
  28. package/types/lib/helpers/atomUtils.d.ts.map +1 -0
  29. package/types/lib/helpers/bomUtils.d.ts +10 -0
  30. package/types/lib/helpers/bomUtils.d.ts.map +1 -1
  31. package/types/lib/helpers/depsUtils.d.ts +9 -0
  32. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  33. package/types/lib/helpers/utils.d.ts +19 -0
  34. package/types/lib/helpers/utils.d.ts.map +1 -1
  35. package/types/lib/managers/binary.d.ts +2 -1
  36. package/types/lib/managers/binary.d.ts.map +1 -1
  37. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  38. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -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 {
@@ -1560,6 +1561,55 @@ export const PREFER_MAVEN_DEPS_TREE = !["false", "0"].includes(
1560
1561
  process.env?.PREFER_MAVEN_DEPS_TREE,
1561
1562
  );
1562
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
+
1563
1613
  /**
1564
1614
  * Determines whether license information should be fetched from remote sources,
1565
1615
  * based on the FETCH_LICENSE environment variable.
@@ -3587,6 +3637,12 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
3587
3637
  if (isDevelopmentNode) {
3588
3638
  _setNpmDevelopmentProperty(pkg);
3589
3639
  }
3640
+ if (node.optional === true) {
3641
+ _setNpmOptionalProperty(pkg);
3642
+ }
3643
+ if (node.peer === true) {
3644
+ _setNpmPeerProperty(pkg);
3645
+ }
3590
3646
  if (node.resolved) {
3591
3647
  if (node.resolved.startsWith("file:")) {
3592
3648
  pkg.properties.push({
@@ -4169,6 +4225,177 @@ function _parseYarnLine(l) {
4169
4225
  return { group, name };
4170
4226
  }
4171
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
+
4172
4399
  /**
4173
4400
  * Parse nodejs yarn lock file
4174
4401
  *
@@ -4190,6 +4417,8 @@ export async function parseYarnLock(
4190
4417
  let pkgList = [];
4191
4418
  const dependenciesList = [];
4192
4419
  const depKeys = {};
4420
+ let yarnRootRefs = _createYarnRootDependencyRefs();
4421
+ const yarnOptionalDependencyRefs = new Set();
4193
4422
  if (safeExistsSync(yarnLockFile)) {
4194
4423
  const lockData = readFileSync(yarnLockFile, "utf8");
4195
4424
  let name = "";
@@ -4206,6 +4435,11 @@ export async function parseYarnLock(
4206
4435
  const workspacePurlMap = {};
4207
4436
  // This would have the keys and the resolved version required to solve the dependency tree
4208
4437
  const identMap = yarnLockToIdentMap(lockData);
4438
+ yarnRootRefs = _collectYarnRootDependencyRefs(
4439
+ yarnLockFile,
4440
+ identMap,
4441
+ workspacePackages,
4442
+ );
4209
4443
  lockData.split("\n").forEach((l) => {
4210
4444
  l = l.replace("\r", "");
4211
4445
  if (l.startsWith("#")) {
@@ -4403,66 +4637,19 @@ export async function parseYarnLock(
4403
4637
  if (dgroupname.endsWith(":")) {
4404
4638
  dgroupname = dgroupname.substring(0, dgroupname.length - 1);
4405
4639
  }
4406
- let dgroupnameToUse = dgroupname;
4407
4640
  const range = tmpA[1].replace(/["']/g, "");
4408
- let versionRange = range;
4409
- // Deal with range with npm: prefix such as npm:string-width@^4.2.0, npm:@types/ioredis@^4.28.10
4410
- if (range.startsWith("npm:")) {
4411
- if (range.includes("@")) {
4412
- // Case like npm:string-width@^4.2.0 or npm:@types/ioredis@^4.28.10
4413
- versionRange = range.split("@").splice(-1)[0];
4414
- dgroupnameToUse = range
4415
- .replace("npm:", "")
4416
- .replace(`@${versionRange}`, "");
4417
- } else {
4418
- // Case like npm:^5.1.1 - just a version range with npm: prefix
4419
- versionRange = range.replace("npm:", "");
4420
- dgroupnameToUse = dgroupname;
4421
- }
4422
- }
4423
- let resolvedVersion =
4424
- identMap[`${dgroupname}|${versionRange}`] ||
4425
- identMap[`${dgroupnameToUse}|${versionRange}`];
4426
- // If we couldn't resolve the version directly, try to find any resolved version for this package
4427
- if (!resolvedVersion) {
4428
- const packageKeys = Object.keys(identMap).filter((key) => {
4429
- const [pkg] = key.split("|");
4430
- return pkg === dgroupname || pkg === dgroupnameToUse;
4431
- });
4432
-
4433
- if (packageKeys.length > 0) {
4434
- // Use the first available resolved version for this package
4435
- resolvedVersion = identMap[packageKeys[0]];
4436
- }
4437
- }
4438
- // If we couldn't resolve the version from yarn.lock, check if it's a workspace package
4439
- if (!resolvedVersion && workspacePackages?.length) {
4440
- // Use helper function for more efficient workspace matching
4441
- const matchingWorkspace = findMatchingWorkspace(
4442
- workspacePackages,
4443
- dgroupnameToUse,
4444
- );
4445
-
4446
- if (matchingWorkspace) {
4447
- // Extract version from workspace PURL like "npm:@swc/helpers@0.4.14"
4448
- const versionMatch = matchingWorkspace.match(/@([^@]+)$/);
4449
- if (versionMatch) {
4450
- resolvedVersion = versionMatch[1];
4451
- }
4452
- }
4453
- }
4454
- // If we still can't resolve the version, use "" as fallback
4455
- if (!resolvedVersion) {
4456
- resolvedVersion = "";
4641
+ const depBomRef = _resolveYarnDependencyBomRef(
4642
+ dgroupname,
4643
+ range,
4644
+ identMap,
4645
+ workspacePackages,
4646
+ );
4647
+ if (depBomRef) {
4648
+ deplist.add(depBomRef);
4457
4649
  }
4458
- // Handle case where the dependency name is really an alias.
4459
- // Eg: legacy-swc-helpers "npm:@swc/helpers@=0.4.14". Here the dgroupname=@swc/helpers
4460
- let purlPart = `pkg:npm/${encodeURIComponent(dgroupnameToUse).replace(/%2F/g, "/")}`;
4461
- if (resolvedVersion?.length) {
4462
- purlPart = `${purlPart}@${resolvedVersion}`;
4650
+ if (optionalDepsMode && depBomRef) {
4651
+ yarnOptionalDependencyRefs.add(depBomRef);
4463
4652
  }
4464
- const depPurlString = PackageURL.fromString(purlPart).toString();
4465
- deplist.add(decodeURIComponent(depPurlString));
4466
4653
  }
4467
4654
  } else if (name !== "") {
4468
4655
  if (!l.startsWith(" ")) {
@@ -4672,6 +4859,13 @@ export async function parseYarnLock(
4672
4859
  }
4673
4860
  }
4674
4861
 
4862
+ _markYarnDependencyScopeClosures(
4863
+ pkgList,
4864
+ dependenciesList,
4865
+ yarnRootRefs,
4866
+ yarnOptionalDependencyRefs,
4867
+ );
4868
+
4675
4869
  if (shouldFetchPackageMetadata() && pkgList?.length) {
4676
4870
  if (DEBUG_MODE) {
4677
4871
  console.log(
@@ -4830,6 +5024,14 @@ function _setNpmDevelopmentProperty(pkg) {
4830
5024
  }
4831
5025
  }
4832
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
+
4833
5035
  function _setTreeWorkspaceRef(
4834
5036
  dependenciesMap,
4835
5037
  depref,
@@ -4931,11 +5133,25 @@ export function parsePnpmWorkspace(workspaceFile) {
4931
5133
  if (!yamlObj) {
4932
5134
  return {};
4933
5135
  }
4934
- const packages = (yamlObj.packages || [])
4935
- .filter((n) => !/^(!|\.|__)/.test(n))
4936
- .map((n) => n.replaceAll("/**", "").replaceAll("/*", ""));
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
+ );
4937
5151
  const catalogs = yamlObj.catalog || {};
4938
5152
  return {
5153
+ excludePackages,
5154
+ packagePatterns,
4939
5155
  packages,
4940
5156
  catalogs,
4941
5157
  };
@@ -5058,12 +5274,20 @@ export function findPnpmPackagePath(baseDir, packageName, version) {
5058
5274
  if (safeExistsSync(pnpmDir)) {
5059
5275
  // pnpm stores packages as {name}@{version} in .pnpm directory
5060
5276
  const encodedName = packageName.replace("/", "%2f");
5277
+ const virtualStoreName = packageName.replace("/", "+");
5061
5278
  let pnpmPackagePath;
5062
5279
 
5063
5280
  // Try different formats that pnpm might use
5064
5281
  const possiblePaths = [
5282
+ join(
5283
+ pnpmDir,
5284
+ `${virtualStoreName}@${version}`,
5285
+ "node_modules",
5286
+ packageName,
5287
+ ),
5065
5288
  join(pnpmDir, `${encodedName}@${version}`, "node_modules", packageName),
5066
5289
  join(pnpmDir, `${packageName}@${version}`, "node_modules", packageName),
5290
+ join(pnpmDir, `${virtualStoreName}@${version}`),
5067
5291
  join(pnpmDir, `${encodedName}@${version}`),
5068
5292
  join(pnpmDir, `${packageName}@${version}`),
5069
5293
  ];
@@ -5109,7 +5333,8 @@ export async function pnpmMetadata(pkgList, lockFilePath) {
5109
5333
  }
5110
5334
  let enhancedCount = 0;
5111
5335
  for (const pkg of pkgList) {
5112
- const packagePath = findPnpmPackagePath(baseDir, pkg.name, pkg.version);
5336
+ const packageName = pkg.group ? `${pkg.group}/${pkg.name}` : pkg.name;
5337
+ const packagePath = findPnpmPackagePath(baseDir, packageName, pkg.version);
5113
5338
  if (!packagePath) {
5114
5339
  continue;
5115
5340
  }
@@ -5186,9 +5411,10 @@ export async function pnpmMetadata(pkgList, lockFilePath) {
5186
5411
  * @param {Object} parentComponent parent component
5187
5412
  * @param {Array[String]} workspacePackages Workspace packages
5188
5413
  * @param {Object} workspaceSrcFiles Workspace package.json files
5189
- * @param {Object} workspaceCatalogs Workspace catalogs
5190
- * @param {Object} workspaceDirectDeps Direct dependencies of each workspace
5414
+ * @param {Object} _workspaceCatalogs Workspace catalogs
5415
+ * @param {Object} _workspaceDirectDeps Direct dependencies of each workspace
5191
5416
  * @param {Object} depsWorkspaceRefs Workspace references for each dependency
5417
+ * @param {string} projectRoot Root path used to relativize pnpm-lock evidence paths
5192
5418
  */
5193
5419
  export async function parsePnpmLock(
5194
5420
  pnpmLock,
@@ -5198,9 +5424,15 @@ export async function parsePnpmLock(
5198
5424
  _workspaceCatalogs = {},
5199
5425
  _workspaceDirectDeps = {},
5200
5426
  depsWorkspaceRefs = {},
5427
+ projectRoot = undefined,
5201
5428
  ) {
5202
5429
  let pkgList = [];
5203
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;
5204
5436
  // For lockfile >= 9, we need to track dev and optional packages manually
5205
5437
  // See: #1163
5206
5438
  // Moreover, we have changed >= 9 for >= 6
@@ -5302,7 +5534,9 @@ export async function parsePnpmLock(
5302
5534
  }
5303
5535
  // Find the root optional and peer dependencies
5304
5536
  for (const rdk of Object.keys({ ...rootOptionalDeps, ...rootPeerDeps })) {
5305
- const version = await getVersionNumPnpm(rootOptionalDeps[rdk]);
5537
+ const version = await getVersionNumPnpm(
5538
+ rootOptionalDeps[rdk] || rootPeerDeps[rdk],
5539
+ );
5306
5540
  let specifier;
5307
5541
  if (
5308
5542
  typeof rootOptionalDeps[rdk] === "object" &&
@@ -5371,8 +5605,12 @@ export async function parsePnpmLock(
5371
5605
  let compPurl;
5372
5606
  let pkgSrcFile;
5373
5607
  let fallbackMode = true;
5374
- if (safeExistsSync(join(importedComponentName, "package.json"))) {
5375
- pkgSrcFile = join(importedComponentName, "package.json");
5608
+ if (
5609
+ safeExistsSync(
5610
+ join(pnpmLockDir, importedComponentName, "package.json"),
5611
+ )
5612
+ ) {
5613
+ pkgSrcFile = join(pnpmLockDir, importedComponentName, "package.json");
5376
5614
  const importedComponentObj = await parsePkgJson(pkgSrcFile, true);
5377
5615
  if (importedComponentObj.length) {
5378
5616
  const version = importedComponentObj[0].version;
@@ -5504,7 +5742,9 @@ export async function parsePnpmLock(
5504
5742
  ...componentOptionalDeps,
5505
5743
  ...componentPeerDeps,
5506
5744
  })) {
5507
- const version = await getVersionNumPnpm(componentOptionalDeps[cdk]);
5745
+ const version = await getVersionNumPnpm(
5746
+ componentOptionalDeps[cdk] || componentPeerDeps[cdk],
5747
+ );
5508
5748
  const dpurl = new PackageURL(
5509
5749
  "npm",
5510
5750
  "",
@@ -5795,7 +6035,7 @@ export async function parsePnpmLock(
5795
6035
  const properties = [
5796
6036
  {
5797
6037
  name: "SrcFile",
5798
- value: pnpmLock,
6038
+ value: pnpmLockSrcFile,
5799
6039
  },
5800
6040
  ];
5801
6041
  if (hasBin) {
@@ -5906,7 +6146,7 @@ export async function parsePnpmLock(
5906
6146
  {
5907
6147
  technique: "manifest-analysis",
5908
6148
  confidence: 1,
5909
- value: pnpmLock,
6149
+ value: pnpmLockSrcFile,
5910
6150
  },
5911
6151
  ],
5912
6152
  },
@@ -5920,6 +6160,9 @@ export async function parsePnpmLock(
5920
6160
  },
5921
6161
  ];
5922
6162
  }
6163
+ if (possibleOptionalDeps[thePkg["bom-ref"]]) {
6164
+ _setNpmOptionalProperty(thePkg);
6165
+ }
5923
6166
  // Don't add internal workspace packages to the components list
5924
6167
  if (thePkg.type !== "application") {
5925
6168
  pkgList.push(thePkg);
@@ -6258,6 +6501,15 @@ export function parsePom(pomFile) {
6258
6501
  attributesKey: "$",
6259
6502
  commentKey: "value",
6260
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
+ }
6261
6513
  for (const aprop of [
6262
6514
  "groupId",
6263
6515
  "artifactId",
@@ -6292,14 +6544,20 @@ export function parsePom(pomFile) {
6292
6544
  null,
6293
6545
  ).toString();
6294
6546
  }
6547
+ const moduleEntries = [];
6295
6548
  if (project?.modules?.module) {
6296
- if (Array.isArray(project.modules.module)) {
6297
- // If it's an array, proceed with mapping
6298
- modules = project.modules.module.map((m) => m?._);
6299
- } else {
6300
- // If not an array, handle/convert it accordingly. For instance:
6301
- modules = [project.modules.module._];
6302
- }
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
+ });
6303
6561
  }
6304
6562
  if (project?.properties) {
6305
6563
  for (const aprop of Object.keys(project.properties)) {
@@ -6344,18 +6602,28 @@ export function parsePom(pomFile) {
6344
6602
  if (versionStr?.includes("$")) {
6345
6603
  versionStr = properties[versionStr?.replace(/[${}]/g, "")];
6346
6604
  }
6347
- if (includeMavenTestScope || !adep.scope || adep.scope !== "test") {
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") {
6348
6620
  deps.push({
6349
6621
  group: adep.groupId ? adep.groupId._ : "",
6350
6622
  name: adep.artifactId ? adep.artifactId._ : "",
6351
6623
  version: versionStr,
6352
- qualifiers: { type: "jar" },
6353
- properties: [
6354
- {
6355
- name: "SrcFile",
6356
- value: pomFile,
6357
- },
6358
- ],
6624
+ qualifiers,
6625
+ scope: mapMavenScope(scope, optional === "true"),
6626
+ properties: dependencyProperties,
6359
6627
  evidence: {
6360
6628
  identity: {
6361
6629
  field: "purl",
@@ -6376,6 +6644,194 @@ export function parsePom(pomFile) {
6376
6644
  return { isQuarkus, pomPurl, modules, properties, dependencies: deps };
6377
6645
  }
6378
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
+
6379
6835
  /**
6380
6836
  * Parse maven tree output
6381
6837
  * @param {string} rawOutput Raw string output
@@ -6387,6 +6843,9 @@ export function parseMavenTree(rawOutput, pomFile) {
6387
6843
  if (!rawOutput) {
6388
6844
  return {};
6389
6845
  }
6846
+ if (rawOutput.trim().startsWith("{")) {
6847
+ return parseMavenTreeJson(rawOutput, pomFile);
6848
+ }
6390
6849
  const deps = [];
6391
6850
  const dependenciesList = [];
6392
6851
  const keys_cache = {};
@@ -6398,6 +6857,8 @@ export function parseMavenTree(rawOutput, pomFile) {
6398
6857
  const stack = [];
6399
6858
  tmpA.forEach((l) => {
6400
6859
  l = l.replace("\r", "");
6860
+ const isOptional = /\s+\(optional\)\s*$/.test(l);
6861
+ l = l.replace(/\s+\(optional\)\s*$/, "");
6401
6862
  if (!includeMavenTestScope && l.trim().endsWith(":test")) {
6402
6863
  return;
6403
6864
  }
@@ -6409,118 +6870,50 @@ export function parseMavenTree(rawOutput, pomFile) {
6409
6870
  }
6410
6871
  l = tmpline[tmpline.length - 1];
6411
6872
  const pkgArr = l.split(":");
6412
- // Support for classifiers
6413
- // com.github.jnr:jffi:jar:1.3.11:compile
6414
- // com.github.jnr:jffi:jar:native:1.3.11:runtime
6415
- let classifier;
6416
6873
  if (pkgArr && pkgArr.length > 2) {
6417
- let versionStr = pkgArr[pkgArr.length - 2];
6418
6874
  const componentScope = pkgArr[pkgArr.length - 1];
6419
- if (
6420
- pkgArr.length >= 6 &&
6421
- pkgArr[3] !== versionStr &&
6422
- !pkgArr[3].includes(".jar")
6423
- ) {
6424
- classifier = pkgArr[3];
6425
- }
6426
6875
  // Ignore test scope
6427
6876
  if (!includeMavenTestScope && componentScope === "test") {
6428
6877
  return;
6429
6878
  }
6430
- let scope;
6431
- if (["compile", "runtime"].includes(componentScope)) {
6432
- scope = "required";
6433
- } else if (componentScope === "test") {
6434
- scope = "optional";
6435
- } else if (componentScope === "provided") {
6436
- scope = "excluded";
6437
- }
6438
- if (pkgArr.length === 4) {
6439
- versionStr = pkgArr[pkgArr.length - 1];
6440
- }
6441
- const qualifiers = { type: pkgArr[2] };
6442
- if (classifier) {
6443
- qualifiers.classifier = classifier;
6444
- }
6445
- const purlString = new PackageURL(
6446
- "maven",
6447
- pkgArr[0],
6448
- pkgArr[1],
6449
- versionStr,
6450
- qualifiers,
6451
- null,
6452
- ).toString();
6453
- const bomRef = decodeURIComponent(purlString);
6879
+ const apkg = createMavenComponentFromCoordinateParts(
6880
+ pkgArr,
6881
+ pomFile,
6882
+ isOptional,
6883
+ );
6884
+ const bomRef = apkg["bom-ref"];
6454
6885
  const key = bomRef;
6455
6886
  if (!first_ref) {
6456
6887
  first_ref = bomRef;
6457
6888
  }
6458
6889
  if (!keys_cache[key]) {
6459
6890
  keys_cache[key] = key;
6460
- const properties = [];
6461
- if (scope) {
6462
- properties.push({
6463
- name: "cdx:maven:component_scope",
6464
- value: componentScope,
6465
- });
6466
- }
6467
- const apkg = {
6468
- group: pkgArr[0],
6469
- name: pkgArr[1],
6470
- version: versionStr,
6471
- qualifiers,
6472
- scope,
6473
- properties,
6474
- purl: purlString,
6475
- "bom-ref": bomRef,
6476
- };
6477
- if (pomFile) {
6478
- properties.push({
6479
- name: "SrcFile",
6480
- value: pomFile,
6481
- });
6482
- apkg.evidence = {
6483
- identity: {
6484
- field: "purl",
6485
- confidence: 0.5,
6486
- methods: [
6487
- {
6488
- technique: "manifest-analysis",
6489
- confidence: 0.5,
6490
- value: pomFile,
6491
- },
6492
- ],
6493
- },
6494
- };
6495
- }
6496
6891
  deps.push(apkg);
6497
- if (!level_trees[bomRef]) {
6498
- level_trees[bomRef] = [];
6499
- }
6500
- if (level === 0 || last_purl === "") {
6501
- stack.push(bomRef);
6502
- } else if (level > last_level) {
6503
- const cnodes = level_trees[last_purl] || [];
6504
- cnodes.push(bomRef);
6505
- level_trees[last_purl] = cnodes;
6506
- if (stack[stack.length - 1] !== bomRef) {
6507
- stack.push(bomRef);
6508
- }
6509
- } else {
6510
- for (let i = level; i <= last_level; i++) {
6511
- stack.pop();
6512
- }
6513
- const last_stack = stack.length
6514
- ? stack[stack.length - 1]
6515
- : first_ref;
6516
- const cnodes = level_trees[last_stack] || [];
6517
- cnodes.push(bomRef);
6518
- 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) {
6519
6903
  stack.push(bomRef);
6520
6904
  }
6521
- last_level = level;
6522
- last_purl = bomRef;
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);
6523
6914
  }
6915
+ last_level = level;
6916
+ last_purl = bomRef;
6524
6917
  }
6525
6918
  }
6526
6919
  });
@@ -9709,7 +10102,7 @@ export async function getPyModules(src, epkgList, options) {
9709
10102
  modList = slicesData;
9710
10103
  }
9711
10104
  } else {
9712
- modList = findAppModules(src, "python", "parsedeps", slicesFile);
10105
+ modList = findAppModules(src, "python", "parsedeps", slicesFile, options);
9713
10106
  }
9714
10107
  const pyDefaultModules = new Set(PYTHON_STD_MODULES);
9715
10108
  modList = modList.filter(
@@ -17132,7 +17525,7 @@ export async function collectMvnDependencies(
17132
17525
  `-Dmdep.stripVersion=${process.env.MAVEN_STRIP_VERSION || "false"}`,
17133
17526
  ];
17134
17527
  if (process.env.MVN_ARGS) {
17135
- const addArgs = process.env.MVN_ARGS.split(" ");
17528
+ const addArgs = parseMavenArgs(process.env.MVN_ARGS);
17136
17529
  copyArgs = copyArgs.concat(addArgs);
17137
17530
  }
17138
17531
  if (basePath && basePath !== MAVEN_CACHE_DIR) {
@@ -17616,11 +18009,11 @@ export function parsePomProperties(pomProperties) {
17616
18009
  return properties;
17617
18010
  }
17618
18011
  pomProperties.split("\n").forEach((l) => {
17619
- l = l.replace("\r", "");
18012
+ l = l.replaceAll("\r", "");
17620
18013
  if (l.includes("=")) {
17621
- const tmpA = l.split("=");
17622
- if (tmpA && tmpA.length === 2) {
17623
- properties[tmpA[0]] = tmpA[1].replace("\r", "");
18014
+ const separatorIndex = l.indexOf("=");
18015
+ if (separatorIndex !== -1) {
18016
+ properties[l.slice(0, separatorIndex)] = l.slice(separatorIndex + 1);
17624
18017
  }
17625
18018
  }
17626
18019
  });
@@ -19292,11 +19685,9 @@ export function getMavenCommand(srcPath, rootPath) {
19292
19685
  }
19293
19686
  if (isWrapperFound) {
19294
19687
  if (DEBUG_MODE) {
19295
- console.log(
19296
- "Testing the wrapper script by invoking wrapper:wrapper task",
19297
- );
19688
+ console.log("Testing the wrapper script by invoking --version");
19298
19689
  }
19299
- const result = safeSpawnSync(mavenWrapperCmd, ["wrapper:wrapper"], {
19690
+ const result = safeSpawnSync(mavenWrapperCmd, ["--version"], {
19300
19691
  cdxgenActivity: {
19301
19692
  kind: "probe",
19302
19693
  metadata: {
@@ -19517,6 +19908,7 @@ export function executeAtom(src, args, extra_env = {}) {
19517
19908
  * @param {string} language
19518
19909
  * @param {string} methodology
19519
19910
  * @param {string} slicesFile
19911
+ * @param {Object} options CLI options
19520
19912
  * @returns List of imported modules
19521
19913
  */
19522
19914
  export function findAppModules(
@@ -19524,6 +19916,7 @@ export function findAppModules(
19524
19916
  language,
19525
19917
  methodology = "usages",
19526
19918
  slicesFile = undefined,
19919
+ options = {},
19527
19920
  ) {
19528
19921
  const tempDir = safeMkdtempSync(join(tmpdir(), "atom-deps-"));
19529
19922
  const atomFile = join(tempDir, `${language}-app.atom`);
@@ -19541,7 +19934,7 @@ export function findAppModules(
19541
19934
  resolve(slicesFile),
19542
19935
  resolve(src),
19543
19936
  ];
19544
- executeAtom(src, args);
19937
+ executeAtom(src, args, buildAtomCommandEnv(options, language));
19545
19938
  if (safeExistsSync(slicesFile)) {
19546
19939
  const slicesData = JSON.parse(readFileSync(slicesFile, "utf-8"), {
19547
19940
  encoding: "utf-8",
@@ -21381,6 +21774,7 @@ export function getCppModules(src, options, osPkgsList, epkgList) {
21381
21774
  options.deep ? "c" : "h",
21382
21775
  "usages",
21383
21776
  options.usagesSlicesFile,
21777
+ options,
21384
21778
  );
21385
21779
  }
21386
21780
  const usageData = parseCUsageSlice(sliceData);