@cyclonedx/cdxgen 12.2.0 → 12.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +242 -90
  2. package/bin/audit.js +191 -0
  3. package/bin/cdxgen.js +532 -168
  4. package/bin/convert.js +99 -0
  5. package/bin/evinse.js +23 -0
  6. package/bin/repl.js +339 -8
  7. package/bin/sign.js +8 -0
  8. package/bin/validate.js +8 -0
  9. package/bin/verify.js +8 -0
  10. package/data/container-knowledge-index.json +125 -0
  11. package/data/gtfobins-index.json +6296 -0
  12. package/data/lolbas-index.json +150 -0
  13. package/data/queries-darwin.json +63 -3
  14. package/data/queries-win.json +45 -3
  15. package/data/queries.json +74 -2
  16. package/data/rules/chrome-extensions.yaml +240 -0
  17. package/data/rules/ci-permissions.yaml +478 -18
  18. package/data/rules/container-risk.yaml +270 -0
  19. package/data/rules/obom-runtime.yaml +891 -0
  20. package/data/rules/package-integrity.yaml +49 -0
  21. package/data/spdx-export.schema.json +6794 -0
  22. package/data/spdx-model-v3.0.1.jsonld +15999 -0
  23. package/lib/audit/index.js +1924 -0
  24. package/lib/audit/index.poku.js +1488 -0
  25. package/lib/audit/progress.js +137 -0
  26. package/lib/audit/progress.poku.js +188 -0
  27. package/lib/audit/reporters.js +618 -0
  28. package/lib/audit/scoring.js +310 -0
  29. package/lib/audit/scoring.poku.js +341 -0
  30. package/lib/audit/targets.js +260 -0
  31. package/lib/audit/targets.poku.js +331 -0
  32. package/lib/cli/index.js +276 -68
  33. package/lib/cli/index.poku.js +368 -0
  34. package/lib/helpers/analyzer.js +1052 -5
  35. package/lib/helpers/analyzer.poku.js +301 -0
  36. package/lib/helpers/annotationFormatter.js +49 -0
  37. package/lib/helpers/annotationFormatter.poku.js +44 -0
  38. package/lib/helpers/bomUtils.js +36 -0
  39. package/lib/helpers/bomUtils.poku.js +51 -0
  40. package/lib/helpers/caxa.js +2 -2
  41. package/lib/helpers/chromextutils.js +1153 -0
  42. package/lib/helpers/chromextutils.poku.js +493 -0
  43. package/lib/helpers/ciParsers/githubActions.js +1632 -45
  44. package/lib/helpers/ciParsers/githubActions.poku.js +853 -1
  45. package/lib/helpers/containerRisk.js +186 -0
  46. package/lib/helpers/containerRisk.poku.js +52 -0
  47. package/lib/helpers/depsUtils.js +16 -0
  48. package/lib/helpers/depsUtils.poku.js +58 -1
  49. package/lib/helpers/display.js +245 -61
  50. package/lib/helpers/display.poku.js +162 -2
  51. package/lib/helpers/exportUtils.js +123 -0
  52. package/lib/helpers/exportUtils.poku.js +60 -0
  53. package/lib/helpers/formulationParsers.js +69 -0
  54. package/lib/helpers/formulationParsers.poku.js +44 -0
  55. package/lib/helpers/gtfobins.js +189 -0
  56. package/lib/helpers/gtfobins.poku.js +49 -0
  57. package/lib/helpers/lolbas.js +267 -0
  58. package/lib/helpers/lolbas.poku.js +39 -0
  59. package/lib/helpers/osqueryTransform.js +84 -0
  60. package/lib/helpers/osqueryTransform.poku.js +49 -0
  61. package/lib/helpers/provenanceUtils.js +193 -0
  62. package/lib/helpers/provenanceUtils.poku.js +145 -0
  63. package/lib/helpers/pylockutils.js +281 -0
  64. package/lib/helpers/pylockutils.poku.js +48 -0
  65. package/lib/helpers/registryProvenance.js +793 -0
  66. package/lib/helpers/registryProvenance.poku.js +452 -0
  67. package/lib/helpers/remote/dependency-track.js +84 -0
  68. package/lib/helpers/remote/dependency-track.poku.js +119 -0
  69. package/lib/helpers/source.js +1267 -0
  70. package/lib/helpers/source.poku.js +771 -0
  71. package/lib/helpers/spdxUtils.js +97 -0
  72. package/lib/helpers/spdxUtils.poku.js +70 -0
  73. package/lib/helpers/table.js +384 -0
  74. package/lib/helpers/table.poku.js +186 -0
  75. package/lib/helpers/unicodeScan.js +147 -0
  76. package/lib/helpers/unicodeScan.poku.js +45 -0
  77. package/lib/helpers/utils.js +882 -136
  78. package/lib/helpers/utils.poku.js +995 -91
  79. package/lib/managers/binary.js +29 -5
  80. package/lib/managers/docker.js +179 -52
  81. package/lib/managers/docker.poku.js +327 -28
  82. package/lib/managers/oci.js +107 -23
  83. package/lib/managers/oci.poku.js +132 -0
  84. package/lib/server/openapi.yaml +50 -0
  85. package/lib/server/server.js +228 -331
  86. package/lib/server/server.poku.js +220 -5
  87. package/lib/stages/postgen/annotator.js +7 -0
  88. package/lib/stages/postgen/annotator.poku.js +40 -0
  89. package/lib/stages/postgen/auditBom.js +20 -5
  90. package/lib/stages/postgen/auditBom.poku.js +1729 -67
  91. package/lib/stages/postgen/postgen.js +40 -0
  92. package/lib/stages/postgen/postgen.poku.js +47 -0
  93. package/lib/stages/postgen/ruleEngine.js +80 -2
  94. package/lib/stages/postgen/spdxConverter.js +796 -0
  95. package/lib/stages/postgen/spdxConverter.poku.js +341 -0
  96. package/lib/validator/bomValidator.js +232 -0
  97. package/lib/validator/bomValidator.poku.js +70 -0
  98. package/lib/validator/complianceRules.js +70 -7
  99. package/lib/validator/complianceRules.poku.js +30 -0
  100. package/lib/validator/reporters/annotations.js +2 -2
  101. package/lib/validator/reporters/console.js +13 -2
  102. package/lib/validator/reporters.poku.js +13 -0
  103. package/package.json +10 -8
  104. package/types/bin/audit.d.ts +3 -0
  105. package/types/bin/audit.d.ts.map +1 -0
  106. package/types/bin/convert.d.ts +3 -0
  107. package/types/bin/convert.d.ts.map +1 -0
  108. package/types/bin/repl.d.ts.map +1 -1
  109. package/types/lib/audit/index.d.ts +115 -0
  110. package/types/lib/audit/index.d.ts.map +1 -0
  111. package/types/lib/audit/progress.d.ts +27 -0
  112. package/types/lib/audit/progress.d.ts.map +1 -0
  113. package/types/lib/audit/reporters.d.ts +35 -0
  114. package/types/lib/audit/reporters.d.ts.map +1 -0
  115. package/types/lib/audit/scoring.d.ts +35 -0
  116. package/types/lib/audit/scoring.d.ts.map +1 -0
  117. package/types/lib/audit/targets.d.ts +63 -0
  118. package/types/lib/audit/targets.d.ts.map +1 -0
  119. package/types/lib/cli/index.d.ts +8 -0
  120. package/types/lib/cli/index.d.ts.map +1 -1
  121. package/types/lib/helpers/analyzer.d.ts +13 -0
  122. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  123. package/types/lib/helpers/annotationFormatter.d.ts +23 -0
  124. package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
  125. package/types/lib/helpers/bomUtils.d.ts +5 -0
  126. package/types/lib/helpers/bomUtils.d.ts.map +1 -0
  127. package/types/lib/helpers/chromextutils.d.ts +97 -0
  128. package/types/lib/helpers/chromextutils.d.ts.map +1 -0
  129. package/types/lib/helpers/ciParsers/githubActions.d.ts +3 -8
  130. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
  131. package/types/lib/helpers/containerRisk.d.ts +17 -0
  132. package/types/lib/helpers/containerRisk.d.ts.map +1 -0
  133. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  134. package/types/lib/helpers/display.d.ts +4 -1
  135. package/types/lib/helpers/display.d.ts.map +1 -1
  136. package/types/lib/helpers/exportUtils.d.ts +40 -0
  137. package/types/lib/helpers/exportUtils.d.ts.map +1 -0
  138. package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
  139. package/types/lib/helpers/gtfobins.d.ts +17 -0
  140. package/types/lib/helpers/gtfobins.d.ts.map +1 -0
  141. package/types/lib/helpers/lolbas.d.ts +16 -0
  142. package/types/lib/helpers/lolbas.d.ts.map +1 -0
  143. package/types/lib/helpers/osqueryTransform.d.ts +7 -0
  144. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -0
  145. package/types/lib/helpers/provenanceUtils.d.ts +90 -0
  146. package/types/lib/helpers/provenanceUtils.d.ts.map +1 -0
  147. package/types/lib/helpers/pylockutils.d.ts +51 -0
  148. package/types/lib/helpers/pylockutils.d.ts.map +1 -0
  149. package/types/lib/helpers/registryProvenance.d.ts +17 -0
  150. package/types/lib/helpers/registryProvenance.d.ts.map +1 -0
  151. package/types/lib/helpers/remote/dependency-track.d.ts +16 -0
  152. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
  153. package/types/lib/helpers/source.d.ts +141 -0
  154. package/types/lib/helpers/source.d.ts.map +1 -0
  155. package/types/lib/helpers/spdxUtils.d.ts +2 -0
  156. package/types/lib/helpers/spdxUtils.d.ts.map +1 -0
  157. package/types/lib/helpers/table.d.ts +6 -0
  158. package/types/lib/helpers/table.d.ts.map +1 -0
  159. package/types/lib/helpers/unicodeScan.d.ts +46 -0
  160. package/types/lib/helpers/unicodeScan.d.ts.map +1 -0
  161. package/types/lib/helpers/utils.d.ts +30 -11
  162. package/types/lib/helpers/utils.d.ts.map +1 -1
  163. package/types/lib/managers/binary.d.ts.map +1 -1
  164. package/types/lib/managers/docker.d.ts.map +1 -1
  165. package/types/lib/managers/oci.d.ts.map +1 -1
  166. package/types/lib/server/server.d.ts +0 -35
  167. package/types/lib/server/server.d.ts.map +1 -1
  168. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  169. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  170. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  171. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  172. package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
  173. package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
  174. package/types/lib/validator/bomValidator.d.ts +1 -0
  175. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  176. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  177. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  178. package/types/bin/dependencies.d.ts +0 -3
  179. package/types/bin/dependencies.d.ts.map +0 -1
  180. package/types/bin/licenses.d.ts +0 -3
  181. package/types/bin/licenses.d.ts.map +0 -1
package/lib/cli/index.js CHANGED
@@ -19,15 +19,26 @@ import got from "got";
19
19
  import { PackageURL } from "packageurl-js";
20
20
  import { gte, lte } from "semver";
21
21
  import { parse } from "ssri";
22
- import { table } from "table";
23
22
  import { v4 as uuidv4 } from "uuid";
24
23
  import { parse as loadYaml } from "yaml";
25
24
 
26
25
  import { findJSImportsExports } from "../helpers/analyzer.js";
27
26
  import { parseCaxaMetadata } from "../helpers/caxa.js";
27
+ import {
28
+ CHROME_EXTENSION_PURL_TYPE,
29
+ collectChromeExtensionsFromPath,
30
+ collectInstalledChromeExtensions,
31
+ discoverChromiumExtensionDirs,
32
+ } from "../helpers/chromextutils.js";
28
33
  import { mergeDependencies, trimComponents } from "../helpers/depsUtils.js";
29
34
  import { GIT_COMMAND } from "../helpers/envcontext.js";
30
35
  import { thoughtLog } from "../helpers/logger.js";
36
+ import { isPyLockFile } from "../helpers/pylockutils.js";
37
+ import {
38
+ buildDependencyTrackBomPayload,
39
+ getDependencyTrackBomUrl,
40
+ } from "../helpers/remote/dependency-track.js";
41
+ import { table } from "../helpers/table.js";
31
42
  import {
32
43
  addEvidenceForDotnet,
33
44
  addEvidenceForImports,
@@ -1252,7 +1263,7 @@ const buildBomNSData = (options, pkgInfo, ptype, context) => {
1252
1263
  // CycloneDX Json Template
1253
1264
  const jsonTpl = {
1254
1265
  bomFormat: "CycloneDX",
1255
- specVersion: `${options.specVersion || "1.5"}`,
1266
+ specVersion: `${options.specVersion || "1.7"}`,
1256
1267
  serialNumber: serialNum,
1257
1268
  version: 1,
1258
1269
  metadata: metadata,
@@ -1490,6 +1501,8 @@ export async function createJavaBom(path, options) {
1490
1501
  }
1491
1502
  let result;
1492
1503
  let mvnArgs;
1504
+ // FIXME: How do we motivate everyone to upgrade to 1.7?
1505
+ const toolsSpecVersion = 1.6;
1493
1506
  if (isQuarkus) {
1494
1507
  thoughtLog(
1495
1508
  "This appears to be a Quarkus project. Let's use the right Maven plugin.",
@@ -1500,12 +1513,14 @@ export async function createJavaBom(path, options) {
1500
1513
  "quarkus:dependency-sbom",
1501
1514
  "-Dquarkus.analytics.disabled=true",
1502
1515
  ];
1503
- if (options.specVersion) {
1516
+ if (options.specVersion >= 1.6) {
1504
1517
  mvnArgs = mvnArgs.concat(
1505
- `-Dquarkus.dependency.sbom.schema-version=${options.specVersion}`,
1518
+ `-Dquarkus.dependency.sbom.schema-version=${toolsSpecVersion}`,
1506
1519
  );
1507
1520
  }
1508
1521
  } else {
1522
+ // FIXME: The last maven plugin release was on November 28th, 2024.
1523
+ // Should we fork this repo and maintain it ourselves?
1509
1524
  const cdxMavenPlugin =
1510
1525
  process.env.CDX_MAVEN_PLUGIN ||
1511
1526
  "org.cyclonedx:cyclonedx-maven-plugin:2.9.1";
@@ -1527,9 +1542,11 @@ export async function createJavaBom(path, options) {
1527
1542
  const addArgs = process.env.MVN_ARGS.split(" ");
1528
1543
  mvnArgs = mvnArgs.concat(addArgs);
1529
1544
  }
1530
- // specVersion 1.4 doesn't support externalReferences.type=disribution-intake
1545
+ // specVersion 1.4 doesn't support externalReferences.type=distribution-intake
1531
1546
  // so we need to run the plugin with the correct version
1532
- if (options.specVersion) {
1547
+ if (options.specVersion >= 1.6) {
1548
+ mvnArgs = mvnArgs.concat(`-DschemaVersion=${toolsSpecVersion}`);
1549
+ } else if (options.specVersion > 1.4) {
1533
1550
  mvnArgs = mvnArgs.concat(`-DschemaVersion=${options.specVersion}`);
1534
1551
  }
1535
1552
  }
@@ -2723,6 +2740,21 @@ export async function createNodejsBom(path, options) {
2723
2740
  `${options.multiProject ? "**/" : ""}bower.json`,
2724
2741
  options,
2725
2742
  );
2743
+ if (DEBUG_MODE) {
2744
+ const wasmFiles = getAllFiles(
2745
+ path,
2746
+ `${options.multiProject ? "**/" : ""}*.wasm`,
2747
+ {
2748
+ ...options,
2749
+ includeNodeModulesDir: true,
2750
+ },
2751
+ );
2752
+ if (wasmFiles?.length) {
2753
+ console.log(
2754
+ `Found ${wasmFiles.length} wasm files in this project. cdxgen will make a best attempt to identify the exports and imports from these files.`,
2755
+ );
2756
+ }
2757
+ }
2726
2758
  // Parse min js files
2727
2759
  if (minJsFiles?.length) {
2728
2760
  manifestFiles = manifestFiles.concat(minJsFiles);
@@ -2994,7 +3026,7 @@ export async function createNodejsBom(path, options) {
2994
3026
  if (!wpkgJsonFiles?.length) {
2995
3027
  if (!workspaceWarningShown) {
2996
3028
  workspaceWarningShown = true;
2997
- console.log(
3029
+ console.warn(
2998
3030
  `Unable to find any package.json files belonging to the workspace '${awp}' referred in ${f}. To improve SBOM precision, run cdxgen from the directory containing the complete source code.`,
2999
3031
  );
3000
3032
  }
@@ -3084,7 +3116,7 @@ export async function createNodejsBom(path, options) {
3084
3116
  }
3085
3117
  if (!Object.keys(parentComponent).length) {
3086
3118
  if (safeExistsSync(packageJsonF)) {
3087
- const pcs = await parsePkgJson(packageJsonF, true);
3119
+ const pcs = await parsePkgJson(packageJsonF, true, true);
3088
3120
  if (pcs.length && Object.keys(pcs[0]).length) {
3089
3121
  parentComponent = { ...pcs[0] };
3090
3122
  parentComponent.type = "application";
@@ -3170,7 +3202,7 @@ export async function createNodejsBom(path, options) {
3170
3202
  const basePath = dirname(f);
3171
3203
  const packageJsonF = join(basePath, "package.json");
3172
3204
  if (safeExistsSync(packageJsonF)) {
3173
- const pcs = await parsePkgJson(packageJsonF, true);
3205
+ const pcs = await parsePkgJson(packageJsonF, true, true);
3174
3206
  if (pcs.length && Object.keys(pcs[0]).length) {
3175
3207
  tmpParentComponent = { ...pcs[0] };
3176
3208
  tmpParentComponent.type = "application";
@@ -3242,6 +3274,7 @@ export async function createNodejsBom(path, options) {
3242
3274
  const pnpmLock = join(path, "common", "config", "rush", "pnpm-lock.yaml");
3243
3275
  if (safeExistsSync(swFile)) {
3244
3276
  let pkgList = await parseNodeShrinkwrap(swFile);
3277
+ pkgList = addWasmComponentsFromImports(pkgList, allImports);
3245
3278
  if (allImports && Object.keys(allImports).length) {
3246
3279
  pkgList = await addEvidenceForImports(
3247
3280
  pkgList,
@@ -3258,9 +3291,13 @@ export async function createNodejsBom(path, options) {
3258
3291
  }
3259
3292
  if (safeExistsSync(pnpmLock)) {
3260
3293
  const pnpmLockObj = await parsePnpmLock(pnpmLock);
3294
+ let pkgList = addWasmComponentsFromImports(
3295
+ pnpmLockObj.pkgList,
3296
+ allImports,
3297
+ );
3261
3298
  if (allImports && Object.keys(allImports).length) {
3262
3299
  pkgList = await addEvidenceForImports(
3263
- pnpmLockObj.pkgList,
3300
+ pkgList,
3264
3301
  allImports,
3265
3302
  allExports,
3266
3303
  options.deep,
@@ -3335,7 +3372,7 @@ export async function createNodejsBom(path, options) {
3335
3372
  if (!wpkgJsonFiles?.length) {
3336
3373
  if (!workspaceWarningShown) {
3337
3374
  workspaceWarningShown = true;
3338
- console.log(
3375
+ console.warn(
3339
3376
  `Unable to find any package.json files belonging to the workspace '${awp}' referred in ${packageJsonFile}. To improve SBOM precision, run cdxgen from the directory containing the complete source code.`,
3340
3377
  );
3341
3378
  }
@@ -3389,7 +3426,7 @@ export async function createNodejsBom(path, options) {
3389
3426
  // Determine the parent component
3390
3427
  const packageJsonF = join(basePath, "package.json");
3391
3428
  if (safeExistsSync(packageJsonF)) {
3392
- const pcs = await parsePkgJson(packageJsonF, true);
3429
+ const pcs = await parsePkgJson(packageJsonF, true, true);
3393
3430
  if (pcs.length && Object.keys(pcs[0]).length) {
3394
3431
  const tmpParentComponent = { ...pcs[0] };
3395
3432
  tmpParentComponent.type = "application";
@@ -3498,7 +3535,7 @@ export async function createNodejsBom(path, options) {
3498
3535
  }
3499
3536
  if (!parentComponent || !Object.keys(parentComponent).length) {
3500
3537
  if (safeExistsSync(join(path, "package.json"))) {
3501
- const pcs = await parsePkgJson(join(path, "package.json"), true);
3538
+ const pcs = await parsePkgJson(join(path, "package.json"), true, true);
3502
3539
  if (pcs.length && Object.keys(pcs[0]).length) {
3503
3540
  parentComponent = { ...pcs[0] };
3504
3541
  parentComponent.type = "application";
@@ -3537,6 +3574,7 @@ export async function createNodejsBom(path, options) {
3537
3574
  options.parentComponent = parentComponent;
3538
3575
  }
3539
3576
  if (allImports && Object.keys(allImports).length) {
3577
+ pkgList = addWasmComponentsFromImports(pkgList, allImports);
3540
3578
  pkgList = await addEvidenceForImports(
3541
3579
  pkgList,
3542
3580
  allImports,
@@ -3552,6 +3590,84 @@ export async function createNodejsBom(path, options) {
3552
3590
  });
3553
3591
  }
3554
3592
 
3593
+ const WASM_IMPORT_PATTERN = /\.wasm([?#].*)?$/i;
3594
+
3595
+ /**
3596
+ * Adds generic wasm components from discovered source imports.
3597
+ *
3598
+ * @param {Array<Object>} pkgList Node.js package list
3599
+ * @param {Object} allImports analyzer imports map
3600
+ * @returns {Array<Object>} pkgList enriched with wasm components
3601
+ */
3602
+ const addWasmComponentsFromImports = (pkgList, allImports) => {
3603
+ if (!allImports || !Object.keys(allImports).length) {
3604
+ return pkgList;
3605
+ }
3606
+ const existingPurls = new Set();
3607
+ for (const pkg of pkgList) {
3608
+ if (pkg?.purl) {
3609
+ existingPurls.add(pkg.purl);
3610
+ }
3611
+ }
3612
+ for (const [importPath, occurrences] of Object.entries(allImports)) {
3613
+ if (!WASM_IMPORT_PATTERN.test(importPath)) {
3614
+ continue;
3615
+ }
3616
+ const cleanImportPath = importPath.replace(/[?#].*$/, "");
3617
+ const normalizedImportPath = cleanImportPath
3618
+ .replace(/\\/g, "/")
3619
+ .replace(/^\.\//, "");
3620
+ const wasmComponentName = normalizedImportPath || cleanImportPath;
3621
+ const wasmFileName = basename(cleanImportPath);
3622
+ if (!allImports[wasmComponentName]) {
3623
+ allImports[wasmComponentName] = new Set();
3624
+ }
3625
+ for (const occurrence of occurrences) {
3626
+ allImports[wasmComponentName].add(occurrence);
3627
+ }
3628
+ const wasmPurl = new PackageURL(
3629
+ "generic",
3630
+ "",
3631
+ wasmFileName,
3632
+ "",
3633
+ normalizedImportPath ? { path: normalizedImportPath } : undefined,
3634
+ undefined,
3635
+ ).toString();
3636
+ if (existingPurls.has(wasmPurl)) {
3637
+ continue;
3638
+ }
3639
+ const firstOccurrence = Array.from(occurrences)[0];
3640
+ const srcFile = firstOccurrence?.importedAs || importPath;
3641
+ pkgList.push({
3642
+ name: wasmComponentName,
3643
+ type: "library",
3644
+ purl: wasmPurl,
3645
+ "bom-ref": wasmPurl,
3646
+ properties: [
3647
+ {
3648
+ name: "SrcFile",
3649
+ value: srcFile,
3650
+ },
3651
+ ],
3652
+ evidence: {
3653
+ identity: {
3654
+ field: "purl",
3655
+ confidence: 0.3,
3656
+ methods: [
3657
+ {
3658
+ technique: "filename",
3659
+ confidence: 0.3,
3660
+ value: srcFile,
3661
+ },
3662
+ ],
3663
+ },
3664
+ },
3665
+ });
3666
+ existingPurls.add(wasmPurl);
3667
+ }
3668
+ return pkgList;
3669
+ };
3670
+
3555
3671
  /**
3556
3672
  * Function to create bom string for Projects that use Pixi package manager.
3557
3673
  * createPixiBom is based on createPythonBom.
@@ -3687,6 +3803,14 @@ export async function createPythonBom(path, options) {
3687
3803
  if (uvLockFiles?.length) {
3688
3804
  poetryFiles = poetryFiles.concat(uvLockFiles);
3689
3805
  }
3806
+ const pyLockFiles = getAllFiles(
3807
+ path,
3808
+ `${options.multiProject ? "**/" : ""}pylock*.toml`,
3809
+ options,
3810
+ )?.filter((f) => isPyLockFile(f));
3811
+ if (pyLockFiles?.length) {
3812
+ poetryFiles = poetryFiles.concat(pyLockFiles);
3813
+ }
3690
3814
  let reqFiles = getAllFiles(
3691
3815
  path,
3692
3816
  `${options.multiProject ? "**/" : ""}*requirements*.txt`,
@@ -3752,7 +3876,9 @@ export async function createPythonBom(path, options) {
3752
3876
  }
3753
3877
  // When we identify uv lock files, do not parse requirements files
3754
3878
  const requirementsMode =
3755
- (reqFiles?.length || reqDirFiles?.length) && !uvLockFiles.length;
3879
+ (reqFiles?.length || reqDirFiles?.length) &&
3880
+ !uvLockFiles.length &&
3881
+ !pyLockFiles?.length;
3756
3882
  const poetryMode = poetryFiles?.length;
3757
3883
 
3758
3884
  // TODO: Support for nested directories
@@ -3771,6 +3897,12 @@ export async function createPythonBom(path, options) {
3771
3897
  if (retMap?.workspaceWarningShown) {
3772
3898
  options.failOnError && process.exit(1);
3773
3899
  }
3900
+ if (retMap?.pyLockProperties?.length) {
3901
+ parentComponent.properties = parentComponent.properties || [];
3902
+ parentComponent.properties = parentComponent.properties.concat(
3903
+ retMap.pyLockProperties,
3904
+ );
3905
+ }
3774
3906
  if (retMap.pkgList?.length) {
3775
3907
  pkgList = pkgList.concat(retMap.pkgList);
3776
3908
  pkgList = trimComponents(pkgList);
@@ -3790,7 +3922,11 @@ export async function createPythonBom(path, options) {
3790
3922
  parentComponent,
3791
3923
  );
3792
3924
  }
3793
- if ((options.deep || !dependencies.length) && !f.endsWith("uv.lock")) {
3925
+ if (
3926
+ (options.deep || !dependencies.length) &&
3927
+ !f.endsWith("uv.lock") &&
3928
+ !isPyLockFile(f)
3929
+ ) {
3794
3930
  if (options.installDeps) {
3795
3931
  retMap = await getPipFrozenTree(
3796
3932
  basePath,
@@ -5583,15 +5719,27 @@ export async function createCocoaBom(path, options) {
5583
5719
  for (const podFile of cocoaFiles) {
5584
5720
  const projectPath = dirname(podFile);
5585
5721
  const lockFile = `${podFile}.lock`;
5586
- if (!safeExistsSync(lockFile) || options.deep) {
5722
+ let missingLockWarningShown = false;
5723
+ if (!safeExistsSync(lockFile)) {
5587
5724
  if (options.installDeps) {
5588
5725
  executePodCommand(["install"], projectPath, options);
5589
5726
  } else {
5590
5727
  console.log(
5591
5728
  "No 'Podfile.lock' found and '--no-install-deps' is set -- A Podfile.lock is needed to parse dependencies!",
5592
5729
  );
5730
+ missingLockWarningShown = true;
5593
5731
  options.failOnError && process.exit(1);
5594
5732
  }
5733
+ } else if (options.deep && options.installDeps) {
5734
+ executePodCommand(["install"], projectPath, options);
5735
+ }
5736
+ if (!safeExistsSync(lockFile)) {
5737
+ if (!missingLockWarningShown) {
5738
+ console.log(
5739
+ `No 'Podfile.lock' found for ${projectPath}. Skipping CocoaPods dependency parsing for this project.`,
5740
+ );
5741
+ }
5742
+ continue;
5595
5743
  }
5596
5744
  const parentComponent = await buildObjectForCocoaPod(
5597
5745
  {
@@ -7179,6 +7327,61 @@ export async function createVscodeExtensionBom(path, options) {
7179
7327
  });
7180
7328
  }
7181
7329
 
7330
+ /**
7331
+ * Function to create BOM for installed Chrome and Chromium-based browser extensions.
7332
+ *
7333
+ * @param {string} path to the project path or a directly provided extension path
7334
+ * @param {Object} options Parse options from the cli
7335
+ * @returns {Promise<Object>} Promise resolving to BOM object
7336
+ */
7337
+ export async function createChromeExtensionBom(path, options) {
7338
+ let dependencies = [];
7339
+ let sourcePaths = [];
7340
+ const directResult = collectChromeExtensionsFromPath(path);
7341
+ const chromeDirs = discoverChromiumExtensionDirs();
7342
+ let pkgList = directResult.components || [];
7343
+ if (directResult.extensionDirs?.length) {
7344
+ sourcePaths = directResult.extensionDirs.slice();
7345
+ for (const extDir of directResult.extensionDirs) {
7346
+ const deepResult = await analyzeExtensionDir(extDir, options);
7347
+ if (deepResult.pkgList.length) {
7348
+ pkgList = pkgList.concat(deepResult.pkgList);
7349
+ }
7350
+ if (deepResult.dependencies.length) {
7351
+ dependencies = mergeDependencies(dependencies, deepResult.dependencies);
7352
+ }
7353
+ }
7354
+ }
7355
+ if (pkgList.length && DEBUG_MODE) {
7356
+ thoughtLog(
7357
+ `Found ${pkgList.length} component(s) from direct Chrome extension path scan`,
7358
+ );
7359
+ }
7360
+ if (chromeDirs.length) {
7361
+ if (DEBUG_MODE) {
7362
+ thoughtLog(
7363
+ `Discovered Chromium extension directories: ${chromeDirs.map((d) => `${d.browser} (${d.channel}): ${d.dir}`).join(", ")}`,
7364
+ );
7365
+ }
7366
+ if (!pkgList.length) {
7367
+ pkgList = collectInstalledChromeExtensions(chromeDirs);
7368
+ sourcePaths = chromeDirs.map((d) => d.dir);
7369
+ }
7370
+ if (DEBUG_MODE && pkgList.length && !directResult.components?.length) {
7371
+ thoughtLog(
7372
+ `Found ${pkgList.length} Chrome/Chromium extension(s) from ${chromeDirs.length} browser location(s)`,
7373
+ );
7374
+ }
7375
+ }
7376
+ pkgList = trimComponents(pkgList);
7377
+ return buildBomNSData(options, pkgList, CHROME_EXTENSION_PURL_TYPE, {
7378
+ src: path,
7379
+ filename: sourcePaths.join(", "),
7380
+ nsMapping: {},
7381
+ dependencies,
7382
+ });
7383
+ }
7384
+
7182
7385
  /**
7183
7386
  * Analyze an extracted extension directory for bundled dependencies.
7184
7387
  * Looks for npm lock files, node_modules, package.json files, minified JS,
@@ -7350,7 +7553,7 @@ export function dedupeBom(options, components, parentComponent, dependencies) {
7350
7553
  components,
7351
7554
  bomJson: {
7352
7555
  bomFormat: "CycloneDX",
7353
- specVersion: `${options.specVersion || 1.5}`,
7556
+ specVersion: `${options.specVersion || 1.7}`,
7354
7557
  serialNumber: serialNum,
7355
7558
  version: 1,
7356
7559
  metadata: addMetadata(parentComponent, options, {}),
@@ -8099,6 +8302,46 @@ export async function createMultiXBom(pathList, options) {
8099
8302
  }
8100
8303
  }
8101
8304
  }
8305
+ if (hasAnyProjectType(["chrome-extension"], options)) {
8306
+ let isExplicitChromeExtensionPath = false;
8307
+ if (safeExistsSync(path)) {
8308
+ if (basename(path) === "manifest.json") {
8309
+ isExplicitChromeExtensionPath = true;
8310
+ } else {
8311
+ try {
8312
+ isExplicitChromeExtensionPath =
8313
+ statSync(path).isDirectory() &&
8314
+ safeExistsSync(join(path, "manifest.json"));
8315
+ } catch (_err) {
8316
+ isExplicitChromeExtensionPath = false;
8317
+ }
8318
+ }
8319
+ }
8320
+ if (isExplicitChromeExtensionPath || !options.__didScanChromeExtensions) {
8321
+ if (!isExplicitChromeExtensionPath) {
8322
+ options.__didScanChromeExtensions = true;
8323
+ }
8324
+ bomData = await createChromeExtensionBom(path, options);
8325
+ if (bomData?.bomJson?.components?.length) {
8326
+ if (DEBUG_MODE) {
8327
+ console.log(
8328
+ `Found ${bomData.bomJson.components.length} Chrome extension(s) on this host`,
8329
+ );
8330
+ }
8331
+ components = components.concat(bomData.bomJson.components);
8332
+ dependencies = mergeDependencies(
8333
+ dependencies,
8334
+ bomData.bomJson.dependencies,
8335
+ );
8336
+ if (
8337
+ bomData.parentComponent &&
8338
+ Object.keys(bomData.parentComponent).length
8339
+ ) {
8340
+ parentSubComponents.push(bomData.parentComponent);
8341
+ }
8342
+ }
8343
+ }
8344
+ }
8102
8345
  // Collect any crypto keys
8103
8346
  if (options.specVersion >= 1.6 && options.includeCrypto) {
8104
8347
  if (!hasAnyProjectType(["oci"], options, false)) {
@@ -8253,10 +8496,16 @@ export async function createXBom(path, options) {
8253
8496
  // python
8254
8497
  const pipenvMode = safeExistsSync(join(path, "Pipfile"));
8255
8498
  const poetryMode = safeExistsSync(join(path, "poetry.lock"));
8499
+ const pyLockFiles = getAllFiles(
8500
+ path,
8501
+ `${options.multiProject ? "**/" : ""}pylock*.toml`,
8502
+ options,
8503
+ ).filter((f) => isPyLockFile(f));
8504
+ const pyLockMode = pyLockFiles.length > 0;
8256
8505
  const pyProjectMode =
8257
- !poetryMode && safeExistsSync(join(path, "pyproject.toml"));
8506
+ !poetryMode && !pyLockMode && safeExistsSync(join(path, "pyproject.toml"));
8258
8507
  const setupPyMode = safeExistsSync(join(path, "setup.py"));
8259
- if (pipenvMode || poetryMode || pyProjectMode || setupPyMode) {
8508
+ if (pipenvMode || poetryMode || pyLockMode || pyProjectMode || setupPyMode) {
8260
8509
  return await createPythonBom(path, options);
8261
8510
  }
8262
8511
  const reqFiles = getAllFiles(
@@ -8818,6 +9067,9 @@ export async function createBom(path, options) {
8818
9067
  if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
8819
9068
  return await createVscodeExtensionBom(path, options);
8820
9069
  }
9070
+ if (PROJECT_TYPE_ALIASES["chrome-extension"].includes(projectType[0])) {
9071
+ return await createChromeExtensionBom(path, options);
9072
+ }
8821
9073
  switch (projectType[0]) {
8822
9074
  case "jar":
8823
9075
  return createJarBom(path, options);
@@ -8856,66 +9108,22 @@ export async function createBom(path, options) {
8856
9108
  * @throws {Error} if the request fails
8857
9109
  */
8858
9110
  export async function submitBom(args, bomContents) {
8859
- const serverUrl = `${args.serverUrl.replace(/\/$/, "")}/api/v1/bom`;
8860
- let encodedBomContents = Buffer.from(JSON.stringify(bomContents)).toString(
8861
- "base64",
8862
- );
8863
- if (encodedBomContents.startsWith("77u/")) {
8864
- encodedBomContents = encodedBomContents.substring(4);
8865
- }
8866
- const bomPayload = {
8867
- autoCreate: "true",
8868
- bom: encodedBomContents,
8869
- };
8870
- const projectVersion = args.projectVersion || "main";
8871
- if (
8872
- typeof args.projectId !== "undefined" ||
8873
- (typeof args.projectName !== "undefined" &&
8874
- typeof projectVersion !== "undefined")
8875
- ) {
8876
- if (typeof args.projectId !== "undefined") {
8877
- bomPayload.project = args.projectId;
8878
- }
8879
- if (typeof args.projectName !== "undefined") {
8880
- bomPayload.projectName = args.projectName;
8881
- }
8882
- if (typeof projectVersion !== "undefined") {
8883
- bomPayload.projectVersion = projectVersion;
8884
- }
8885
- } else {
9111
+ const serverUrl = getDependencyTrackBomUrl(args.serverUrl);
9112
+ const bomPayload = buildDependencyTrackBomPayload(args, bomContents);
9113
+ if (!bomPayload) {
8886
9114
  console.log(
8887
- "projectId, projectName and projectVersion, or all three must be provided.",
9115
+ "Invalid Dependency-Track submission arguments. Provide projectId or projectName (projectVersion defaults to main) and specify parent project either by UUID or by parent project name + version.",
8888
9116
  );
8889
9117
  args.failOnError && process.exit(1);
8890
9118
  return;
8891
9119
  }
8892
- if (
8893
- typeof args.parentProjectId !== "undefined" ||
8894
- typeof args.parentUUID !== "undefined"
8895
- ) {
8896
- bomPayload.parentUUID = args.parentProjectId || args.parentUUID;
8897
- }
8898
- // Add project tags if provided
8899
- // see https://docs.dependencytrack.org/2024/10/01/v4.12.0/
8900
- // corresponding API usage documentation can be found on the
8901
- // API docs site of your instance, see
8902
- // https://docs.dependencytrack.org/integrations/rest-api/
8903
- // or public instance see https://yoursky.blue/documentation/rest-api
8904
- if (typeof args.projectTag !== "undefined") {
8905
- // If args.projectTag is not an array, convert it to an array
8906
- // Attention, array items should be of form { name: "tagName " }
8907
- // see https://yoursky.blue/documentation/rest-api#tag/bom/operation/UploadBomBase64Encoded
8908
- bomPayload.projectTags = (
8909
- Array.isArray(args.projectTag) ? args.projectTag : [args.projectTag]
8910
- ).map((tag) => ({ name: tag }));
8911
- }
8912
9120
  if (DEBUG_MODE) {
8913
9121
  console.log(
8914
9122
  "Submitting BOM to",
8915
9123
  serverUrl,
8916
9124
  "params",
8917
9125
  args.projectName,
8918
- projectVersion,
9126
+ bomPayload.projectVersion,
8919
9127
  );
8920
9128
  }
8921
9129
  try {