@cyclonedx/cdxgen 11.2.2 → 11.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -147,7 +147,8 @@ Options:
147
147
  --server-host Listen address [default: "127.0.0.1"]
148
148
  --server-port Listen port [default: "9090"]
149
149
  --install-deps Install dependencies automatically for some projects. Defaults to true but disabled for c
150
- ontainers and oci scans. Use --no-install-deps to disable this feature. [boolean]
150
+ ontainers and oci scans. Use --no-install-deps to disable this feature.
151
+ [boolean] [default: true]
151
152
  --validate Validate the generated SBOM using json schema. Defaults to true. Pass --no-validate to di
152
153
  sable. [boolean] [default: true]
153
154
  --evidence Generate SBOM with evidence for supported languages. [boolean] [default: false]
@@ -170,6 +171,7 @@ Options:
170
171
  luated against or attested to.
171
172
  [array] [choices: "asvs-5.0", "asvs-4.0.3", "bsimm-v13", "masvs-2.0.0", "nist_ssdf-1.1", "pcissc-secure-slc-1.1", "scv
172
173
  s-1.0.0", "ssaf-DRAFT-2023-11"]
174
+ --json-pretty Pretty-print the generated BOM json. [boolean] [default: false]
173
175
  --min-confidence Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% con
174
176
  fidence. [number] [default: 0]
175
177
  --technique Analysis technique to use
package/bin/cdxgen.js CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  import { thoughtEnd, thoughtLog } from "../lib/helpers/logger.js";
25
25
  import {
26
26
  ATOM_DB,
27
+ DEBUG_MODE,
27
28
  dirNameStr,
28
29
  getTmpDir,
29
30
  isMac,
@@ -55,10 +56,6 @@ if (configPath) {
55
56
  }
56
57
  }
57
58
 
58
- let url = import.meta.url;
59
- if (!url.startsWith("file://")) {
60
- url = new URL(`file://${import.meta.url}`).toString();
61
- }
62
59
  const dirName = dirNameStr;
63
60
 
64
61
  import yargs from "yargs";
@@ -221,6 +218,7 @@ const args = yargs(hideBin(process.argv))
221
218
  description: "CycloneDX Specification version to use. Defaults to 1.6",
222
219
  default: 1.6,
223
220
  type: "number",
221
+ choices: [1.4, 1.5, 1.6],
224
222
  })
225
223
  .option("filter", {
226
224
  description:
@@ -303,6 +301,11 @@ const args = yargs(hideBin(process.argv))
303
301
  description:
304
302
  "Do not show the donation banner. Set this attribute if you are an active sponsor for OWASP CycloneDX.",
305
303
  })
304
+ .option("json-pretty", {
305
+ type: "boolean",
306
+ default: DEBUG_MODE,
307
+ description: "Pretty-print the generated BOM json.",
308
+ })
306
309
  .option("feature-flags", {
307
310
  description: "Experimental feature flags to enable. Advanced users only.",
308
311
  hidden: true,
@@ -442,11 +445,23 @@ if (!options.projectType) {
442
445
  "Ok, the user wants me to identify all the project types and generate a consolidated BOM document.",
443
446
  );
444
447
  }
445
- if (process.argv[1].includes("cbom")) {
446
- thoughtLog(
447
- "Ok, the user wants to generate Cryptographic Bill-of-Materials (CBOM).",
448
- );
449
- options.includeCrypto = true;
448
+ // Handle dedicated cbom and saasbom commands
449
+ if (["cbom", "saasbom"].includes(process.argv[1])) {
450
+ if (process.argv[1].includes("cbom")) {
451
+ thoughtLog(
452
+ "Ok, the user wants to generate Cryptographic Bill-of-Materials (CBOM).",
453
+ );
454
+ options.includeCrypto = true;
455
+ } else if (process.argv[1].includes("saasbom")) {
456
+ thoughtLog(
457
+ "Ok, the user wants to generate a Software as a Service Bill-of-Materials (SaaSBOM). I should carefully collect the services, endpoints, and data flows.",
458
+ );
459
+ if (process.env?.CDXGEN_IN_CONTAINER !== "true") {
460
+ thoughtLog(
461
+ "Wait, I'm not running in a container. This means the chances of successfully collecting this inventory are quite low. Perhaps this is an advanced user who has set up atom and atom-tools already 🤔?",
462
+ );
463
+ }
464
+ }
450
465
  options.evidence = true;
451
466
  options.specVersion = 1.6;
452
467
  options.deep = true;
@@ -693,7 +708,7 @@ const checkPermissions = (filePath, options) => {
693
708
  "usages-slices-file",
694
709
  "reachables-slices-file",
695
710
  ];
696
- if (options?.type?.includes("swift")) {
711
+ if (options?.type?.includes("swift") || options?.type?.includes("scala")) {
697
712
  slicesFilesKeys.push("semantics-slices-file");
698
713
  }
699
714
  for (const sf of slicesFilesKeys) {
@@ -798,7 +813,11 @@ const checkPermissions = (filePath, options) => {
798
813
  fs.writeFileSync(jsonFile, bomNSData.bomJson);
799
814
  jsonPayload = bomNSData.bomJson;
800
815
  } else {
801
- jsonPayload = JSON.stringify(bomNSData.bomJson, null, null);
816
+ jsonPayload = JSON.stringify(
817
+ bomNSData.bomJson,
818
+ null,
819
+ options.jsonPretty ? 2 : null,
820
+ );
802
821
  fs.writeFileSync(jsonFile, jsonPayload);
803
822
  if (jsonFile.endsWith("bom.json")) {
804
823
  thoughtLog(
@@ -900,7 +919,11 @@ const checkPermissions = (filePath, options) => {
900
919
  bomJsonUnsignedObj.signature = signatureBlock;
901
920
  fs.writeFileSync(
902
921
  jsonFile,
903
- JSON.stringify(bomJsonUnsignedObj, null, null),
922
+ JSON.stringify(
923
+ bomJsonUnsignedObj,
924
+ null,
925
+ options.jsonPretty ? 2 : null,
926
+ ),
904
927
  );
905
928
  thoughtLog(`Signing the BOM file "${jsonFile}".`);
906
929
  if (publicKeyFile) {
@@ -937,7 +960,9 @@ const checkPermissions = (filePath, options) => {
937
960
  }
938
961
  } else if (!options.print) {
939
962
  if (bomNSData.bomJson) {
940
- console.log(JSON.stringify(bomNSData.bomJson, null, 2));
963
+ console.log(
964
+ JSON.stringify(bomNSData.bomJson, null, options.jsonPretty ? 2 : null),
965
+ );
941
966
  } else {
942
967
  console.log("Unable to produce BOM for", filePath);
943
968
  console.log("Try running the command with -t <type> or -r argument");
@@ -967,6 +992,7 @@ const checkPermissions = (filePath, options) => {
967
992
  includeCrypto: options.includeCrypto,
968
993
  specVersion: options.specVersion,
969
994
  profile: options.profile,
995
+ jsonPretty: options.jsonPretty,
970
996
  };
971
997
  const dbObjMap = await evinserModule.prepareDB(evinseOptions);
972
998
  if (dbObjMap) {
@@ -1003,6 +1029,7 @@ const checkPermissions = (filePath, options) => {
1003
1029
  await submitBom(options, bomNSData.bomJson);
1004
1030
  } catch (err) {
1005
1031
  console.log(err);
1032
+ process.exit(1);
1006
1033
  }
1007
1034
  }
1008
1035
  // Protobuf serialization
package/bin/evinse.js CHANGED
@@ -73,6 +73,7 @@ const args = yargs(hideBin(process.argv))
73
73
  "swift",
74
74
  "ios",
75
75
  "ruby",
76
+ "scala",
76
77
  ],
77
78
  })
78
79
  .option("db-path", {
@@ -127,7 +128,10 @@ const args = yargs(hideBin(process.argv))
127
128
  .option("semantics-slices-file", {
128
129
  description: "Use an existing semantics slices file.",
129
130
  default: "semantics.slices.json",
130
- hidden: true,
131
+ })
132
+ .option("openapi-spec-file", {
133
+ description: "Use an existing openapi specification file (SaaSBOM).",
134
+ default: "openapi.json",
131
135
  })
132
136
  .option("print", {
133
137
  alias: "p",
package/bin/verify.js CHANGED
@@ -9,10 +9,6 @@ import yargs from "yargs";
9
9
  import { hideBin } from "yargs/helpers";
10
10
  import { dirNameStr } from "../lib/helpers/utils.js";
11
11
 
12
- let url = import.meta.url;
13
- if (!url.startsWith("file://")) {
14
- url = new URL(`file://${import.meta.url}`).toString();
15
- }
16
12
  const dirName = dirNameStr;
17
13
 
18
14
  const args = yargs(hideBin(process.argv))
package/index.cjs ADDED
@@ -0,0 +1,15 @@
1
+ // this file is a wrapper of ./lib/cli/index.js that can be used by commonjs projects importing this module
2
+ // that prefer to use require instead of await import()
3
+ const importPromise = import("./lib/cli/index.js");
4
+
5
+ module.exports = new Proxy(
6
+ {},
7
+ {
8
+ get:
9
+ (_, prop) =>
10
+ async (...args) => {
11
+ const mod = await importPromise;
12
+ return typeof mod[prop] === "function" ? mod[prop](...args) : mod[prop];
13
+ },
14
+ },
15
+ );
package/lib/cli/index.js CHANGED
@@ -73,6 +73,7 @@ import {
73
73
  getGradleCommand,
74
74
  getLicenses,
75
75
  getMavenCommand,
76
+ getMillCommand,
76
77
  getMvnMetadata,
77
78
  getNugetMetadata,
78
79
  getPipFrozenTree,
@@ -130,6 +131,7 @@ import {
130
131
  parseLeiningenData,
131
132
  parseMakeDFile,
132
133
  parseMavenTree,
134
+ parseMillDependency,
133
135
  parseMinJs,
134
136
  parseMixLockData,
135
137
  parseNodeShrinkwrap,
@@ -180,10 +182,6 @@ import {
180
182
  parseImageName,
181
183
  } from "../managers/docker.js";
182
184
 
183
- let url = import.meta.url;
184
- if (!url.startsWith("file://")) {
185
- url = new URL(`file://${import.meta.url}`).toString();
186
- }
187
185
  const dirName = dirNameStr;
188
186
 
189
187
  const selfPJson = JSON.parse(
@@ -416,7 +414,10 @@ const addLifecyclesSection = (options) => {
416
414
  if (inspectData) {
417
415
  lifecycles.push({ phase: "post-build" });
418
416
  }
419
- } else if (options.deep) {
417
+ } else if (
418
+ options?.projectType?.length &&
419
+ options?.projectType?.includes("binary")
420
+ ) {
420
421
  lifecycles.push({ phase: "post-build" });
421
422
  }
422
423
  if (options.projectType?.includes("os")) {
@@ -1393,8 +1394,8 @@ export async function createJarBom(path, options) {
1393
1394
  if (hpiFiles.length) {
1394
1395
  jarFiles = jarFiles.concat(hpiFiles);
1395
1396
  }
1396
- const tempDir = mkdtempSync(join(getTmpDir(), "jar-deps-"));
1397
1397
  for (const jar of jarFiles) {
1398
+ const tempDir = mkdtempSync(join(getTmpDir(), "jar-deps-"));
1398
1399
  if (DEBUG_MODE) {
1399
1400
  console.log(`Parsing ${jar}`);
1400
1401
  }
@@ -1405,10 +1406,10 @@ export async function createJarBom(path, options) {
1405
1406
  if (pkgList.length) {
1406
1407
  pkgList = await getMvnMetadata(pkgList);
1407
1408
  }
1408
- }
1409
- // Clean up
1410
- if (tempDir?.startsWith(getTmpDir()) && rmSync) {
1411
- rmSync(tempDir, { recursive: true, force: true });
1409
+ // Clean up
1410
+ if (tempDir?.startsWith(getTmpDir()) && rmSync) {
1411
+ rmSync(tempDir, { recursive: true, force: true });
1412
+ }
1412
1413
  }
1413
1414
  pkgList = pkgList.concat(convertJarNSToPackages(nsMapping));
1414
1415
  return buildBomNSData(options, pkgList, "maven", {
@@ -1513,10 +1514,20 @@ export async function createJavaBom(path, options) {
1513
1514
  `${options.multiProject ? "**/" : ""}build.gradle*`,
1514
1515
  options,
1515
1516
  );
1517
+ // mill
1518
+ const millFiles = getAllFiles(
1519
+ path,
1520
+ `${options.multiProject ? "**/" : ""}build.mill`,
1521
+ options,
1522
+ );
1516
1523
  let bomJsonFiles = [];
1517
1524
  if (
1518
1525
  pomFiles?.length &&
1519
- isPackageManagerAllowed("maven", ["bazel", "sbt", "gradle"], options)
1526
+ isPackageManagerAllowed(
1527
+ "maven",
1528
+ ["bazel", "sbt", "gradle", "mill"],
1529
+ options,
1530
+ )
1520
1531
  ) {
1521
1532
  if (gradleFiles.length) {
1522
1533
  thoughtLog(
@@ -1542,7 +1553,7 @@ export async function createJavaBom(path, options) {
1542
1553
  let mvnArgs;
1543
1554
  if (isQuarkus) {
1544
1555
  thoughtLog(
1545
- "This appears to be a quarkus project. Let's use the right maven plugin.",
1556
+ "This appears to be a Quarkus project. Let's use the right Maven plugin.",
1546
1557
  );
1547
1558
  // disable analytics. See: https://quarkus.io/usage/
1548
1559
  mvnArgs = [
@@ -1550,6 +1561,11 @@ export async function createJavaBom(path, options) {
1550
1561
  "quarkus:dependency-sbom",
1551
1562
  "-Dquarkus.analytics.disabled=true",
1552
1563
  ];
1564
+ if (options.specVersion) {
1565
+ mvnArgs = mvnArgs.concat(
1566
+ `-Dquarkus.dependency.sbom.schema-version=${options.specVersion}`,
1567
+ );
1568
+ }
1553
1569
  } else {
1554
1570
  const cdxMavenPlugin =
1555
1571
  process.env.CDX_MAVEN_PLUGIN ||
@@ -1755,7 +1771,7 @@ export async function createJavaBom(path, options) {
1755
1771
  );
1756
1772
  } else {
1757
1773
  console.log(
1758
- "1. Java version requirement: cdxgen container image bundles Java 23 with maven 3.9 which might be incompatible. Try running cdxgen with the custom JDK11-based image `ghcr.io/cyclonedx/cdxgen-java11:v11`.",
1774
+ "1. Java version requirement: cdxgen container image bundles Java 24 with maven 3.9 which might be incompatible. Try running cdxgen with the custom JDK11-based image `ghcr.io/cyclonedx/cdxgen-java11:v11`.",
1759
1775
  );
1760
1776
  }
1761
1777
  console.log(
@@ -1937,7 +1953,11 @@ export async function createJavaBom(path, options) {
1937
1953
  // Execute gradle properties
1938
1954
  if (
1939
1955
  gradleFiles?.length &&
1940
- isPackageManagerAllowed("gradle", ["maven", "bazel", "sbt"], options)
1956
+ isPackageManagerAllowed(
1957
+ "gradle",
1958
+ ["maven", "bazel", "sbt", "mill"],
1959
+ options,
1960
+ )
1941
1961
  ) {
1942
1962
  let rootProjects = [null];
1943
1963
  let allProjectsStr = [];
@@ -2073,7 +2093,11 @@ export async function createJavaBom(path, options) {
2073
2093
  if (
2074
2094
  gradleFiles?.length &&
2075
2095
  options.installDeps &&
2076
- isPackageManagerAllowed("gradle", ["maven", "bazel", "sbt"], options)
2096
+ isPackageManagerAllowed(
2097
+ "gradle",
2098
+ ["maven", "bazel", "sbt", "mill"],
2099
+ options,
2100
+ )
2077
2101
  ) {
2078
2102
  allProjects.push(parentComponent);
2079
2103
  const gradleCmd = getGradleCommand(gradleRootPath, null);
@@ -2209,7 +2233,11 @@ export async function createJavaBom(path, options) {
2209
2233
  if (
2210
2234
  bazelFiles?.length &&
2211
2235
  !hasAnyProjectType(["docker", "oci", "container", "os"], options, false) &&
2212
- isPackageManagerAllowed("bazel", ["maven", "gradle", "sbt"], options)
2236
+ isPackageManagerAllowed(
2237
+ "bazel",
2238
+ ["maven", "gradle", "sbt", "mill"],
2239
+ options,
2240
+ )
2213
2241
  ) {
2214
2242
  let BAZEL_CMD = "bazel";
2215
2243
  if (process.env.BAZEL_HOME) {
@@ -2347,10 +2375,15 @@ export async function createJavaBom(path, options) {
2347
2375
  `${options.multiProject ? "**/" : ""}build.sbt.lock`,
2348
2376
  options,
2349
2377
  );
2350
-
2378
+ const tempCacheDir = mkdtempSync(join(getTmpDir(), "sbt-cache-"));
2379
+ safeMkdirSync(tempCacheDir, { recursive: true });
2351
2380
  if (
2352
2381
  sbtProjects?.length &&
2353
- isPackageManagerAllowed("sbt", ["bazel", "maven", "gradle"], options)
2382
+ isPackageManagerAllowed(
2383
+ "sbt",
2384
+ ["bazel", "maven", "gradle", "mill"],
2385
+ options,
2386
+ )
2354
2387
  ) {
2355
2388
  // If the project use sbt lock files
2356
2389
  if (sbtLockFiles?.length) {
@@ -2402,6 +2435,14 @@ export async function createJavaBom(path, options) {
2402
2435
  }
2403
2436
  }
2404
2437
  writeFileSync(tempSbtPlugins, sbtPluginDefinition);
2438
+ let sbtExtraArgs = "";
2439
+ const env = { ...process.env };
2440
+ // We need to collect the jars from the cache
2441
+ if (options.deep) {
2442
+ sbtExtraArgs = " updateClassifiers";
2443
+ env["COURSIER_CACHE"] = tempCacheDir;
2444
+ env["SBT_IVY_HOME"] = tempCacheDir;
2445
+ }
2405
2446
  for (const i in sbtProjects) {
2406
2447
  const basePath = sbtProjects[i];
2407
2448
  const dlFile = join(tempDir, `dl-${i}.tmp`);
@@ -2416,11 +2457,11 @@ export async function createJavaBom(path, options) {
2416
2457
  // write to the existing plugins file
2417
2458
  if (useSlashSyntax) {
2418
2459
  sbtArgs = [
2419
- `'set ThisBuild / asciiGraphWidth := 400' "dependencyTree / toFile ${dlFile} --force"`,
2460
+ `'set ThisBuild / asciiGraphWidth := 800'${sbtExtraArgs} "dependencyTree / toFile ${dlFile} --force"`,
2420
2461
  ];
2421
2462
  } else {
2422
2463
  sbtArgs = [
2423
- `'set asciiGraphWidth in ThisBuild := 400' "dependencyTree::toFile ${dlFile} --force"`,
2464
+ `'set asciiGraphWidth in ThisBuild := 800'${sbtExtraArgs} "dependencyTree::toFile ${dlFile} --force"`,
2424
2465
  ];
2425
2466
  }
2426
2467
  pluginFile = addPlugin(basePath, sbtPluginDefinition);
@@ -2441,6 +2482,7 @@ export async function createJavaBom(path, options) {
2441
2482
  encoding: "utf-8",
2442
2483
  timeout: TIMEOUT_MS,
2443
2484
  maxBuffer: MAX_BUFFER,
2485
+ env,
2444
2486
  });
2445
2487
  if (result.status !== 0 || result.error) {
2446
2488
  console.error(result.stdout, result.stderr);
@@ -2492,12 +2534,148 @@ export async function createJavaBom(path, options) {
2492
2534
  }
2493
2535
  // Should we attempt to resolve class names
2494
2536
  if (options.resolveClass || options.deep) {
2495
- const tmpjarNSMapping = await collectJarNS(SBT_CACHE_DIR);
2537
+ const tmpjarNSMapping = await collectJarNS(tempCacheDir);
2496
2538
  if (tmpjarNSMapping && Object.keys(tmpjarNSMapping).length) {
2497
2539
  jarNSMapping = { ...jarNSMapping, ...tmpjarNSMapping };
2498
2540
  }
2541
+ // sbt can store jars in the target directory
2542
+ const jarNSData = await createJarBom(path, options);
2543
+ if (jarNSData?.bomJson?.components) {
2544
+ pkgList = pkgList.concat(jarNSData?.bomJson?.components);
2545
+ const targetJarNSMapping = {};
2546
+ for (const p of jarNSData.bomJson.components) {
2547
+ if (!p?.purl || !p?.properties?.length) {
2548
+ continue;
2549
+ }
2550
+ const nsProp = p.properties.filter(
2551
+ (prop) => prop.name === "Namespaces",
2552
+ );
2553
+ if (nsProp.length) {
2554
+ targetJarNSMapping[p.purl] = nsProp[0].value;
2555
+ }
2556
+ }
2557
+ jarNSMapping = { ...jarNSMapping, ...targetJarNSMapping };
2558
+ }
2559
+ }
2560
+ }
2561
+ if (tempCacheDir?.startsWith(getTmpDir())) {
2562
+ rmSync(tempCacheDir, {
2563
+ recursive: true,
2564
+ force: true,
2565
+ });
2566
+ }
2567
+
2568
+ if (
2569
+ millFiles?.length &&
2570
+ isPackageManagerAllowed(
2571
+ "mill",
2572
+ ["bazel", "sbt", "gradle", "maven"],
2573
+ options,
2574
+ )
2575
+ ) {
2576
+ const millRootPath = dirname(millFiles[0]);
2577
+ parentComponent = createDefaultParentComponent(
2578
+ millRootPath,
2579
+ "maven",
2580
+ options,
2581
+ );
2582
+ const millCmd = getMillCommand(millRootPath);
2583
+ const millCommonArgs = [
2584
+ "--no-server",
2585
+ "--silent",
2586
+ "--disable-prompt",
2587
+ "--disable-callgraph",
2588
+ "-k",
2589
+ "--color",
2590
+ "false",
2591
+ ];
2592
+ const millArgs = [...millCommonArgs, "__.ivyDepsTree"];
2593
+ if (DEBUG_MODE) {
2594
+ console.log("Executing", millCmd, millArgs.join(" "), "in", millRootPath);
2595
+ }
2596
+ let sresult = spawnSync(millCmd, millArgs, {
2597
+ cwd: millRootPath,
2598
+ encoding: "utf-8",
2599
+ shell: isWin,
2600
+ timeout: TIMEOUT_MS,
2601
+ maxBuffer: MAX_BUFFER * 10,
2602
+ });
2603
+ if (sresult.status !== 0 || sresult.error) {
2604
+ if (options.failOnError || DEBUG_MODE) {
2605
+ console.error(sresult.stdout, sresult.stderr);
2606
+ }
2607
+ options.failOnError && process.exit(1);
2608
+ }
2609
+ const millResolveArgs = [...millCommonArgs, "resolve", "__.ivyDepsTree"];
2610
+ if (DEBUG_MODE) {
2611
+ console.log(
2612
+ "Executing",
2613
+ millCmd,
2614
+ millResolveArgs.join(" "),
2615
+ "in",
2616
+ millRootPath,
2617
+ );
2618
+ }
2619
+ sresult = spawnSync(millCmd, millResolveArgs, {
2620
+ cwd: millRootPath,
2621
+ encoding: "utf-8",
2622
+ shell: isWin,
2623
+ timeout: TIMEOUT_MS,
2624
+ maxBuffer: MAX_BUFFER,
2625
+ });
2626
+ if (sresult.status !== 0 || sresult.error) {
2627
+ if (options.failOnError || DEBUG_MODE) {
2628
+ console.error(sresult.stdout, sresult.stderr);
2629
+ }
2630
+ options.failOnError && process.exit(1);
2631
+ }
2632
+ const sstdout = sresult.stdout;
2633
+ if (sstdout) {
2634
+ parentComponent.components = [];
2635
+ const modules = sstdout
2636
+ .trim()
2637
+ .split("\n")
2638
+ .map((a) => a.substring(0, a.lastIndexOf(".")))
2639
+ .filter((a) =>
2640
+ ["true", "1"].includes(process.env.MILL_EXCLUDE_TEST)
2641
+ ? !a.endsWith(".test")
2642
+ : true,
2643
+ );
2644
+ const moduleBomRefs = [];
2645
+ const packages = new Map();
2646
+ const relations = new Map();
2647
+ relations.set(parentComponent["bom-ref"], []);
2648
+ for (const module of modules) {
2649
+ moduleBomRefs.push(
2650
+ parseMillDependency(module, packages, relations, millRootPath),
2651
+ );
2652
+ }
2653
+ for (const module of moduleBomRefs) {
2654
+ parentComponent.components.push(packages.get(module));
2655
+ relations.get(parentComponent["bom-ref"]).push(module);
2656
+ packages.delete(module);
2657
+ }
2658
+ const newDependencies = [];
2659
+ for (const [ref, dependsOn] of relations.entries()) {
2660
+ newDependencies.push({
2661
+ ref,
2662
+ dependsOn,
2663
+ });
2664
+ }
2665
+ if (DEBUG_MODE) {
2666
+ console.log(
2667
+ `Obtained ${packages.size} components and ${relations.size} dependencies from mill.`,
2668
+ );
2669
+ }
2670
+ pkgList = pkgList.concat(...packages.values());
2671
+ dependencies = mergeDependencies(
2672
+ dependencies,
2673
+ newDependencies,
2674
+ parentComponent,
2675
+ );
2499
2676
  }
2500
2677
  }
2678
+
2501
2679
  pkgList = trimComponents(pkgList);
2502
2680
  pkgList = await getMvnMetadata(pkgList, jarNSMapping, options.deep);
2503
2681
  return buildBomNSData(options, pkgList, "maven", {
@@ -6669,6 +6847,16 @@ export function dedupeBom(options, components, parentComponent, dependencies) {
6669
6847
  dependencies = [];
6670
6848
  }
6671
6849
  components = trimComponents(components);
6850
+ // Let's apply some common tweaks
6851
+ // Convert evidence.identity section to an object for 1.5
6852
+ if (options.specVersion === 1.5) {
6853
+ for (const comp of components) {
6854
+ if (comp?.evidence?.identity && Array.isArray(comp.evidence.identity)) {
6855
+ comp.evidence.identity = comp.evidence.identity[0];
6856
+ delete comp.evidence.identity.concludedValue;
6857
+ }
6858
+ }
6859
+ }
6672
6860
  if (DEBUG_MODE) {
6673
6861
  console.log(
6674
6862
  `Obtained ${components.length} components and ${dependencies.length} dependencies after dedupe.`,
@@ -6723,7 +6911,7 @@ export async function createMultiXBom(pathList, options) {
6723
6911
  binPaths,
6724
6912
  executables,
6725
6913
  sharedLibs,
6726
- } = getOSPackages(
6914
+ } = await getOSPackages(
6727
6915
  options.allLayersExplodedDir,
6728
6916
  options.exportData?.inspectData?.Config,
6729
6917
  );
@@ -8069,7 +8257,8 @@ export async function createBom(path, options) {
8069
8257
  *
8070
8258
  * @param {Object} args CLI args
8071
8259
  * @param {Object} bomContents BOM Json
8072
- * @return {Promise<{ token: string } | { errors: string[] } | undefined>} a promise with a token (if request was successful), a body with errors (if request failed) or undefined (in case of invalid arguments)
8260
+ * @return {Promise<{ token: string } | undefined>} a promise with a token (if request was successful) or undefined (in case of invalid arguments)
8261
+ * @throws {Error} if the request fails
8073
8262
  */
8074
8263
  export async function submitBom(args, bomContents) {
8075
8264
  const serverUrl = `${args.serverUrl.replace(/\/$/, "")}/api/v1/bom`;
@@ -8102,6 +8291,7 @@ export async function submitBom(args, bomContents) {
8102
8291
  console.log(
8103
8292
  "projectId, projectName and projectVersion, or all three must be provided.",
8104
8293
  );
8294
+ args.failOnError && process.exit(1);
8105
8295
  return;
8106
8296
  }
8107
8297
  if (
@@ -8187,6 +8377,7 @@ export async function submitBom(args, bomContents) {
8187
8377
  console.log("Unable to submit the SBOM to the Dependency-Track server");
8188
8378
  }
8189
8379
  }
8190
- return error.response?.body;
8380
+ // rethrow error as function is async and we should try to catch it in the caller
8381
+ throw error;
8191
8382
  }
8192
8383
  }