@cyclonedx/cdxgen 12.1.5 → 12.2.1

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 (193) hide show
  1. package/README.md +51 -40
  2. package/bin/cdxgen.js +194 -97
  3. package/bin/evinse.js +4 -4
  4. package/bin/repl.js +1 -1
  5. package/bin/sign.js +102 -0
  6. package/bin/validate.js +233 -0
  7. package/bin/verify.js +69 -28
  8. package/data/queries.json +1 -1
  9. package/data/rules/ci-permissions.yaml +186 -0
  10. package/data/rules/dependency-sources.yaml +123 -0
  11. package/data/rules/package-integrity.yaml +135 -0
  12. package/data/rules/vscode-extensions.yaml +228 -0
  13. package/lib/cli/index.js +449 -429
  14. package/lib/cli/index.poku.js +117 -0
  15. package/lib/evinser/db.js +137 -0
  16. package/lib/{helpers → evinser}/db.poku.js +2 -6
  17. package/lib/evinser/evinser.js +2 -14
  18. package/lib/helpers/analyzer.js +606 -3
  19. package/lib/helpers/analyzer.poku.js +230 -0
  20. package/lib/helpers/bomSigner.js +312 -0
  21. package/lib/helpers/bomSigner.poku.js +156 -0
  22. package/lib/helpers/ciParsers/azurePipelines.js +295 -0
  23. package/lib/helpers/ciParsers/azurePipelines.poku.js +253 -0
  24. package/lib/helpers/ciParsers/circleCi.js +286 -0
  25. package/lib/helpers/ciParsers/circleCi.poku.js +230 -0
  26. package/lib/helpers/ciParsers/common.js +24 -0
  27. package/lib/helpers/ciParsers/githubActions.js +636 -0
  28. package/lib/helpers/ciParsers/githubActions.poku.js +802 -0
  29. package/lib/helpers/ciParsers/gitlabCi.js +213 -0
  30. package/lib/helpers/ciParsers/gitlabCi.poku.js +247 -0
  31. package/lib/helpers/ciParsers/jenkins.js +181 -0
  32. package/lib/helpers/ciParsers/jenkins.poku.js +197 -0
  33. package/lib/helpers/depsUtils.js +219 -0
  34. package/lib/helpers/depsUtils.poku.js +207 -0
  35. package/lib/helpers/display.js +426 -5
  36. package/lib/helpers/envcontext.js +18 -3
  37. package/lib/helpers/formulationParsers.js +351 -0
  38. package/lib/helpers/logger.js +14 -0
  39. package/lib/helpers/protobom.js +9 -9
  40. package/lib/helpers/pythonutils.js +9 -0
  41. package/lib/helpers/remote/dependency-track.js +84 -0
  42. package/lib/helpers/remote/dependency-track.poku.js +119 -0
  43. package/lib/helpers/table.js +384 -0
  44. package/lib/helpers/table.poku.js +186 -0
  45. package/lib/helpers/utils.js +865 -416
  46. package/lib/helpers/utils.poku.js +172 -265
  47. package/lib/helpers/versutils.js +202 -0
  48. package/lib/helpers/versutils.poku.js +315 -0
  49. package/lib/helpers/vsixutils.js +1061 -0
  50. package/lib/helpers/vsixutils.poku.js +2247 -0
  51. package/lib/managers/binary.js +19 -19
  52. package/lib/managers/docker.js +108 -1
  53. package/lib/managers/oci.js +10 -0
  54. package/lib/managers/piptree.js +3 -9
  55. package/lib/parsers/npmrc.js +17 -13
  56. package/lib/parsers/npmrc.poku.js +41 -5
  57. package/lib/server/openapi.yaml +34 -1
  58. package/lib/server/server.js +50 -13
  59. package/lib/server/server.poku.js +332 -144
  60. package/lib/stages/postgen/annotator.js +1 -1
  61. package/lib/stages/postgen/auditBom.js +196 -0
  62. package/lib/stages/postgen/auditBom.poku.js +378 -0
  63. package/lib/stages/postgen/postgen.js +54 -1
  64. package/lib/stages/postgen/postgen.poku.js +90 -1
  65. package/lib/stages/postgen/ruleEngine.js +369 -0
  66. package/lib/stages/pregen/envAudit.js +299 -0
  67. package/lib/stages/pregen/envAudit.poku.js +572 -0
  68. package/lib/stages/pregen/pregen.js +12 -8
  69. package/lib/{helpers/validator.js → validator/bomValidator.js} +107 -47
  70. package/lib/validator/complianceEngine.js +241 -0
  71. package/lib/validator/complianceEngine.poku.js +168 -0
  72. package/lib/validator/complianceRules.js +1610 -0
  73. package/lib/validator/complianceRules.poku.js +328 -0
  74. package/lib/validator/index.js +222 -0
  75. package/lib/validator/index.poku.js +144 -0
  76. package/lib/validator/reporters/annotations.js +121 -0
  77. package/lib/validator/reporters/console.js +149 -0
  78. package/lib/validator/reporters/index.js +41 -0
  79. package/lib/validator/reporters/json.js +37 -0
  80. package/lib/validator/reporters/sarif.js +184 -0
  81. package/lib/validator/reporters.poku.js +150 -0
  82. package/package.json +8 -9
  83. package/types/bin/sign.d.ts +3 -0
  84. package/types/bin/sign.d.ts.map +1 -0
  85. package/types/bin/validate.d.ts +3 -0
  86. package/types/bin/validate.d.ts.map +1 -0
  87. package/types/helpers/utils.d.ts +0 -1
  88. package/types/lib/cli/index.d.ts +49 -52
  89. package/types/lib/cli/index.d.ts.map +1 -1
  90. package/types/lib/evinser/db.d.ts +34 -0
  91. package/types/lib/evinser/db.d.ts.map +1 -0
  92. package/types/lib/evinser/evinser.d.ts +63 -16
  93. package/types/lib/evinser/evinser.d.ts.map +1 -1
  94. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  95. package/types/lib/helpers/bomSigner.d.ts +27 -0
  96. package/types/lib/helpers/bomSigner.d.ts.map +1 -0
  97. package/types/lib/helpers/ciParsers/azurePipelines.d.ts +17 -0
  98. package/types/lib/helpers/ciParsers/azurePipelines.d.ts.map +1 -0
  99. package/types/lib/helpers/ciParsers/circleCi.d.ts +17 -0
  100. package/types/lib/helpers/ciParsers/circleCi.d.ts.map +1 -0
  101. package/types/lib/helpers/ciParsers/common.d.ts +11 -0
  102. package/types/lib/helpers/ciParsers/common.d.ts.map +1 -0
  103. package/types/lib/helpers/ciParsers/githubActions.d.ts +34 -0
  104. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -0
  105. package/types/lib/helpers/ciParsers/gitlabCi.d.ts +17 -0
  106. package/types/lib/helpers/ciParsers/gitlabCi.d.ts.map +1 -0
  107. package/types/lib/helpers/ciParsers/jenkins.d.ts +17 -0
  108. package/types/lib/helpers/ciParsers/jenkins.d.ts.map +1 -0
  109. package/types/lib/helpers/depsUtils.d.ts +21 -0
  110. package/types/lib/helpers/depsUtils.d.ts.map +1 -0
  111. package/types/lib/helpers/display.d.ts +111 -11
  112. package/types/lib/helpers/display.d.ts.map +1 -1
  113. package/types/lib/helpers/envcontext.d.ts +19 -7
  114. package/types/lib/helpers/envcontext.d.ts.map +1 -1
  115. package/types/lib/helpers/formulationParsers.d.ts +50 -0
  116. package/types/lib/helpers/formulationParsers.d.ts.map +1 -0
  117. package/types/lib/helpers/logger.d.ts +15 -1
  118. package/types/lib/helpers/logger.d.ts.map +1 -1
  119. package/types/lib/helpers/protobom.d.ts +2 -2
  120. package/types/lib/helpers/pythonutils.d.ts +10 -1
  121. package/types/lib/helpers/pythonutils.d.ts.map +1 -1
  122. package/types/lib/helpers/remote/dependency-track.d.ts +16 -0
  123. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
  124. package/types/lib/helpers/table.d.ts +6 -0
  125. package/types/lib/helpers/table.d.ts.map +1 -0
  126. package/types/lib/helpers/utils.d.ts +533 -128
  127. package/types/lib/helpers/utils.d.ts.map +1 -1
  128. package/types/lib/helpers/versutils.d.ts +8 -0
  129. package/types/lib/helpers/versutils.d.ts.map +1 -0
  130. package/types/lib/helpers/vsixutils.d.ts +130 -0
  131. package/types/lib/helpers/vsixutils.d.ts.map +1 -0
  132. package/types/lib/managers/docker.d.ts +12 -31
  133. package/types/lib/managers/docker.d.ts.map +1 -1
  134. package/types/lib/managers/oci.d.ts +11 -1
  135. package/types/lib/managers/oci.d.ts.map +1 -1
  136. package/types/lib/managers/piptree.d.ts.map +1 -1
  137. package/types/lib/parsers/npmrc.d.ts +4 -1
  138. package/types/lib/parsers/npmrc.d.ts.map +1 -1
  139. package/types/lib/server/server.d.ts +22 -2
  140. package/types/lib/server/server.d.ts.map +1 -1
  141. package/types/lib/stages/postgen/auditBom.d.ts +20 -0
  142. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -0
  143. package/types/lib/stages/postgen/postgen.d.ts +8 -1
  144. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  145. package/types/lib/stages/postgen/ruleEngine.d.ts +18 -0
  146. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -0
  147. package/types/lib/stages/pregen/envAudit.d.ts +8 -0
  148. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -0
  149. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  150. package/types/lib/{helpers/validator.d.ts → validator/bomValidator.d.ts} +1 -1
  151. package/types/lib/validator/bomValidator.d.ts.map +1 -0
  152. package/types/lib/validator/complianceEngine.d.ts +66 -0
  153. package/types/lib/validator/complianceEngine.d.ts.map +1 -0
  154. package/types/lib/validator/complianceRules.d.ts +70 -0
  155. package/types/lib/validator/complianceRules.d.ts.map +1 -0
  156. package/types/lib/validator/index.d.ts +70 -0
  157. package/types/lib/validator/index.d.ts.map +1 -0
  158. package/types/lib/validator/reporters/annotations.d.ts +31 -0
  159. package/types/lib/validator/reporters/annotations.d.ts.map +1 -0
  160. package/types/lib/validator/reporters/console.d.ts +30 -0
  161. package/types/lib/validator/reporters/console.d.ts.map +1 -0
  162. package/types/lib/validator/reporters/index.d.ts +21 -0
  163. package/types/lib/validator/reporters/index.d.ts.map +1 -0
  164. package/types/lib/validator/reporters/json.d.ts +11 -0
  165. package/types/lib/validator/reporters/json.d.ts.map +1 -0
  166. package/types/lib/validator/reporters/sarif.d.ts +16 -0
  167. package/types/lib/validator/reporters/sarif.d.ts.map +1 -0
  168. package/lib/helpers/db.js +0 -162
  169. package/lib/stages/pregen/env-audit.js +0 -34
  170. package/lib/stages/pregen/env-audit.poku.js +0 -290
  171. package/types/helpers/db.d.ts +0 -35
  172. package/types/helpers/db.d.ts.map +0 -1
  173. package/types/lib/helpers/db.d.ts +0 -35
  174. package/types/lib/helpers/db.d.ts.map +0 -1
  175. package/types/lib/helpers/validator.d.ts.map +0 -1
  176. package/types/lib/stages/pregen/env-audit.d.ts +0 -2
  177. package/types/lib/stages/pregen/env-audit.d.ts.map +0 -1
  178. package/types/managers/binary.d.ts +0 -37
  179. package/types/managers/binary.d.ts.map +0 -1
  180. package/types/managers/docker.d.ts +0 -56
  181. package/types/managers/docker.d.ts.map +0 -1
  182. package/types/managers/oci.d.ts +0 -2
  183. package/types/managers/oci.d.ts.map +0 -1
  184. package/types/managers/piptree.d.ts +0 -2
  185. package/types/managers/piptree.d.ts.map +0 -1
  186. package/types/server/server.d.ts +0 -34
  187. package/types/server/server.d.ts.map +0 -1
  188. package/types/stages/postgen/annotator.d.ts +0 -27
  189. package/types/stages/postgen/annotator.d.ts.map +0 -1
  190. package/types/stages/postgen/postgen.d.ts +0 -51
  191. package/types/stages/postgen/postgen.d.ts.map +0 -1
  192. package/types/stages/pregen/pregen.d.ts +0 -59
  193. package/types/stages/pregen/pregen.d.ts.map +0 -1
@@ -1,5 +1,4 @@
1
1
  import {
2
- existsSync,
3
2
  lstatSync,
4
3
  mkdtempSync,
5
4
  readFileSync,
@@ -31,6 +30,7 @@ import {
31
30
  isSpdxLicenseExpression,
32
31
  multiChecksumFile,
33
32
  retrieveCdxgenPluginVersion,
33
+ safeExistsSync,
34
34
  safeMkdirSync,
35
35
  safeSpawnSync,
36
36
  } from "../helpers/utils.js";
@@ -82,8 +82,8 @@ let extraNMBinPath;
82
82
  // Is there a non-empty local plugins directory
83
83
  if (
84
84
  !CDXGEN_PLUGINS_DIR &&
85
- existsSync(join(dirName, "plugins")) &&
86
- existsSync(join(dirName, "plugins", "trivy"))
85
+ safeExistsSync(join(dirName, "plugins")) &&
86
+ safeExistsSync(join(dirName, "plugins", "trivy"))
87
87
  ) {
88
88
  CDXGEN_PLUGINS_DIR = join(dirName, "plugins");
89
89
  }
@@ -91,7 +91,7 @@ if (
91
91
  // Is there a non-empty local node_modules directory
92
92
  if (
93
93
  !CDXGEN_PLUGINS_DIR &&
94
- existsSync(
94
+ safeExistsSync(
95
95
  join(
96
96
  dirName,
97
97
  "node_modules",
@@ -100,7 +100,7 @@ if (
100
100
  "plugins",
101
101
  ),
102
102
  ) &&
103
- existsSync(
103
+ safeExistsSync(
104
104
  join(
105
105
  dirName,
106
106
  "node_modules",
@@ -118,7 +118,7 @@ if (
118
118
  `cdxgen-plugins-bin${pluginsBinSuffix}`,
119
119
  "plugins",
120
120
  );
121
- if (existsSync(join(dirName, "node_modules", ".bin"))) {
121
+ if (safeExistsSync(join(dirName, "node_modules", ".bin"))) {
122
122
  extraNMBinPath = join(dirName, "node_modules", ".bin");
123
123
  }
124
124
  }
@@ -168,7 +168,7 @@ if (!CDXGEN_PLUGINS_DIR) {
168
168
  `cdxgen-plugins-bin${pluginsBinSuffix}`,
169
169
  "plugins",
170
170
  );
171
- if (existsSync(join(tmpA[0], "node_modules", ".bin"))) {
171
+ if (safeExistsSync(join(tmpA[0], "node_modules", ".bin"))) {
172
172
  extraNMBinPath = join(tmpA[0], "node_modules", ".bin");
173
173
  }
174
174
  } else if (dirName.includes(join(".pnpm", "@cyclonedx+cdxgen"))) {
@@ -183,7 +183,7 @@ if (!CDXGEN_PLUGINS_DIR) {
183
183
  `cdxgen-plugins-bin${pluginsBinSuffix}`,
184
184
  "plugins",
185
185
  );
186
- if (existsSync(join(tmpA[0], ".bin"))) {
186
+ if (safeExistsSync(join(tmpA[0], ".bin"))) {
187
187
  extraNMBinPath = join(tmpA[0], ".bin");
188
188
  }
189
189
  } else if (dirName.includes(join("caxa", "applications"))) {
@@ -201,12 +201,12 @@ if (!CDXGEN_PLUGINS_DIR) {
201
201
  extraNMBinPath = join(dirName, "node_modules", ".bin");
202
202
  }
203
203
  // Set the plugins directory
204
- if (globalPlugins && existsSync(globalPlugins)) {
204
+ if (globalPlugins && safeExistsSync(globalPlugins)) {
205
205
  CDXGEN_PLUGINS_DIR = globalPlugins;
206
206
  if (DEBUG_MODE) {
207
207
  console.log("Found global plugins", CDXGEN_PLUGINS_DIR);
208
208
  }
209
- } else if (altGlobalPlugins && existsSync(altGlobalPlugins)) {
209
+ } else if (altGlobalPlugins && safeExistsSync(altGlobalPlugins)) {
210
210
  CDXGEN_PLUGINS_DIR = altGlobalPlugins;
211
211
  // To help detect bin commands such as atom, astgen, etc, we need to set this to the PATH variable.
212
212
  if (DEBUG_MODE) {
@@ -227,7 +227,7 @@ if (!CDXGEN_PLUGINS_DIR) {
227
227
  CDXGEN_PLUGINS_DIR = "";
228
228
  }
229
229
  let TRIVY_BIN = process.env.TRIVY_CMD;
230
- if (existsSync(join(CDXGEN_PLUGINS_DIR, "trivy"))) {
230
+ if (safeExistsSync(join(CDXGEN_PLUGINS_DIR, "trivy"))) {
231
231
  TRIVY_BIN = join(
232
232
  CDXGEN_PLUGINS_DIR,
233
233
  "trivy",
@@ -235,7 +235,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "trivy"))) {
235
235
  );
236
236
  }
237
237
  let CARGO_AUDITABLE_BIN = process.env.CARGO_AUDITABLE_CMD;
238
- if (existsSync(join(CDXGEN_PLUGINS_DIR, "cargo-auditable"))) {
238
+ if (safeExistsSync(join(CDXGEN_PLUGINS_DIR, "cargo-auditable"))) {
239
239
  CARGO_AUDITABLE_BIN = join(
240
240
  CDXGEN_PLUGINS_DIR,
241
241
  "cargo-auditable",
@@ -243,7 +243,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "cargo-auditable"))) {
243
243
  );
244
244
  }
245
245
  let OSQUERY_BIN = process.env.OSQUERY_CMD;
246
- if (existsSync(join(CDXGEN_PLUGINS_DIR, "osquery"))) {
246
+ if (safeExistsSync(join(CDXGEN_PLUGINS_DIR, "osquery"))) {
247
247
  OSQUERY_BIN = join(
248
248
  CDXGEN_PLUGINS_DIR,
249
249
  "osquery",
@@ -255,7 +255,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "osquery"))) {
255
255
  }
256
256
  }
257
257
  let DOSAI_BIN = process.env.DOSAI_CMD;
258
- if (existsSync(join(CDXGEN_PLUGINS_DIR, "dosai"))) {
258
+ if (safeExistsSync(join(CDXGEN_PLUGINS_DIR, "dosai"))) {
259
259
  DOSAI_BIN = join(
260
260
  CDXGEN_PLUGINS_DIR,
261
261
  "dosai",
@@ -268,7 +268,7 @@ const BLINT_BIN = process.env.BLINT_CMD || "blint";
268
268
 
269
269
  // sourcekitten
270
270
  let SOURCEKITTEN_BIN = process.env.SOURCEKITTEN_CMD;
271
- if (existsSync(join(CDXGEN_PLUGINS_DIR, "sourcekitten"))) {
271
+ if (safeExistsSync(join(CDXGEN_PLUGINS_DIR, "sourcekitten"))) {
272
272
  SOURCEKITTEN_BIN = join(CDXGEN_PLUGINS_DIR, "sourcekitten", "sourcekitten");
273
273
  }
274
274
 
@@ -476,7 +476,7 @@ export async function getOSPackages(src, imageConfig) {
476
476
  } catch (_err) {
477
477
  // ignore errors
478
478
  }
479
- if (existsSync(src)) {
479
+ if (safeExistsSync(src)) {
480
480
  imageType = "rootfs";
481
481
  }
482
482
  const tempDir = mkdtempSync(join(getTmpDir(), "trivy-cdxgen-"));
@@ -513,7 +513,7 @@ export async function getOSPackages(src, imageConfig) {
513
513
  console.error(result.stdout, result.stderr);
514
514
  }
515
515
  }
516
- if (existsSync(bomJsonFile)) {
516
+ if (safeExistsSync(bomJsonFile)) {
517
517
  let tmpBom = {};
518
518
  try {
519
519
  tmpBom = JSON.parse(
@@ -536,9 +536,9 @@ export async function getOSPackages(src, imageConfig) {
536
536
  const osReleaseData = {};
537
537
  let osReleaseFile;
538
538
  // Let's try to read the os-release file from various locations
539
- if (existsSync(join(src, "etc", "os-release"))) {
539
+ if (safeExistsSync(join(src, "etc", "os-release"))) {
540
540
  osReleaseFile = join(src, "etc", "os-release");
541
- } else if (existsSync(join(src, "usr", "lib", "os-release"))) {
541
+ } else if (safeExistsSync(join(src, "usr", "lib", "os-release"))) {
542
542
  osReleaseFile = join(src, "usr", "lib", "os-release");
543
543
  }
544
544
  if (osReleaseFile) {
@@ -71,7 +71,14 @@ if (
71
71
  isContainerd = true;
72
72
  }
73
73
 
74
- // Taken from https://github.com/isaacs/node-tar/blob/main/src/strip-absolute-path.ts
74
+ /**
75
+ * Strip absolute path prefixes from a path string, handling both Unix and
76
+ * Windows paths (including UNC and extended-length paths such as //?/C:/).
77
+ * Taken from https://github.com/isaacs/node-tar/blob/main/src/strip-absolute-path.ts
78
+ *
79
+ * @param {string} path The path to strip
80
+ * @returns {string} The path with its absolute root removed
81
+ */
75
82
  export const stripAbsolutePath = (path) => {
76
83
  // This appears to be a most frequent case, so let's return quickly.
77
84
  if (path === "/") {
@@ -165,6 +172,18 @@ export function detectRancherDesktop() {
165
172
  // Cache the registry auth keys
166
173
  const registry_auth_keys = {};
167
174
  const REQUEST_TIMEOUT_SECS = 60000;
175
+ /**
176
+ * Build a `got` options object for Docker / registry API requests. Resolves
177
+ * authentication headers by consulting (in order) the DOCKER_AUTH_CONFIG
178
+ * environment variable, DOCKER_USER/DOCKER_PASSWORD/DOCKER_EMAIL environment
179
+ * variables, hardcoded tokens in ~/.docker/config.json, credential helpers
180
+ * listed in credHelpers/credsStore, and finally TLS certificate files pointed
181
+ * to by DOCKER_CERT_PATH.
182
+ *
183
+ * @param {string} [forRegistry] Registry hostname (e.g. "registry-1.docker.io").
184
+ * Defaults to DOCKER_SERVER_ADDRESS env var or "docker.io".
185
+ * @returns {Object} Options object suitable for passing to `got`
186
+ */
168
187
  const getDefaultOptions = (forRegistry) => {
169
188
  let authTokenSet = false;
170
189
  if (!forRegistry) {
@@ -358,6 +377,20 @@ const getDefaultOptions = (forRegistry) => {
358
377
  return opts;
359
378
  };
360
379
 
380
+ /**
381
+ * Establish (or reuse) a `got` client connected to the local Docker or Podman
382
+ * daemon. Tries multiple socket / URL candidates in order: the default Docker
383
+ * socket, the rootless Docker socket, the Windows TCP endpoint, the rootless
384
+ * Podman socket, and the root Podman socket. Sets the module-level flags
385
+ * `isPodman`, `isPodmanRootless`, `isDockerRootless`, and `isWinLocalTLS` as a
386
+ * side-effect. Returns `undefined` when containerd / nerdctl is in use or no
387
+ * daemon could be reached.
388
+ *
389
+ * @param {Object} options Additional `got` options to merge into the connection
390
+ * @param {string} [forRegistry] Registry hostname forwarded to `getDefaultOptions`
391
+ * @returns {Promise<import("got").Got|undefined>} A `got` instance bound to the
392
+ * daemon base URL, or `undefined`
393
+ */
361
394
  export const getConnection = async (options, forRegistry) => {
362
395
  if (isContainerd || isNerdctl) {
363
396
  return undefined;
@@ -461,6 +494,17 @@ export const getConnection = async (options, forRegistry) => {
461
494
  return dockerConn;
462
495
  };
463
496
 
497
+ /**
498
+ * Send a single HTTP request to the Docker / Podman daemon via the `got`
499
+ * client returned by {@link getConnection}. GET requests are parsed as JSON;
500
+ * all other methods receive a Buffer response body.
501
+ *
502
+ * @param {string} path API path relative to the daemon base URL (e.g. "images/ubuntu:latest/json")
503
+ * @param {string} method HTTP method (e.g. "GET", "POST", "DELETE")
504
+ * @param {string} [forRegistry] Registry hostname forwarded to `getDefaultOptions` for auth headers
505
+ * @returns {Promise<Object|Buffer|undefined>} Parsed JSON object for GET
506
+ * requests, raw Buffer for other methods, or `undefined` if no client is available
507
+ */
464
508
  export const makeRequest = async (path, method, forRegistry) => {
465
509
  const client = await getConnection({}, forRegistry);
466
510
  if (!client) {
@@ -815,6 +859,14 @@ function tarFilter(path, entry) {
815
859
  );
816
860
  }
817
861
 
862
+ /**
863
+ * Suppress low-signal tar warnings (TAR_ENTRY_INFO, TAR_LONGLINK) that are
864
+ * expected when extracting container image layers. All other warning codes are
865
+ * logged when DEBUG_MODE is enabled.
866
+ *
867
+ * @param {string} code Tar warning code (e.g. "TAR_ENTRY_INFO")
868
+ * @param {string} message Human-readable warning message
869
+ */
818
870
  function handleTarWarning(code, message) {
819
871
  if (code === "TAR_ENTRY_INFO" || code === "TAR_LONGLINK") {
820
872
  return;
@@ -885,6 +937,18 @@ const EXTRACT_EXCLUDE_TYPES = new Set([
885
937
  "Link",
886
938
  ]);
887
939
 
940
+ /**
941
+ * Extract a container image tar archive into a destination directory.
942
+ * Applies path sanitisation, ownership/permission preservation settings, and
943
+ * an entry filter to skip problematic files and device nodes. Handles common
944
+ * tar errors gracefully, logging only unexpected ones.
945
+ *
946
+ * @param {string} fullImageName Path to the source tar archive
947
+ * @param {string} dir Destination directory to extract into
948
+ * @param {Object} options CLI options (uses `options.failOnError`)
949
+ * @returns {Promise<boolean>} `true` on success, `false` when the archive is
950
+ * empty or a non-fatal error was encountered
951
+ */
888
952
  export const extractTar = async (fullImageName, dir, options) => {
889
953
  try {
890
954
  await stream.pipeline(
@@ -1016,6 +1080,21 @@ export const exportArchive = async (fullImageName, options = {}) => {
1016
1080
  return undefined;
1017
1081
  };
1018
1082
 
1083
+ /**
1084
+ * Parse a Docker/containerd manifest file and extract all image layers into a
1085
+ * single merged directory. Resolves the last layer's config to determine the
1086
+ * container's working directory, and builds the package path list for
1087
+ * subsequent analysis.
1088
+ *
1089
+ * @param {string} manifestFile Path to the manifest.json (or index.json) file
1090
+ * @param {Object} localData Local image inspect data (e.g. from `docker inspect`)
1091
+ * @param {string} tempDir Temporary directory that holds the unpacked image
1092
+ * @param {string} allLayersExplodedDir Directory where all layers are merged
1093
+ * @param {Object} options CLI options (uses `options.failOnError`)
1094
+ * @returns {Promise<Object>} Export data object containing `manifest`,
1095
+ * `allLayersDir`, `allLayersExplodedDir`, `lastLayerConfig`,
1096
+ * `lastWorkingDir`, `binPaths`, and `pkgPathList`
1097
+ */
1019
1098
  export const extractFromManifest = async (
1020
1099
  manifestFile,
1021
1100
  localData,
@@ -1414,10 +1493,29 @@ export const getPkgPathList = (exportData, lastWorkingDir) => {
1414
1493
  return pathList;
1415
1494
  };
1416
1495
 
1496
+ /**
1497
+ * Remove a container image from the local Docker / Podman daemon.
1498
+ *
1499
+ * @param {string} fullImageName Full image name including tag or digest (e.g. "ubuntu:22.04")
1500
+ * @param {boolean} [force=false] When `true`, force-remove the image even if it is in use
1501
+ * @returns {Promise<Buffer|undefined>} Raw response buffer from the daemon, or
1502
+ * `undefined` if no daemon connection is available
1503
+ */
1417
1504
  export const removeImage = async (fullImageName, force = false) => {
1418
1505
  return await makeRequest(`images/${fullImageName}?force=${force}`, "DELETE");
1419
1506
  };
1420
1507
 
1508
+ /**
1509
+ * Retrieve a base64url-encoded authentication token for a registry server by
1510
+ * invoking the `docker-credential-<exeSuffix>` credential helper binary.
1511
+ * Results are cached in `registry_auth_keys` to avoid redundant subprocess
1512
+ * calls.
1513
+ *
1514
+ * @param {string} exeSuffix Credential helper name suffix (e.g. "osxkeychain", "wincred", "pass")
1515
+ * @param {string} serverAddress Registry server address (e.g. "https://index.docker.io/v1/")
1516
+ * @returns {string|undefined} Base64url-encoded JSON auth token, or `undefined`
1517
+ * if the helper is unavailable or returns an error
1518
+ */
1421
1519
  export const getCredsFromHelper = (exeSuffix, serverAddress) => {
1422
1520
  if (registry_auth_keys[serverAddress]) {
1423
1521
  return registry_auth_keys[serverAddress];
@@ -1460,6 +1558,15 @@ export const getCredsFromHelper = (exeSuffix, serverAddress) => {
1460
1558
  return undefined;
1461
1559
  };
1462
1560
 
1561
+ /**
1562
+ * Append skipped source-file entries to the `SrcFile` properties of matching
1563
+ * components. A component matches when its `oci:SrcImage` property value
1564
+ * equals the skipped image's `image` field and the source file path is not
1565
+ * already listed.
1566
+ *
1567
+ * @param {Array<{image: string, src: string}>} skippedImageSrcs List of skipped image/source pairs
1568
+ * @param {Array<Object>} components CycloneDX component objects to update in place
1569
+ */
1463
1570
  export const addSkippedSrcFiles = (skippedImageSrcs, components) => {
1464
1571
  for (const skippedImage of skippedImageSrcs) {
1465
1572
  for (const co of components) {
@@ -9,6 +9,16 @@ import {
9
9
  safeSpawnSync,
10
10
  } from "../helpers/utils.js";
11
11
 
12
+ /**
13
+ * Retrieves a CycloneDX BOM attached to an OCI image using the `oras` CLI tool.
14
+ * Discovers SBOM attachments via `oras discover`, pulls the first matching
15
+ * artifact, and returns the parsed BOM JSON. Retries automatically with a
16
+ * platform-specific manifest when the initial platform-agnostic discovery fails.
17
+ *
18
+ * @param {string} image OCI image reference (e.g. `"registry.example.com/org/app:tag"`)
19
+ * @param {string} [platform] OCI platform string (e.g. `"linux/amd64"`); detected automatically when omitted
20
+ * @returns {Object|undefined} Parsed CycloneDX BOM JSON object, or `undefined` if not found
21
+ */
12
22
  export function getBomWithOras(image, platform = undefined) {
13
23
  const platformArch = arch() === "arm64" ? "arm64" : "amd64";
14
24
  let parameters = [
@@ -4,16 +4,10 @@
4
4
  *
5
5
  * We use the internal pip api to construct the dependency tree for modern python + pip environments
6
6
  */
7
- import {
8
- existsSync,
9
- mkdtempSync,
10
- readFileSync,
11
- rmSync,
12
- writeFileSync,
13
- } from "node:fs";
7
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
14
8
  import { delimiter, join } from "node:path";
15
9
 
16
- import { getTmpDir, safeSpawnSync } from "../helpers/utils.js";
10
+ import { getTmpDir, safeExistsSync, safeSpawnSync } from "../helpers/utils.js";
17
11
 
18
12
  const PIP_TREE_PLUGIN_CONTENT = `
19
13
  import importlib.metadata as importlib_metadata
@@ -254,7 +248,7 @@ export const getTreeWithPlugin = (env, python_cmd, basePath) => {
254
248
  console.log(result.stdout, result.stderr);
255
249
  }
256
250
  }
257
- if (existsSync(pipTreeJson)) {
251
+ if (safeExistsSync(pipTreeJson)) {
258
252
  tree = JSON.parse(
259
253
  readFileSync(pipTreeJson, {
260
254
  encoding: "utf-8",
@@ -56,7 +56,7 @@ export function parseNpmrc(content) {
56
56
  }
57
57
 
58
58
  /**
59
- * Extract npm configuration values from environment variables.
59
+ * Extract npm/pnpm configuration values from environment variables.
60
60
  * See https://docs.npmjs.com/cli/v11/using-npm/config
61
61
  *
62
62
  * npm uses the NPM_CONFIG_ prefix for env var config:
@@ -65,24 +65,28 @@ export function parseNpmrc(content) {
65
65
  * - Simple keys are lowercased; scoped/URI keys preserve case
66
66
  * - Boolean flags without values are treated as true
67
67
  *
68
+ * pnpm v11+ uses the PNPM_CONFIG_ prefix instead of NPM_CONFIG_ for pnpm-specific settings.
69
+ * Both prefixes are supported; pnpm_config_* takes precedence over npm_config_* for the same key.
70
+ * See https://pnpm.io/next/npmrc
68
71
  * @param {Object} env - Environment variables object (defaults to process.env)
69
72
  * @returns {Object} Parsed npm config key-value pairs
70
73
  */
71
74
  export function parseNpmrcFromEnv(env = process.env) {
72
75
  const result = {};
73
- const PREFIX = "npm_config_";
74
-
75
- for (const [fullKey, value] of Object.entries(env)) {
76
- if (!fullKey.toLowerCase().startsWith(PREFIX)) {
77
- continue;
78
- }
79
-
80
- let configKey = fullKey.slice(PREFIX.length);
81
- if (!configKey) continue;
82
- if (!configKey.startsWith("//") && !configKey.startsWith("@")) {
83
- configKey = configKey.toLowerCase();
76
+ const NPM_PREFIX = "npm_config_";
77
+ const PNPM_PREFIX = "pnpm_config_";
78
+ for (const prefix of [NPM_PREFIX, PNPM_PREFIX]) {
79
+ for (const [fullKey, value] of Object.entries(env)) {
80
+ if (!fullKey.toLowerCase().startsWith(prefix)) {
81
+ continue;
82
+ }
83
+ let configKey = fullKey.slice(prefix.length);
84
+ if (!configKey) continue;
85
+ if (!configKey.startsWith("//") && !configKey.startsWith("@")) {
86
+ configKey = configKey.toLowerCase();
87
+ }
88
+ result[configKey] = value === "" || value === undefined ? "true" : value;
84
89
  }
85
- result[configKey] = value === "" || value === undefined ? "true" : value;
86
90
  }
87
91
  return result;
88
92
  }
@@ -415,16 +415,52 @@ const VALID_ENV_CASES = [
415
415
  cache: "/tmp/cache",
416
416
  },
417
417
  },
418
- {
419
- name: "scoped registry auth in env",
420
- env: { "npm_config_//registry.example.com/:_authToken": "secret123" },
421
- expected: { "//registry.example.com/:_authToken": "secret123" },
422
- },
423
418
  {
424
419
  name: "unicode values preserved",
425
420
  env: { npm_config_description: "测试🔐" },
426
421
  expected: { description: "测试🔐" },
427
422
  },
423
+ {
424
+ name: "basic pnpm_config_ prefix",
425
+ env: { pnpm_config_registry: "https://pnpm-registry.example.com" },
426
+ expected: { registry: "https://pnpm-registry.example.com" },
427
+ },
428
+ {
429
+ name: "case-insensitive PNPM_CONFIG_ prefix",
430
+ env: { PNPM_CONFIG_PROXY: "http://proxy.local" },
431
+ expected: { proxy: "http://proxy.local" },
432
+ },
433
+ {
434
+ name: "pnpm_config_ simple key lowercased",
435
+ env: { PNPM_CONFIG_STORE_DIR: "/custom/store" },
436
+ expected: { store_dir: "/custom/store" },
437
+ },
438
+ {
439
+ name: "pnpm_config_ boolean flag with empty value → true",
440
+ env: { pnpm_config_shamefully_hoist: "" },
441
+ expected: { shamefully_hoist: "true" },
442
+ },
443
+ {
444
+ name: "pnpm_config_ overrides npm_config_ for same key",
445
+ env: {
446
+ npm_config_registry: "https://npm-registry.com",
447
+ pnpm_config_registry: "https://pnpm-registry.com",
448
+ },
449
+ expected: { registry: "https://pnpm-registry.com" },
450
+ },
451
+ {
452
+ name: "pnpm_config_ and npm_config_ for different keys are both included",
453
+ env: {
454
+ npm_config_cache: "/npm-cache",
455
+ pnpm_config_store_dir: "/pnpm-store",
456
+ },
457
+ expected: { cache: "/npm-cache", store_dir: "/pnpm-store" },
458
+ },
459
+ {
460
+ name: "empty config key after pnpm_config_ prefix ignored",
461
+ env: { pnpm_config_: "value" },
462
+ expected: {},
463
+ },
428
464
  ];
429
465
 
430
466
  const EDGE_ENV_CASES = [
@@ -94,11 +94,31 @@ paths:
94
94
  required: false
95
95
  schema:
96
96
  $ref: '#/components/schemas/CDXGEN/properties/projectVersion'
97
+ - name: autoCreate
98
+ in: query
99
+ required: false
100
+ schema:
101
+ $ref: '#/components/schemas/CDXGEN/properties/autoCreate'
102
+ - name: isLatest
103
+ in: query
104
+ required: false
105
+ schema:
106
+ $ref: '#/components/schemas/CDXGEN/properties/isLatest'
97
107
  - name: parentUUID
98
108
  in: query
99
109
  required: false
100
110
  schema:
101
111
  $ref: '#/components/schemas/CDXGEN/properties/parentUUID'
112
+ - name: parentProjectName
113
+ in: query
114
+ required: false
115
+ schema:
116
+ $ref: '#/components/schemas/CDXGEN/properties/parentProjectName'
117
+ - name: parentProjectVersion
118
+ in: query
119
+ required: false
120
+ schema:
121
+ $ref: '#/components/schemas/CDXGEN/properties/parentProjectVersion'
102
122
  - name: serverUrl
103
123
  in: query
104
124
  required: false
@@ -252,9 +272,22 @@ components:
252
272
  type: string
253
273
  description: Dependency Track project version
254
274
  default: ""
275
+ autoCreate:
276
+ type: boolean
277
+ description: Dependency Track autoCreate value for BOM submissions
278
+ default: true
279
+ isLatest:
280
+ type: boolean
281
+ description: Dependency Track isLatest value for BOM submissions
255
282
  parentUUID:
256
283
  type: string
257
284
  description: UUID of the parent Dependency Track project
285
+ parentProjectName:
286
+ type: string
287
+ description: Name of the parent Dependency Track project
288
+ parentProjectVersion:
289
+ type: string
290
+ description: Version of the parent Dependency Track project
258
291
  serverUrl:
259
292
  type: string
260
293
  description: URL to the Dependency Track API server
@@ -264,7 +297,7 @@ components:
264
297
  specVersion:
265
298
  type: string
266
299
  description: CycloneDX Specification version to use
267
- default: "1.6"
300
+ default: "1.7"
268
301
  filter:
269
302
  type: array
270
303
  items: