@cyclonedx/cdxgen 9.7.5 → 9.8.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.
package/README.md CHANGED
@@ -2,17 +2,16 @@
2
2
 
3
3
  ![cdxgen logo](cdxgen.png)
4
4
 
5
- cdxgen is a cli tool, library, [REPL](./ADVANCED.md) and server to create a valid and compliant [CycloneDX][cyclonedx-homepage] Software Bill-of-Materials (SBOM) containing an aggregate of all project dependencies for c/c++, node.js, php, python, ruby, rust, java, .Net, dart, haskell, elixir, and Go projects in JSON format. CycloneDX 1.5 is a lightweight SBOM specification that is easily created, human and machine-readable, and simple to parse.
5
+ cdxgen is a cli tool, library, [REPL](./ADVANCED.md), and server to create a valid and compliant [CycloneDX][cyclonedx-homepage] Software Bill-of-Materials (SBOM) containing an aggregate of all project dependencies for c/c++, node.js, php, python, ruby, rust, java, .Net, dart, haskell, elixir, and Go projects in JSON format. CycloneDX 1.5 is a lightweight SBOM specification that is easily created, human and machine-readable, and simple to parse.
6
6
 
7
- When used with plugins, cdxgen could generate an OBoM for Linux docker images and even VMs running Linux or Windows operating system. cdxgen also includes a tool called `evinse` that can generate component evidences and SaaSBoM for some languages.
7
+ When used with plugins, cdxgen could generate an OBoM for Linux docker images and even VMs running Linux or Windows operating systems. cdxgen also includes an evinse tool to generate component evidence and SaaSBoM for some languages.
8
8
 
9
9
  NOTE:
10
10
 
11
11
  CycloneDX 1.5 specification is new and unsupported by many downstream tools. Use version 8.6.0 for 1.4 compatibility or pass the argument `--spec-version 1.4`.
12
12
 
13
13
  ## Why cdxgen?
14
-
15
- A typical application might comprise of several repos, components, and libraries linked together. Traditional techniques to generate a single SBoM per language or package manifest do not work in enterprise environments. So we built cdxgen - the universal polyglot SBoM generator!
14
+ A typical application might have several repos, components, and libraries. Traditional techniques to generate a single SBoM per language or package manifest do not work in enterprise environments. So we built cdxgen - the universal polyglot SBoM generator!
16
15
 
17
16
  <img src="./docs/why-cdxgen.jpg" alt="why cdxgen" width="256">
18
17
 
@@ -31,7 +30,7 @@ A typical application might comprise of several repos, components, and libraries
31
30
  | dart | pubspec.lock, pubspec.yaml | Only for pubspec.lock |
32
31
  | haskell | cabal.project.freeze | Yes |
33
32
  | elixir | mix.lock | Yes |
34
- | c/c++ | conan.lock, conanfile.txt, \*.cmake, CMakeLists.txt, meson.build | Yes only for conan.lock. Best effort basis for cmake without version numbers. |
33
+ | c/c++/Objective C/c++11 | conan.lock, conanfile.txt, \*.cmake, CMakeLists.txt, meson.build, codebase without package managers! | Yes only for conan.lock. Best effort basis for cmake without version numbers. |
35
34
  | clojure | Clojure CLI (deps.edn), Leiningen (project.clj) | Yes unless the files are parsed manually due to lack of clojure cli or leiningen command |
36
35
  | swift | Package.resolved, Package.swift (swiftpm) | Yes |
37
36
  | docker / oci image | All supported languages. Linux OS packages with plugins [4] | Best effort based on lock files |
@@ -56,28 +55,27 @@ NOTE:
56
55
 
57
56
  - Apache maven 3.x is required for parsing pom.xml
58
57
  - gradle or gradlew is required to parse gradle projects
59
- - sbt is required for parsing scala sbt projects. Only scala 2.10 + sbt 0.13.6+ and 2.12 + sbt 1.0+ is supported for now.
58
+ - sbt is required for parsing scala sbt projects. Only scala 2.10 + sbt 0.13.6+ and 2.12 + sbt 1.0+ are currently supported.
60
59
  - Alternatively, create a lock file using sbt-dependency-lock [plugin](https://github.com/stringbean/sbt-dependency-lock)
61
60
 
62
61
  Footnotes:
63
62
 
64
- - [1] - For multi-module application, the BoM file could include components that may not be included in the packaged war or ear file.
63
+ - [1] - For multi-module applications, the BoM file could include components not included in the packaged war or ear file.
65
64
  - [2] - Pip freeze is automatically performed to improve precision. Requires virtual environment.
66
- - [3] - Perform dotnet or nuget restore to generate project.assets.json. Without this file cdxgen would not include indirect dependencies.
67
- - [4] - See section on plugins
68
- - [5] - Powered by osquery. See section on plugins
65
+ - [3] - Perform dotnet or nuget restore to generate project.assets.json. Without this file, cdxgen would not include indirect dependencies.
66
+ - [4] - See the section on plugins
67
+ - [5] - Powered by osquery. See the section on plugins
69
68
 
70
69
  <img src="./docs/cdxgen-tree.jpg" alt="cdxgen tree" width="256">
71
70
 
72
71
  ### Automatic usage detection
72
+ For node.js projects, lock files are parsed initially, so the SBoM would include all dependencies, including dev ones. An AST parser powered by babel-parser is then used to detect packages that are imported and used by non-test code. Such imported packages would automatically set their scope property to `required` in the resulting SBoM. You can turn off this analysis by passing the argument `--no-babel`. Scope property would then be set based on the `dev` attribute in the lock file.
73
73
 
74
- For node.js projects, lock files are parsed initially so the SBoM would include all dependencies including dev dependencies. An AST parser powered by babel-parser is then used to detect packages that are imported and used by non-test code. Such imported packages would automatically have their `scope` property set to `required` in the resulting SBoM. By passing the argument `--no-babel`, you can disable this analysis. Scope property would then be set based on the `dev` attribute in the lock file.
75
-
76
- This attribute can be later used for various purposes. For example, [dep-scan](https://github.com/cyclonedx/dep-scan) use this attribute to prioritize vulnerabilities. Tools such dependency track, unfortunately, do not include this feature and hence might over-report the CVEs.
74
+ 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.
77
75
 
78
- By passing the argument `--required-only`, you can limit the SBoM to only include packages with the scope "required", commonly referred to as 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.
76
+ 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.
79
77
 
80
- For go, `go mod why` command is used to identify required packages. For php, composer lock file is used to distinguish required (packages) from optional (packages-dev).
78
+ 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).
81
79
 
82
80
  ## Usage
83
81
 
@@ -99,7 +97,7 @@ $ brew install cdxgen
99
97
  Deno install is also supported.
100
98
 
101
99
  ```shell
102
- deno install --allow-read --allow-env --allow-run --allow-sys=uid,systemMemoryInfo --allow-write --allow-net -n cdxgen "npm:@cyclonedx/cdxgen/cdxgen"
100
+ deno install --allow-read --allow-env --allow-run --allow-sys=uid,systemMemoryInfo,gid --allow-write --allow-net -n cdxgen "npm:@cyclonedx/cdxgen/cdxgen"
103
101
  ```
104
102
 
105
103
  You can also use the cdxgen container image
@@ -147,6 +145,7 @@ $ cdxgen -h
147
145
  --project-version Dependency track project version [default: ""]
148
146
  --project-id Dependency track project id. Either provide the i
149
147
  d or the project name and version together
148
+ --parent-project-id Dependency track parent project id
150
149
  --required-only Include only the packages with required scope on
151
150
  the SBoM. [boolean]
152
151
  --fail-on-error Fail if any dependency extractor fails. [boolean]
@@ -174,7 +173,7 @@ $ cdxgen -h
174
173
  -v, --version Show version number [boolean]
175
174
  ```
176
175
 
177
- All boolean arguments accepts `--no` prefix to toggle the behavior.
176
+ All boolean arguments accept `--no` prefix to toggle the behavior.
178
177
 
179
178
  ## Example
180
179
 
@@ -184,7 +183,7 @@ Minimal example.
184
183
  cdxgen -o bom.json
185
184
  ```
186
185
 
187
- For a java project. This would automatically detect maven, gradle or sbt and build bom accordingly
186
+ For a java project. cdxgen would automatically detect maven, gradle, or sbt and build bom accordingly
188
187
 
189
188
  ```shell
190
189
  cdxgen -t java -o bom.json
@@ -202,7 +201,7 @@ To recursively generate a single BoM for all languages pass `-r` argument.
202
201
  cdxgen -r -o bom.json
203
202
  ```
204
203
 
205
- To generate SBoM for an older specification version such as 1.4, pass the version using the `--spec-version` argument.
204
+ To generate SBoM for an older specification version, such as 1.4, pass the version number using the `--spec-version` argument.
206
205
 
207
206
  ```shell
208
207
  cdxgen -r -o bom.json --spec-version 1.4
@@ -215,15 +214,15 @@ To generate SBoM for C or Python, ensure Java >= 17 is installed.
215
214
  cdxgen -t c -o bom.json
216
215
  ```
217
216
 
218
- NOTE: cdxgen is known to freeze with Java 8 or 11 so ensure >= 17 is installed and JAVA_HOME environment variable configured correctly. If in doubt, use the cdxgen container image.
217
+ NOTE: cdxgen is known to freeze with Java 8 or 11, so ensure >= 17 is installed and JAVA_HOME environment variable is configured correctly. If in doubt, use the cdxgen container image.
219
218
 
220
219
  ## Universal SBoM
221
220
 
222
- By passing the type `-t universal`, cdxgen could be forced to opportunistically collect as many components and services as possible by scanning all package, container and kubernetes manifests. The resulting SBoM could have over thousand components thus requiring additional triaging before use with traditional SCA tools.
221
+ By passing the type argument `-t universal`, cdxgen could be forced to opportunistically collect as many components and services as possible by scanning all package, container, and Kubernetes manifests. The resulting SBoM could have over a thousand components, thus requiring additional triaging before use with traditional SCA tools.
223
222
 
224
223
  ## SBoM server
225
224
 
226
- Invoke cdxgen with `--server` argument to run it in a server mode. By default, it listens to port `9090` which can be customized with the arguments `--server-host` and `--server-port`.
225
+ Invoke cdxgen with `--server` argument to run it in server mode. By default, it listens to port `9090`, which can be customized with the arguments `--server-host` and `--server-port`.
227
226
 
228
227
  ```shell
229
228
  cdxgen --server
@@ -235,7 +234,7 @@ Or use the container image.
235
234
  docker run --rm -v /tmp:/tmp -p 9090:9090 -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen -r /app --server --server-host 0.0.0.0
236
235
  ```
237
236
 
238
- Use curl or your favourite tool to pass arguments to the `/sbom` route.
237
+ Use curl or your favorite tool to pass arguments to the `/sbom` route.
239
238
 
240
239
  ### Scanning a local path
241
240
 
@@ -273,7 +272,7 @@ cdxgen app.war
273
272
 
274
273
  ## Resolving class names
275
274
 
276
- Sometimes it is necessary to resolve class names contained in jar files. By passing an optional argument `--resolve-class`, it is possible to get cdxgen create a separate mapping file with the jar name (including the version) as the key and class names list as a value.
275
+ Sometimes, it is necessary to resolve class names contained in jar files. By passing an optional argument `--resolve-class`, it is possible to get cdxgen to create a separate mapping file with the jar name (including the version) as the key and class names list as a value.
277
276
 
278
277
  ```shell
279
278
  cdxgen -t java --resolve-class -o bom.json
@@ -283,7 +282,7 @@ This would create a bom.json.map file with the jar - class name mapping. Refer t
283
282
 
284
283
  ## Resolving licenses
285
284
 
286
- cdxgen can automatically query the public registries such as maven or npm or nuget to resolve the package licenses. This is a time consuming operation and is disabled by default. To enable, set the environment variable `FETCH_LICENSE` to `true` as shown.
285
+ cdxgen can automatically query public registries such as maven, npm, or nuget to resolve the package licenses. This is a time-consuming operation and is disabled by default. To enable, set the environment variable `FETCH_LICENSE` to `true`, as shown.
287
286
 
288
287
  ```bash
289
288
  export FETCH_LICENSE=true
@@ -300,6 +299,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
300
299
  - Gradle
301
300
  - Scala SBT
302
301
  - Python (requirements.txt, setup.py, pyproject.toml, poetry.lock)
302
+ - csharp (projects.assets.json)
303
303
 
304
304
  ## Environment variables
305
305
 
@@ -369,15 +369,11 @@ podman save -q --format oci-archive -o /tmp/slim.tar shiftleft/scan-slim
369
369
  cdxgen /tmp/slim.tar -o /tmp/bom.json -t docker
370
370
  ```
371
371
 
372
- NOTE:
373
-
374
- - Only application related packages are collected by cdxgen. Support for OS installed packages is coming soon.
375
-
376
372
  ### Podman in rootless mode
377
373
 
378
374
  Setup podman in either [rootless](https://github.com/containers/podman/blob/master/docs/tutorials/rootless_tutorial.md) or [remote](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md) mode
379
375
 
380
- On Linux, do not forget to start the podman socket which is required for API access.
376
+ Do not forget to start the podman socket required for API access on Linux.
381
377
 
382
378
  ```shell
383
379
  systemctl --user enable --now podman.socket
@@ -395,27 +391,27 @@ obom
395
391
  # cdxgen -t os
396
392
  ```
397
393
 
398
- This feature is powered by osquery which is [installed](https://github.com/cyclonedx/cdxgen-plugins-bin/blob/main/build.sh#L8) along with the binary plugins. cdxgen would opportunistically try to detect as many components, apps and extensions as possible using the [default queries](queries.json). The process would take several minutes and result in an SBoM file with thousands of components of various types such as operating-system, device-drivers, files, and data.
394
+ This feature is powered by osquery, which is [installed](https://github.com/cyclonedx/cdxgen-plugins-bin/blob/main/build.sh#L8) along with the binary plugins. cdxgen would opportunistically try to detect as many components, apps, and extensions as possible using the [default queries](queries.json). The process would take several minutes and result in an SBoM file with thousands of components of various types, such as operating-system, device-drivers, files, and data.
399
395
 
400
396
  ## Generating SaaSBoM and component evidences
401
397
 
402
398
  See [evinse mode](./ADVANCED.md) in the advanced documentation.
403
399
 
404
- ## SBoM signing
400
+ ## BoM signing
405
401
 
406
- cdxgen can sign the generated SBoM json file to increase authenticity and non-repudiation capabilities. To enable this, set the following environment variables.
402
+ cdxgen can sign the generated BoM json file to increase authenticity and non-repudiation capabilities. To enable this, set the following environment variables.
407
403
 
408
404
  - SBOM_SIGN_ALGORITHM: Algorithm. Example: RS512
409
405
  - SBOM_SIGN_PRIVATE_KEY: Location to the RSA private key
410
406
  - SBOM_SIGN_PUBLIC_KEY: Optional. Location to the RSA public key
411
407
 
412
- To generate test public/private key pairs, you can run cdxgen by passing the argument `--generate-key-and-sign`. The generated json file would have an attribute called `signature` which could be used for validation. [jwt.io](https://jwt.io) is a known site that could be used for such signature validation.
408
+ To generate test public/private key pairs, you can run cdxgen by passing the argument `--generate-key-and-sign`. The generated json file would have an attribute called `signature`, which could be used for validation. [jwt.io](https://jwt.io) is a known site that could be used for such signature validation.
413
409
 
414
410
  ![SBoM signing](sbom-sign.jpg)
415
411
 
416
412
  ### Verifying the signature
417
413
 
418
- Use the bundled `cdx-verify` command which supports verifying a single signature added at the bom level.
414
+ Use the bundled `cdx-verify` command, which supports verifying a single signature added at the bom level.
419
415
 
420
416
  ```shell
421
417
  npm install -g @cyclonedx/cdxgen
@@ -447,7 +443,7 @@ if (validationResult) {
447
443
 
448
444
  ## Automatic services detection
449
445
 
450
- cdxgen can automatically detect names of services from YAML manifests such as docker-compose or Kubernetes or Skaffold manifests. These would be populated under the `services` attribute in the generated SBoM. With [evinse](./ADVANCED.md), additional services could be detected by parsing common annotations from the source code.
446
+ cdxgen can automatically detect names of services from YAML manifests such as docker-compose, Kubernetes, or Skaffold manifests. These would be populated under the `services` attribute in the generated SBoM. With [evinse](./ADVANCED.md), additional services could be detected by parsing common annotations from the source code.
451
447
 
452
448
  ## Conversion to SPDX format
453
449
 
@@ -470,7 +466,7 @@ Minimal example:
470
466
  import { createBom, submitBom } from "npm:@cyclonedx/cdxgen@^9.0.1";
471
467
  ```
472
468
 
473
- See the [Deno Readme](./contrib/deno/README.md) for detailed instruction.
469
+ See the [Deno Readme](./contrib/deno/README.md) for detailed instructions.
474
470
 
475
471
  ```javascript
476
472
  import { createBom, submitBom } from "@cyclonedx/cdxgen";
@@ -486,7 +482,7 @@ Refer to the [permissions document](./docs/PERMISSIONS.md)
486
482
 
487
483
  ## Contributing
488
484
 
489
- Follow the usual PR process but prior to raising a PR run the following commands.
485
+ Follow the usual PR process, but before raising a PR, run the following commands.
490
486
 
491
487
  ```bash
492
488
  npm run lint
@@ -496,4 +492,4 @@ npm test
496
492
 
497
493
  ## Enterprise support
498
494
 
499
- Enterprise support including custom development and integration services are available via [AppThreat Ltd](https://www.appthreat.com). Free community support is also available via [discord](https://discord.gg/tmmtjCEHNV).
495
+ Enterprise support, including custom development and integration services, is available via [AppThreat Ltd](https://www.appthreat.com). Free community support is also available via [Discord](https://discord.gg/tmmtjCEHNV).
package/bin/cdxgen.js CHANGED
@@ -73,6 +73,9 @@ const args = yargs(hideBin(process.argv))
73
73
  description:
74
74
  "Dependency track project id. Either provide the id or the project name and version together"
75
75
  })
76
+ .option("parent-project-id", {
77
+ description: "Dependency track parent project id"
78
+ })
76
79
  .option("required-only", {
77
80
  type: "boolean",
78
81
  description: "Include only the packages with required scope on the SBoM."
package/binary.js CHANGED
@@ -3,7 +3,7 @@ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
3
3
  import { join, dirname, basename } from "node:path";
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { PackageURL } from "packageurl-js";
6
- import { DEBUG_MODE } from "./utils.js";
6
+ import { DEBUG_MODE, findLicenseId } from "./utils.js";
7
7
 
8
8
  import { fileURLToPath } from "node:url";
9
9
 
@@ -509,7 +509,34 @@ export const getOSPackages = (src) => {
509
509
  Array.isArray(comp.licenses) &&
510
510
  comp.licenses.length
511
511
  ) {
512
- comp.licenses = [comp.licenses[0]];
512
+ const newLicenses = [];
513
+ for (const alic of comp.licenses) {
514
+ if (alic.license.name) {
515
+ // Licenses array can either be made of expressions or id/name but not both
516
+ if (
517
+ comp.licenses.length == 1 &&
518
+ (alic.license.name.toUpperCase().includes(" AND ") ||
519
+ alic.license.name.toUpperCase().includes(" OR "))
520
+ ) {
521
+ newLicenses.push({ expression: alic.license.name });
522
+ } else {
523
+ const possibleId = findLicenseId(alic.license.name);
524
+ if (possibleId !== alic.license.name) {
525
+ newLicenses.push({ license: { id: possibleId } });
526
+ } else {
527
+ newLicenses.push({
528
+ license: { name: alic.license.name }
529
+ });
530
+ }
531
+ }
532
+ } else if (
533
+ Object.keys(alic).length &&
534
+ Object.keys(alic.license).length
535
+ ) {
536
+ newLicenses.push(alic);
537
+ }
538
+ }
539
+ comp.licenses = newLicenses;
513
540
  }
514
541
  // Fix hashes
515
542
  if (
@@ -58,6 +58,18 @@
58
58
  "BSD (3-clause)"
59
59
  ]
60
60
  },
61
+ {
62
+ "exp": "BSD-4-Clause",
63
+ "names": [
64
+ "BSD 4 Clause",
65
+ "BSD 4-Clause",
66
+ "BSD-4-Clause",
67
+ "BSD 4-Clause License",
68
+ "The BSD 4-Clause License",
69
+ "BSD 4-Clause \"New\" or \"Revised\" License (BSD-4-Clause)",
70
+ "BSD (4-clause)"
71
+ ]
72
+ },
61
73
  {
62
74
  "exp": "CDDL-1.0",
63
75
  "names": [
@@ -177,6 +189,8 @@
177
189
  {
178
190
  "exp": "GPL-2.0-only",
179
191
  "names": [
192
+ "GPLv2",
193
+ "GPL-2.0",
180
194
  "GNU General Public License (GPL) version 2",
181
195
  "GNU General Public License (GPL) version 2.0",
182
196
  "GNU General Public License v2",
@@ -216,12 +230,14 @@
216
230
  "GNU General Public License v3",
217
231
  "GNU General Public License v3.0",
218
232
  "GNU General Public License as published by the Free Software Foundation, version 3.",
219
- "GPL-3"
233
+ "GPL-3",
234
+ "GPL-3.0"
220
235
  ]
221
236
  },
222
237
  {
223
238
  "exp": "GPL-3.0-or-later",
224
239
  "names": [
240
+ "GPLv3+",
225
241
  "GNU General Public License (GPL) version 3, or any later version",
226
242
  "GNU General Public License (GPL) version 3.0, or any later version",
227
243
  "GNU General Public License v3 or later",
package/index.js CHANGED
@@ -97,7 +97,9 @@ import {
97
97
  addEvidenceForImports,
98
98
  parseSbtTree,
99
99
  parseCmakeLikeFile,
100
- getCppModules
100
+ getCppModules,
101
+ FETCH_LICENSE,
102
+ getNugetMetadata
101
103
  } from "./utils.js";
102
104
  import { spawnSync } from "node:child_process";
103
105
  import { fileURLToPath } from "node:url";
@@ -653,8 +655,8 @@ function addComponent(
653
655
  }
654
656
  if (!isRootPkg) {
655
657
  const pkgIdentifier = parsePackageJsonName(pkg.name);
656
- const author = pkg.author || "";
657
- const publisher = pkg.publisher || "";
658
+ const author = pkg.author || undefined;
659
+ const publisher = pkg.publisher || undefined;
658
660
  let group = pkg.group || pkgIdentifier.scope;
659
661
  // Create empty group
660
662
  group = group || "";
@@ -682,7 +684,7 @@ function addComponent(
682
684
  purlString = decodeURIComponent(purlString);
683
685
  let description = { "#cdata": pkg.description };
684
686
  if (format === "json") {
685
- description = pkg.description || "";
687
+ description = pkg.description || undefined;
686
688
  }
687
689
  let compScope = pkg.scope;
688
690
  if (allImports) {
@@ -1265,7 +1267,7 @@ export const createJavaBom = async (path, options) => {
1265
1267
  );
1266
1268
  } else {
1267
1269
  console.log(
1268
- "1. Java version requirement: cdxgen container image bundles Java 20 with maven 3.9 which might be incompatible."
1270
+ "1. Java version requirement: cdxgen container image bundles Java 21 with maven 3.9 which might be incompatible."
1269
1271
  );
1270
1272
  }
1271
1273
  console.log(
@@ -2983,7 +2985,13 @@ export const createCppBom = (path, options) => {
2983
2985
  }
2984
2986
  }
2985
2987
  }
2986
- if (!["docker", "oci", "os"].includes(options.projectType)) {
2988
+ // The need for java >= 17 with atom is causing confusions since there could be C projects
2989
+ // inside of other project types. So we currently limit this analyis only when -t argument
2990
+ // is used.
2991
+ if (
2992
+ !["docker", "oci", "os"].includes(options.projectType) &&
2993
+ (!options.createMultiXBom || options.deep)
2994
+ ) {
2987
2995
  let osPkgsList = [];
2988
2996
  // Case 1: Development libraries installed in this OS environment might be used for build
2989
2997
  // We collect OS packages with the word dev in the name using osquery here
@@ -3010,10 +3018,12 @@ export const createCppBom = (path, options) => {
3010
3018
  pkgList = pkgList.concat(dlist);
3011
3019
  }
3012
3020
  }
3013
- if (!parentComponent) {
3014
- parentComponent = createDefaultParentComponent(path, "conan", options);
3021
+ if (!options.createMultiXBom) {
3022
+ if (!parentComponent) {
3023
+ parentComponent = createDefaultParentComponent(path, "conan", options);
3024
+ }
3025
+ options.parentComponent = parentComponent;
3015
3026
  }
3016
- options.parentComponent = parentComponent;
3017
3027
  return buildBomNSData(options, pkgList, "conan", {
3018
3028
  src: path,
3019
3029
  parentComponent
@@ -3913,39 +3923,17 @@ export const createRubyBom = async (path, options) => {
3913
3923
  return {};
3914
3924
  };
3915
3925
 
3916
- const removeDuplicates = (pkgList, dependencies) => {
3917
- const uniqueItems = {};
3918
- const uniqueRefs = new Set();
3919
- const newPkgList = [];
3920
- const newDependencies = [];
3921
-
3922
- for (const item of pkgList) {
3923
- if (item) {
3924
- const { name, version } = item;
3925
- const key = `${name}-${version}`;
3926
- if (!uniqueItems[key] && key) {
3927
- uniqueItems[key] = item;
3928
- newPkgList.push(item);
3929
- }
3930
- }
3931
- }
3932
-
3933
- for (const item of dependencies) {
3934
- const { ref } = item;
3935
- if (!uniqueRefs.has(ref)) {
3936
- uniqueRefs.add(ref);
3937
- newDependencies.push(item);
3938
- }
3939
- }
3940
- return [newPkgList, newDependencies];
3941
- };
3942
3926
  /**
3943
3927
  * Function to create bom string for csharp projects
3944
3928
  *
3945
3929
  * @param path to the project
3946
3930
  * @param options Parse options from the cli
3947
3931
  */
3948
- export const createCsharpBom = async (path, options) => {
3932
+ export const createCsharpBom = async (
3933
+ path,
3934
+ options,
3935
+ parentComponent = undefined
3936
+ ) => {
3949
3937
  let manifestFiles = [];
3950
3938
  let pkgData = undefined;
3951
3939
  let dependencies = [];
@@ -3970,7 +3958,7 @@ export const createCsharpBom = async (path, options) => {
3970
3958
  (options.multiProject ? "**/" : "") + "*.nupkg"
3971
3959
  );
3972
3960
  let pkgList = [];
3973
- if (nupkgFiles.length) {
3961
+ if (nupkgFiles.length && projAssetsFiles.length === 0) {
3974
3962
  manifestFiles = manifestFiles.concat(nupkgFiles);
3975
3963
  for (const nf of nupkgFiles) {
3976
3964
  if (DEBUG_MODE) {
@@ -4048,17 +4036,27 @@ export const createCsharpBom = async (path, options) => {
4048
4036
  }
4049
4037
  }
4050
4038
  }
4039
+ if (!parentComponent) {
4040
+ parentComponent = createDefaultParentComponent(path, options.type, options);
4041
+ }
4051
4042
  if (pkgList.length) {
4052
- const uniquePkg = removeDuplicates(pkgList, dependencies);
4053
- dependencies = uniquePkg[1];
4054
- pkgList = uniquePkg[0];
4055
- return buildBomNSData(options, pkgList, "nuget", {
4056
- src: path,
4057
- filename: manifestFiles.join(", "),
4058
- dependencies
4059
- });
4043
+ dependencies = mergeDependencies(dependencies, [], parentComponent);
4044
+ pkgList = trimComponents(pkgList, "json");
4060
4045
  }
4061
- return {};
4046
+ if (FETCH_LICENSE) {
4047
+ const retMap = await getNugetMetadata(pkgList, dependencies);
4048
+ if (retMap.dependencies && retMap.dependencies.length) {
4049
+ dependencies = dependencies.concat(retMap.dependencies);
4050
+ }
4051
+ dependencies = mergeDependencies(dependencies, [], parentComponent);
4052
+ pkgList = trimComponents(pkgList, "json");
4053
+ }
4054
+ return buildBomNSData(options, pkgList, "nuget", {
4055
+ src: path,
4056
+ filename: manifestFiles.join(", "),
4057
+ dependencies,
4058
+ parentComponent
4059
+ });
4062
4060
  };
4063
4061
 
4064
4062
  export const mergeDependencies = (
@@ -4180,6 +4178,7 @@ export const createMultiXBom = async (pathList, options) => {
4180
4178
  let bomData = undefined;
4181
4179
  let parentComponent = determineParentComponent(options) || {};
4182
4180
  let parentSubComponents = [];
4181
+ options.createMultiXBom = true;
4183
4182
  if (
4184
4183
  ["docker", "oci", "container"].includes(options.projectType) &&
4185
4184
  options.allLayersExplodedDir
@@ -4404,7 +4403,7 @@ export const createMultiXBom = async (pathList, options) => {
4404
4403
  listComponents(options, {}, bomData.bomJson.components, "gem", "xml")
4405
4404
  );
4406
4405
  }
4407
- bomData = await createCsharpBom(path, options);
4406
+ bomData = await createCsharpBom(path, options, parentComponent);
4408
4407
  if (bomData && bomData.bomJson && bomData.bomJson.components) {
4409
4408
  if (DEBUG_MODE) {
4410
4409
  console.log(
@@ -5229,6 +5228,9 @@ export async function submitBom(args, bomContents) {
5229
5228
  autoCreate: "true",
5230
5229
  bom: encodedBomContents
5231
5230
  };
5231
+ if (typeof args.parentProjectId !== "undefined") {
5232
+ bomPayload.parentUUID = args.parentProjectId;
5233
+ }
5232
5234
  if (DEBUG_MODE) {
5233
5235
  console.log(
5234
5236
  "Submitting BOM to",
@@ -5265,13 +5267,7 @@ export async function submitBom(args, bomContents) {
5265
5267
  "Content-Type": "application/json",
5266
5268
  "user-agent": `@CycloneDX/cdxgen ${_version}`
5267
5269
  },
5268
- json: {
5269
- project: args.projectId,
5270
- projectName: args.projectName,
5271
- projectVersion: projectVersion,
5272
- autoCreate: "true",
5273
- bom: encodedBomContents
5274
- },
5270
+ json: bomPayload,
5275
5271
  responseType: "json"
5276
5272
  }).json();
5277
5273
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyclonedx/cdxgen",
3
- "version": "9.7.5",
3
+ "version": "9.8.1",
4
4
  "description": "Creates CycloneDX Software Bill-of-Materials (SBOM) from source or container image",
5
5
  "homepage": "http://github.com/cyclonedx/cdxgen",
6
6
  "author": "Prabhu Subramanian <prabhu@appthreat.com>",
@@ -55,7 +55,8 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@babel/parser": "^7.22.16",
58
- "@babel/traverse": "^7.22.17",
58
+ "@babel/traverse": "^7.22.19",
59
+ "@npmcli/arborist": "^7.1.0",
59
60
  "ajv": "^8.12.0",
60
61
  "ajv-formats": "^2.1.1",
61
62
  "cheerio": "^1.0.0-rc.12",
@@ -74,13 +75,13 @@
74
75
  "ssri": "^10.0.4",
75
76
  "table": "^6.8.1",
76
77
  "tar": "^6.2.0",
77
- "uuid": "^9.0.0",
78
+ "uuid": "^9.0.1",
78
79
  "xml-js": "^1.6.11",
79
80
  "xmlbuilder": "^15.1.1",
80
81
  "yargs": "^17.7.2"
81
82
  },
82
83
  "optionalDependencies": {
83
- "@appthreat/atom": "^1.1.15",
84
+ "@appthreat/atom": "^1.2.0",
84
85
  "@cyclonedx/cdxgen-plugins-bin": "^1.4.0",
85
86
  "@cyclonedx/cdxgen-plugins-bin-arm64": "^1.4.0",
86
87
  "@cyclonedx/cdxgen-plugins-bin-ppc64": "^1.4.0",
@@ -98,8 +99,8 @@
98
99
  ],
99
100
  "devDependencies": {
100
101
  "caxa": "^3.0.1",
101
- "eslint": "^8.48.0",
102
- "jest": "^29.6.4",
102
+ "eslint": "^8.49.0",
103
+ "jest": "^29.7.0",
103
104
  "prettier": "3.0.3"
104
105
  }
105
106
  }