@cyclonedx/cdxgen 10.4.1 → 10.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -80,7 +80,7 @@ For node.js projects, lock files are parsed initially, so the SBOM would include
80
80
 
81
81
  This attribute can be later used for various purposes. For example, [dep-scan](https://github.com/cyclonedx/dep-scan) uses this attribute to prioritize vulnerabilities. Unfortunately, tools such as dependency track, do not include this feature and might over-report the CVEs.
82
82
 
83
- By passing the argument `--required-only`, you can limit the SBOM only to include packages with the scope "required", commonly called production or non-dev dependencies. Combine with `--no-babel` to limit this list to only non-dev dependencies based on the `dev` attribute being false in the lock files.
83
+ With the argument `--required-only`, you can limit the SBOM only to include packages with the scope "required", commonly called production or non-dev dependencies. Combine with `--no-babel` to limit this list to only non-dev dependencies based on the `dev` attribute being false in the lock files.
84
84
 
85
85
  For go, `go mod why` command is used to identify required packages. For php, composer lock file is parsed to distinguish required (packages) from optional (packages-dev).
86
86
 
@@ -98,18 +98,18 @@ If you are a [Homebrew](https://brew.sh/) user, you can also install [cdxgen](ht
98
98
  $ brew install cdxgen
99
99
  ```
100
100
 
101
- Deno install is also supported.
101
+ Deno and bun runtime can be used with limited support.
102
102
 
103
103
  ```shell
104
104
  deno install --allow-read --allow-env --allow-run --allow-sys=uid,systemMemoryInfo,gid --allow-write --allow-net -n cdxgen "npm:@cyclonedx/cdxgen/cdxgen"
105
105
  ```
106
106
 
107
- You can also use the cdxgen container image
107
+ You can also use the cdxgen container image with node, deno, or bun runtime versions.
108
+
109
+ The default version uses Node.js 20
108
110
 
109
111
  ```bash
110
112
  docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen -r /app -o /app/bom.json
111
-
112
- docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen:v8.6.0 -r /app -o /app/bom.json
113
113
  ```
114
114
 
115
115
  To use the deno version, use `ghcr.io/cyclonedx/cdxgen-deno` as the image name.
@@ -118,6 +118,12 @@ To use the deno version, use `ghcr.io/cyclonedx/cdxgen-deno` as the image name.
118
118
  docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen-deno -r /app -o /app/bom.json
119
119
  ```
120
120
 
121
+ For the bun version, use `ghcr.io/cyclonedx/cdxgen-bun` as the image name.
122
+
123
+ ```bash
124
+ docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen-bun -r /app -o /app/bom.json
125
+ ```
126
+
121
127
  In deno applications, cdxgen could be directly imported without any conversion. Please see the section on [integration as library](#integration-as-library)
122
128
 
123
129
  ```ts
package/analyzer.js CHANGED
@@ -26,7 +26,6 @@ const IGNORE_DIRS = process.env.ASTGEN_IGNORE_DIRS
26
26
  "codemods",
27
27
  "flow-typed",
28
28
  "i18n",
29
- "__tests__",
30
29
  ];
31
30
 
32
31
  const IGNORE_FILE_PATTERN = new RegExp(
@@ -54,6 +53,7 @@ const getAllFiles = (deep, dir, extn, files, result, regex) => {
54
53
  const dirName = basename(file);
55
54
  if (
56
55
  dirName.startsWith(".") ||
56
+ dirName.startsWith("__") ||
57
57
  IGNORE_DIRS.includes(dirName.toLowerCase())
58
58
  ) {
59
59
  continue;
@@ -141,8 +141,8 @@ const setFileRef = (
141
141
  exportedModules,
142
142
  isExternal: true,
143
143
  fileName: fileRelativeLoc,
144
- lineNumber: sourceLoc && sourceLoc.line ? sourceLoc.line : undefined,
145
- columnNumber: sourceLoc && sourceLoc.column ? sourceLoc.column : undefined,
144
+ lineNumber: sourceLoc?.line ? sourceLoc.line : undefined,
145
+ columnNumber: sourceLoc?.column ? sourceLoc.column : undefined,
146
146
  };
147
147
  // replace relative imports with full path
148
148
  let moduleFullPath = pathway;
@@ -167,7 +167,7 @@ const setFileRef = (
167
167
  allImports[modPkg] = allImports[modPkg] || new Set();
168
168
  allImports[modPkg].add(occurrence);
169
169
  }
170
- if (exportedModules && exportedModules.length) {
170
+ if (exportedModules?.length) {
171
171
  moduleFullPath = moduleFullPath
172
172
  .replace("node_modules/", "")
173
173
  .replace("dist/", "")
@@ -192,7 +192,7 @@ const fileToParseableCode = (file) => {
192
192
  .replace(vueCommentRegex, (match) => match.replaceAll(/\S/g, " "))
193
193
  .replace(
194
194
  vueCleaningRegex,
195
- (match) => match.replaceAll(/\S/g, " ").substring(1) + ";",
195
+ (match) => `${match.replaceAll(/\S/g, " ").substring(1)};`,
196
196
  )
197
197
  .replace(
198
198
  vueBindRegex,
@@ -201,7 +201,7 @@ const fileToParseableCode = (file) => {
201
201
  )
202
202
  .replace(
203
203
  vuePropRegex,
204
- (match, grA, grB) => " " + grA.replace(/[.:@]/g, " ") + grB,
204
+ (match, grA, grB) => ` ${grA.replace(/[.:@]/g, " ")}${grB}`,
205
205
  )
206
206
  .replace(
207
207
  vueTemplateRegex,
@@ -220,7 +220,7 @@ const parseFileASTTree = (src, file, allImports, allExports) => {
220
220
  const ast = parse(fileToParseableCode(file), babelParserOptions);
221
221
  traverse.default(ast, {
222
222
  ImportDeclaration: (path) => {
223
- if (path && path.node) {
223
+ if (path?.node) {
224
224
  setFileRef(
225
225
  allImports,
226
226
  allExports,
@@ -234,8 +234,7 @@ const parseFileASTTree = (src, file, allImports, allExports) => {
234
234
  // For require('') statements
235
235
  Identifier: (path) => {
236
236
  if (
237
- path &&
238
- path.node &&
237
+ path?.node &&
239
238
  path.node.name === "require" &&
240
239
  path.parent.type === "CallExpression"
241
240
  ) {
@@ -244,7 +243,7 @@ const parseFileASTTree = (src, file, allImports, allExports) => {
244
243
  },
245
244
  // Use for dynamic imports like routes.jsx
246
245
  CallExpression: (path) => {
247
- if (path && path.node && path.node.callee.type === "Import") {
246
+ if (path?.node && path.node.callee.type === "Import") {
248
247
  setFileRef(allImports, allExports, src, file, path.node.arguments[0]);
249
248
  }
250
249
  },
@@ -254,7 +253,7 @@ const parseFileASTTree = (src, file, allImports, allExports) => {
254
253
  },
255
254
  ExportNamedDeclaration: (path) => {
256
255
  // ensure there is a path export
257
- if (path && path.node && path.node.source) {
256
+ if (path?.node?.source) {
258
257
  setFileRef(
259
258
  allImports,
260
259
  allExports,
package/bin/cdxgen.js CHANGED
@@ -371,10 +371,13 @@ const applyAdvancedOptions = (options) => {
371
371
  "aab",
372
372
  "go",
373
373
  "golang",
374
+ "rust",
375
+ "rust-lang",
376
+ "cargo",
374
377
  ].includes(options.projectType)
375
378
  ) {
376
379
  console.log(
377
- "PREVIEW: post-build lifecycle SBOM generation is supported only for android, dotnet, and go projects. Please specify the type using the -t argument.",
380
+ "PREVIEW: post-build lifecycle SBOM generation is supported only for android, dotnet, go, and Rust projects. Please specify the type using the -t argument.",
378
381
  );
379
382
  process.exit(1);
380
383
  }
@@ -591,7 +594,7 @@ const checkPermissions = (filePath) => {
591
594
  }
592
595
  // bom ns mapping
593
596
  if (bomNSData.nsMapping && Object.keys(bomNSData.nsMapping).length) {
594
- const nsFile = jsonFile + ".map";
597
+ const nsFile = `${jsonFile}.map`;
595
598
  fs.writeFileSync(nsFile, JSON.stringify(bomNSData.nsMapping));
596
599
  }
597
600
  } else if (!options.print) {
@@ -646,6 +649,7 @@ const checkPermissions = (filePath) => {
646
649
  }
647
650
  }
648
651
  // Automatically submit the bom data
652
+ // biome-ignore lint/suspicious/noDoubleEquals: yargs passes true for empty values
649
653
  if (options.serverUrl && options.serverUrl != true && options.apiKey) {
650
654
  try {
651
655
  const dbody = await submitBom(options, bomNSData.bomJson);
package/bin/repl.js CHANGED
@@ -58,7 +58,7 @@ if (!process.env.CDXGEN_REPL_HISTORY && !fs.existsSync(historyConfigDir)) {
58
58
  }
59
59
 
60
60
  export const importSbom = (sbomOrPath) => {
61
- if (sbomOrPath && sbomOrPath.endsWith(".json") && fs.existsSync(sbomOrPath)) {
61
+ if (sbomOrPath?.endsWith(".json") && fs.existsSync(sbomOrPath)) {
62
62
  try {
63
63
  sbom = JSON.parse(fs.readFileSync(sbomOrPath, "utf-8"));
64
64
  console.log(`✅ SBOM imported successfully from ${sbomOrPath}`);
@@ -298,12 +298,12 @@ cdxgenRepl.defineCommand("update", {
298
298
  return;
299
299
  }
300
300
  if (!updateSpec.startsWith("|")) {
301
- updateSpec = "|" + updateSpec;
301
+ updateSpec = `|${updateSpec}`;
302
302
  }
303
303
  if (!updateSpec.endsWith("|")) {
304
- updateSpec = updateSpec + "|";
304
+ updateSpec = `${updateSpec}|`;
305
305
  }
306
- updateSpec = "$ ~> " + updateSpec;
306
+ updateSpec = `$ ~> ${updateSpec}`;
307
307
  const expression = jsonata(updateSpec);
308
308
  const newSbom = await expression.evaluate(sbom);
309
309
  if (newSbom && newSbom.components.length <= sbom.components.length) {
package/bin/verify.js CHANGED
@@ -61,10 +61,9 @@ for (const comp of bomJson.components) {
61
61
  if (hasInvalidComp) {
62
62
  process.exit(1);
63
63
  }
64
- const bomSignature =
65
- bomJson.signature && bomJson.signature.value
66
- ? bomJson.signature.value
67
- : undefined;
64
+ const bomSignature = bomJson.signature?.value
65
+ ? bomJson.signature.value
66
+ : undefined;
68
67
  if (!bomSignature) {
69
68
  console.log("No signature was found!");
70
69
  } else {
package/binary.js CHANGED
@@ -71,7 +71,7 @@ let CDXGEN_PLUGINS_DIR = process.env.CDXGEN_PLUGINS_DIR;
71
71
  if (
72
72
  !CDXGEN_PLUGINS_DIR &&
73
73
  existsSync(join(dirName, "plugins")) &&
74
- existsSync(join(dirName, "plugins", "goversion"))
74
+ existsSync(join(dirName, "plugins", "trivy"))
75
75
  ) {
76
76
  CDXGEN_PLUGINS_DIR = join(dirName, "plugins");
77
77
  }
@@ -83,7 +83,7 @@ if (
83
83
  dirName,
84
84
  "node_modules",
85
85
  "@cyclonedx",
86
- "cdxgen-plugins-bin" + pluginsBinSuffix,
86
+ `cdxgen-plugins-bin${pluginsBinSuffix}`,
87
87
  "plugins",
88
88
  ),
89
89
  ) &&
@@ -92,9 +92,9 @@ if (
92
92
  dirName,
93
93
  "node_modules",
94
94
  "@cyclonedx",
95
- "cdxgen-plugins-bin" + pluginsBinSuffix,
95
+ `cdxgen-plugins-bin${pluginsBinSuffix}`,
96
96
  "plugins",
97
- "goversion",
97
+ "trivy",
98
98
  ),
99
99
  )
100
100
  ) {
@@ -102,7 +102,7 @@ if (
102
102
  dirName,
103
103
  "node_modules",
104
104
  "@cyclonedx",
105
- "cdxgen-plugins-bin" + pluginsBinSuffix,
105
+ `cdxgen-plugins-bin${pluginsBinSuffix}`,
106
106
  "plugins",
107
107
  );
108
108
  }
@@ -120,7 +120,7 @@ if (!CDXGEN_PLUGINS_DIR) {
120
120
  if (result) {
121
121
  const stdout = result.stdout;
122
122
  if (stdout) {
123
- globalNodePath = Buffer.from(stdout).toString().trim() + "/";
123
+ globalNodePath = `${Buffer.from(stdout).toString().trim()}/`;
124
124
  }
125
125
  }
126
126
  }
@@ -128,7 +128,7 @@ if (!CDXGEN_PLUGINS_DIR) {
128
128
  const globalPlugins = join(
129
129
  globalNodePath,
130
130
  "@cyclonedx",
131
- "cdxgen-plugins-bin" + pluginsBinSuffix,
131
+ `cdxgen-plugins-bin${pluginsBinSuffix}`,
132
132
  "plugins",
133
133
  );
134
134
  if (existsSync(globalPlugins)) {
@@ -153,7 +153,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "goversion"))) {
153
153
  GOVERSION_BIN = join(
154
154
  CDXGEN_PLUGINS_DIR,
155
155
  "goversion",
156
- "goversion-" + platform + "-" + arch + extn,
156
+ `goversion-${platform}-${arch}${extn}`,
157
157
  );
158
158
  }
159
159
  let TRIVY_BIN = null;
@@ -161,7 +161,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "trivy"))) {
161
161
  TRIVY_BIN = join(
162
162
  CDXGEN_PLUGINS_DIR,
163
163
  "trivy",
164
- "trivy-cdxgen-" + platform + "-" + arch + extn,
164
+ `trivy-cdxgen-${platform}-${arch}${extn}`,
165
165
  );
166
166
  } else if (process.env.TRIVY_CMD) {
167
167
  TRIVY_BIN = process.env.TRIVY_CMD;
@@ -171,7 +171,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "cargo-auditable"))) {
171
171
  CARGO_AUDITABLE_BIN = join(
172
172
  CDXGEN_PLUGINS_DIR,
173
173
  "cargo-auditable",
174
- "cargo-auditable-cdxgen-" + platform + "-" + arch + extn,
174
+ `cargo-auditable-cdxgen-${platform}-${arch}${extn}`,
175
175
  );
176
176
  } else if (process.env.CARGO_AUDITABLE_CMD) {
177
177
  CARGO_AUDITABLE_BIN = process.env.CARGO_AUDITABLE_CMD;
@@ -181,7 +181,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "osquery"))) {
181
181
  OSQUERY_BIN = join(
182
182
  CDXGEN_PLUGINS_DIR,
183
183
  "osquery",
184
- "osqueryi-" + platform + "-" + arch + extn,
184
+ `osqueryi-${platform}-${arch}${extn}`,
185
185
  );
186
186
  // osqueryi-darwin-amd64.app/Contents/MacOS/osqueryd
187
187
  if (platform === "darwin") {
@@ -199,7 +199,7 @@ if (existsSync(join(CDXGEN_PLUGINS_DIR, "dosai"))) {
199
199
  DOSAI_BIN = join(
200
200
  CDXGEN_PLUGINS_DIR,
201
201
  "dosai",
202
- "dosai-" + platformToUse + "-" + arch + extn,
202
+ `dosai-${platformToUse}-${arch}${extn}`,
203
203
  );
204
204
  } else if (process.env.DOSAI_CMD) {
205
205
  DOSAI_BIN = process.env.DOSAI_CMD;
@@ -389,7 +389,7 @@ export function getOSPackages(src) {
389
389
  // ignore errors
390
390
  }
391
391
  // Clean up
392
- if (tempDir && tempDir.startsWith(tmpdir())) {
392
+ if (tempDir?.startsWith(tmpdir())) {
393
393
  if (DEBUG_MODE) {
394
394
  console.log(`Cleaning up ${tempDir}`);
395
395
  }
@@ -451,13 +451,13 @@ export function getOSPackages(src) {
451
451
  break;
452
452
  }
453
453
  if (osReleaseData["VERSION_ID"]) {
454
- distro_id = distro_id + "-" + osReleaseData["VERSION_ID"];
454
+ distro_id = `${distro_id}-${osReleaseData["VERSION_ID"]}`;
455
455
  }
456
456
  const tmpDependencies = {};
457
457
  (tmpBom.dependencies || []).forEach((d) => {
458
458
  tmpDependencies[d.ref] = d.dependsOn;
459
459
  });
460
- if (tmpBom && tmpBom.components) {
460
+ if (tmpBom?.components) {
461
461
  for (const comp of tmpBom.components) {
462
462
  if (comp.purl) {
463
463
  // Retain go components alone from trivy
@@ -497,10 +497,10 @@ export function getOSPackages(src) {
497
497
  purlObj.namespace = group;
498
498
  }
499
499
  purlObj.qualifiers = purlObj.qualifiers || {};
500
- if (distro_id && distro_id.length) {
500
+ if (distro_id?.length) {
501
501
  purlObj.qualifiers["distro"] = distro_id;
502
502
  }
503
- if (distro_codename && distro_codename.length) {
503
+ if (distro_codename?.length) {
504
504
  purlObj.qualifiers["distro_name"] = distro_codename;
505
505
  }
506
506
  // Bug fix for mageia and oracle linux
@@ -509,7 +509,7 @@ export function getOSPackages(src) {
509
509
  purlObj["type"] = purl_type;
510
510
  purlObj["namespace"] = "";
511
511
  comp.group = "";
512
- if (comp.purl && comp.purl.includes(".mga")) {
512
+ if (comp.purl?.includes(".mga")) {
513
513
  purlObj["namespace"] = "mageia";
514
514
  comp.group = "mageia";
515
515
  purlObj.qualifiers["distro"] = "mageia";
@@ -529,7 +529,7 @@ export function getOSPackages(src) {
529
529
  allTypes.add(purlObj.type);
530
530
  }
531
531
  // Prefix distro codename for ubuntu
532
- if (purlObj.qualifiers && purlObj.qualifiers.distro) {
532
+ if (purlObj.qualifiers?.distro) {
533
533
  allTypes.add(purlObj.qualifiers.distro);
534
534
  if (OS_DISTRO_ALIAS[purlObj.qualifiers.distro]) {
535
535
  distro_codename =
@@ -537,7 +537,7 @@ export function getOSPackages(src) {
537
537
  } else if (group === "alpine") {
538
538
  const dtmpA = purlObj.qualifiers.distro.split(".");
539
539
  if (dtmpA && dtmpA.length > 2) {
540
- distro_codename = dtmpA[0] + "." + dtmpA[1];
540
+ distro_codename = `${dtmpA[0]}.${dtmpA[1]}`;
541
541
  }
542
542
  } else if (group === "photon") {
543
543
  const dtmpA = purlObj.qualifiers.distro.split("-");
@@ -699,7 +699,7 @@ const retrieveDependencies = (tmpDependencies, origBomRef, comp) => {
699
699
  export function executeOsQuery(query) {
700
700
  if (OSQUERY_BIN) {
701
701
  if (!query.endsWith(";")) {
702
- query = query + ";";
702
+ query = `${query};`;
703
703
  }
704
704
  const args = ["--json", query];
705
705
  // On darwin, we need to disable the safety check and run cdxgen with sudo
package/cbomutils.js CHANGED
@@ -26,10 +26,10 @@ export function collectOSCryptoLibs(options) {
26
26
  results,
27
27
  false,
28
28
  );
29
- if (dlist && dlist.length) {
29
+ if (dlist?.length) {
30
30
  osPkgsList = osPkgsList.concat(dlist);
31
31
  // Should we downgrade from cryptographic-asset to data for < 1.6 spec
32
- if (options && options.specVersion && options.specVersion < 1.6) {
32
+ if (options?.specVersion && options.specVersion < 1.6) {
33
33
  for (const apkg of osPkgsList) {
34
34
  if (apkg.type === "cryptographic-asset") {
35
35
  apkg.type = "data";
package/display.js CHANGED
@@ -17,8 +17,7 @@ export const printTable = (bomJson) => {
17
17
  return;
18
18
  }
19
19
  if (
20
- bomJson.metadata &&
21
- bomJson.metadata.component &&
20
+ bomJson.metadata?.component &&
22
21
  ["operating-system", "platform"].includes(bomJson.metadata.component.type)
23
22
  ) {
24
23
  return printOSTable(bomJson);
@@ -109,7 +108,7 @@ const locationComparator = (a, b) => {
109
108
  const tmpA = a.split("#");
110
109
  const tmpB = b.split("#");
111
110
  if (tmpA.length === 2 && tmpB.length === 2) {
112
- if (tmpA[0] == tmpB[0]) {
111
+ if (tmpA[0] === tmpB[0]) {
113
112
  return tmpA[1] - tmpB[1];
114
113
  }
115
114
  }
@@ -162,7 +161,7 @@ export const printCallStack = (bomJson) => {
162
161
  const frames = Array.from(
163
162
  new Set(
164
163
  comp.evidence.callstack.frames.map(
165
- (c) => `${c.fullFilename}${c.line ? "#" + c.line : ""}`,
164
+ (c) => `${c.fullFilename}${c.line ? `#${c.line}` : ""}`,
166
165
  ),
167
166
  ),
168
167
  ).sort(locationComparator);
@@ -199,7 +198,7 @@ export const printDependencyTree = (bomJson) => {
199
198
  }
200
199
  const depMap = {};
201
200
  for (const d of dependencies) {
202
- if (d.dependsOn && d.dependsOn.length) {
201
+ if (d.dependsOn?.length) {
203
202
  depMap[d.ref] = d.dependsOn.sort();
204
203
  }
205
204
  }
@@ -261,7 +260,7 @@ const recursePrint = (depMap, subtree, level, shownList, treeGraphics) => {
261
260
  level > 0
262
261
  ) {
263
262
  treeGraphics.push(
264
- `${levelPrefix(level, i == listToUse.length - 1)}${refStr}`,
263
+ `${levelPrefix(level, i === listToUse.length - 1)}${refStr}`,
265
264
  );
266
265
  shownList.push(refStr.toLowerCase());
267
266
  if (l && depMap[refStr]) {
@@ -299,7 +298,7 @@ export const printReachables = (sliceArtefacts) => {
299
298
  );
300
299
  const data = [["Package URL", "Reachable Flows"]];
301
300
  for (const apurl of Object.keys(sortedPurls)) {
302
- data.push([apurl, "" + sortedPurls[apurl]]);
301
+ data.push([apurl, `${sortedPurls[apurl]}`]);
303
302
  }
304
303
  const config = {
305
304
  header: {