@cyclonedx/cdxgen 11.1.7 → 11.1.9

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
@@ -84,7 +84,7 @@ deno install --allow-read --allow-env --allow-run --allow-sys=uid,systemMemoryIn
84
84
 
85
85
  You can also use the cdxgen container image with node, deno, or bun runtime versions.
86
86
 
87
- The default version uses Node.js 22
87
+ The default version uses Node.js 23
88
88
 
89
89
  ```bash
90
90
  docker run --rm -e CDXGEN_DEBUG_MODE=debug -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen:master -r /app -o /app/bom.json
@@ -516,7 +516,7 @@ Before raising a PR, please run the following commands.
516
516
 
517
517
  ```bash
518
518
  corepack enable pnpm
519
- pnpm install
519
+ pnpm install --config.strict-dep-builds=true
520
520
  # Generate types using jsdoc syntax
521
521
  pnpm run gen-types
522
522
  # Run biomejs formatter and linter with auto fix
package/bin/cdxgen.js CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  printSummary,
22
22
  printTable,
23
23
  } from "../lib/helpers/display.js";
24
+ import { thoughtEnd, thoughtLog } from "../lib/helpers/logger.js";
24
25
  import {
25
26
  ATOM_DB,
26
27
  dirNameStr,
@@ -397,6 +398,7 @@ if (!args.projectName) {
397
398
  args.projectName = basename(resolve(filePath));
398
399
  }
399
400
  }
401
+ thoughtLog(`Let's try to generate a CycloneDX BOM for the path '${filePath}'`);
400
402
  if (
401
403
  filePath.includes(" ") ||
402
404
  filePath.includes("\r") ||
@@ -412,6 +414,9 @@ if (
412
414
  // Support for obom/cbom aliases
413
415
  if (process.argv[1].includes("obom") && !args.type) {
414
416
  args.type = "os";
417
+ thoughtLog(
418
+ "Ok, the user wants to generate an Operations Bill-of-Materials (OBOM).",
419
+ );
415
420
  }
416
421
 
417
422
  /**
@@ -428,14 +433,28 @@ const options = Object.assign({}, args, {
428
433
  ? resolve(join(filePath, args.output))
429
434
  : args.output,
430
435
  });
431
-
436
+ // Filter duplicate types. Eg: -t gradle -t gradle
437
+ if (options.projectType && Array.isArray(options.projectType)) {
438
+ options.projectType = Array.from(new Set(options.projectType));
439
+ }
440
+ if (!options.projectType) {
441
+ thoughtLog(
442
+ "Ok, the user wants me to identify all the project types and generate a consolidated BOM document.",
443
+ );
444
+ }
432
445
  if (process.argv[1].includes("cbom")) {
446
+ thoughtLog(
447
+ "Ok, the user wants to generate Cryptographic Bill-of-Materials (CBOM).",
448
+ );
433
449
  options.includeCrypto = true;
434
450
  options.evidence = true;
435
451
  options.specVersion = 1.6;
436
452
  options.deep = true;
437
453
  }
438
454
  if (process.argv[1].includes("cdxgen-secure")) {
455
+ thoughtLog(
456
+ "Ok, the user wants cdxgen to run in secure mode by default. Let's try and use the permissions api.",
457
+ );
439
458
  console.log(
440
459
  "NOTE: Secure mode only restricts cdxgen from performing certain activities such as package installation. It does not provide security guarantees in the presence of malicious code.",
441
460
  );
@@ -446,6 +465,9 @@ if (options.standard) {
446
465
  options.specVersion = 1.6;
447
466
  }
448
467
  if (options.includeFormulation) {
468
+ thoughtLog(
469
+ "Wait, the user wants to include formulation information. Let's warn about accidentally disclosing sensitive data via the BOM files.",
470
+ );
449
471
  console.log(
450
472
  "NOTE: Formulation section could include sensitive data such as emails and secrets.\nPlease review the generated SBOM before distribution.\n",
451
473
  );
@@ -456,6 +478,13 @@ if (options.includeFormulation) {
456
478
  * @param {object} options CLI options
457
479
  */
458
480
  const applyAdvancedOptions = (options) => {
481
+ if (options?.profile !== "generic") {
482
+ thoughtLog(`BOM profile to use is '${options.profile}'.`);
483
+ } else {
484
+ thoughtLog(
485
+ "The user hasn't specified a profile. Should I suggest one to optimize the BOM for a specific use case or persona 🤔?",
486
+ );
487
+ }
459
488
  switch (options.profile) {
460
489
  case "appsec":
461
490
  options.deep = true;
@@ -508,6 +537,11 @@ const applyAdvancedOptions = (options) => {
508
537
  default:
509
538
  break;
510
539
  }
540
+ if (options.lifecycle) {
541
+ thoughtLog(
542
+ `BOM must be generated for the lifecycle '${options.lifecycle}'.`,
543
+ );
544
+ }
511
545
  switch (options.lifecycle) {
512
546
  case "pre-build":
513
547
  options.installDeps = false;
@@ -728,7 +762,9 @@ const checkPermissions = (filePath, options) => {
728
762
  options.usagesSlicesFile = `${options.projectName}-usages.json`;
729
763
  }
730
764
  prepareEnv(filePath, options);
765
+ thoughtLog("Getting ready to generate the BOM ⚡️.");
731
766
  let bomNSData = (await createBom(filePath, options)) || {};
767
+ thoughtLog("Tweaking the generated BOM data. Nearly there.");
732
768
  // Add extra metadata and annotations with post processing
733
769
  bomNSData = postProcess(bomNSData, options);
734
770
  if (
@@ -748,6 +784,13 @@ const checkPermissions = (filePath, options) => {
748
784
  } else {
749
785
  jsonPayload = JSON.stringify(bomNSData.bomJson, null, null);
750
786
  fs.writeFileSync(jsonFile, jsonPayload);
787
+ if (jsonFile.endsWith("bom.json")) {
788
+ thoughtLog(
789
+ `Let's save the file to "${jsonFile}". Should I suggest the '.cdx.json' file extension for better semantics?`,
790
+ );
791
+ } else {
792
+ thoughtLog(`Let's save the file to "${jsonFile}".`);
793
+ }
751
794
  }
752
795
  if (
753
796
  jsonPayload &&
@@ -843,6 +886,7 @@ const checkPermissions = (filePath, options) => {
843
886
  jsonFile,
844
887
  JSON.stringify(bomJsonUnsignedObj, null, null),
845
888
  );
889
+ thoughtLog(`Signing the BOM file "${jsonFile}".`);
846
890
  if (publicKeyFile) {
847
891
  // Verifying this signature
848
892
  const signatureVerification = jws.verify(
@@ -929,10 +973,13 @@ const checkPermissions = (filePath, options) => {
929
973
  }
930
974
  // Perform automatic validation
931
975
  if (options.validate) {
976
+ thoughtLog("Wait, let's check the generated BOM file for any issues.");
932
977
  if (!validateBom(bomNSData.bomJson)) {
933
978
  process.exit(1);
934
979
  }
980
+ thoughtLog("BOM file looks valid. Thank you for using cdxgen!");
935
981
  }
982
+ thoughtEnd();
936
983
  // Automatically submit the bom data
937
984
  // biome-ignore lint/suspicious/noDoubleEquals: yargs passes true for empty values
938
985
  if (options.serverUrl && options.serverUrl != true && options.apiKey) {
package/lib/cli/index.js CHANGED
@@ -30,6 +30,8 @@ import {
30
30
  gitTreeHashes,
31
31
  listFiles,
32
32
  } from "../helpers/envcontext.js";
33
+ import { thoughtLog } from "../helpers/logger.js";
34
+
33
35
  import {
34
36
  CARGO_CMD,
35
37
  CLJ_CMD,
@@ -1344,11 +1346,22 @@ export async function createJavaBom(path, options) {
1344
1346
  `${options.multiProject ? "**/" : ""}pom.xml`,
1345
1347
  options,
1346
1348
  );
1349
+ // gradle
1350
+ const gradleFiles = getAllFiles(
1351
+ path,
1352
+ `${options.multiProject ? "**/" : ""}build.gradle*`,
1353
+ options,
1354
+ );
1347
1355
  let bomJsonFiles = [];
1348
1356
  if (
1349
1357
  pomFiles?.length &&
1350
1358
  isPackageManagerAllowed("maven", ["bazel", "sbt", "gradle"], options)
1351
1359
  ) {
1360
+ if (gradleFiles.length) {
1361
+ thoughtLog(
1362
+ `Is this a Gradle project? I recommend invoking cdxgen with the "-t gradle" option if you're encountering build errors.`,
1363
+ );
1364
+ }
1352
1365
  if (!isQuarkus) {
1353
1366
  // Quarkus projects require special treatment. To detect quarkus, we parse the first 3 maven file to look for a hit
1354
1367
  for (const pf of pomFiles.slice(0, 3)) {
@@ -1367,6 +1380,9 @@ export async function createJavaBom(path, options) {
1367
1380
  let result = undefined;
1368
1381
  let mvnArgs;
1369
1382
  if (isQuarkus) {
1383
+ thoughtLog(
1384
+ "This appears to be a quarkus project. Let's use the right maven plugin.",
1385
+ );
1370
1386
  // disable analytics. See: https://quarkus.io/usage/
1371
1387
  mvnArgs = [
1372
1388
  "-fn",
@@ -1438,6 +1454,7 @@ export async function createJavaBom(path, options) {
1438
1454
  }
1439
1455
  // Use the cyclonedx maven plugin if there is no preference for maven deps tree
1440
1456
  if (!useMavenDepsTree) {
1457
+ thoughtLog("The user wants me to use the cyclonedx-maven plugin.");
1441
1458
  console.log(
1442
1459
  `Executing '${mavenCmd} ${mvnArgs.join(" ")}' in`,
1443
1460
  basePath,
@@ -1488,6 +1505,9 @@ export async function createJavaBom(path, options) {
1488
1505
  // For the first pom alone, we need to execute first in non-recursive mode to capture
1489
1506
  // the parent component. Then, we execute all of them in recursive mode
1490
1507
  if (f === firstPom) {
1508
+ thoughtLog(
1509
+ "What is the parent component here? Let's use maven command to find out.",
1510
+ );
1491
1511
  result = spawnSync(
1492
1512
  "mvn",
1493
1513
  ["dependency:tree", "-N", `-DoutputFile=${tempMvnParentTree}`],
@@ -1510,10 +1530,22 @@ export async function createJavaBom(path, options) {
1510
1530
  tmpParentComponent.type = "application";
1511
1531
  parentComponent = tmpParentComponent;
1512
1532
  parentComponent.components = [];
1533
+ if (parentComponent.name) {
1534
+ thoughtLog(
1535
+ `Parent component is called ${parentComponent.name}!`,
1536
+ );
1537
+ }
1513
1538
  }
1514
1539
  }
1515
1540
  }
1516
- console.log(`Executing 'mvn ${mvnTreeArgs.join(" ")}' in ${basePath}`);
1541
+ thoughtLog(
1542
+ `**MAVEN**: Let's use Maven to collect packages from ${basePath}.`,
1543
+ );
1544
+ if (DEBUG_MODE) {
1545
+ console.log(
1546
+ `Executing 'mvn ${mvnTreeArgs.join(" ")}' in ${basePath}`,
1547
+ );
1548
+ }
1517
1549
  // Prefer the built-in maven
1518
1550
  result = spawnSync(
1519
1551
  PREFER_MAVEN_DEPS_TREE ? "mvn" : mavenCmd,
@@ -1577,6 +1609,9 @@ export async function createJavaBom(path, options) {
1577
1609
  console.log(
1578
1610
  "\nFalling back to parsing pom.xml files. Only direct dependencies would get included!",
1579
1611
  );
1612
+ thoughtLog(
1613
+ "**MAVEN**: There appear to be build errors, so the SBOM will be incomplete.",
1614
+ );
1580
1615
  const dlist = parsePom(f);
1581
1616
  if (dlist?.length) {
1582
1617
  pkgList = pkgList.concat(dlist);
@@ -1693,19 +1728,17 @@ export async function createJavaBom(path, options) {
1693
1728
  }
1694
1729
  }
1695
1730
  if (possible_misses) {
1696
- if (!DEBUG_MODE) {
1731
+ if (gradleFiles.length) {
1732
+ console.log(
1733
+ "Is this a gradle project? Try running cdxgen with `-t gradle`.",
1734
+ );
1735
+ } else if (!DEBUG_MODE) {
1697
1736
  console.warn(
1698
1737
  "Multiple errors occurred while building this project with maven. The SBOM is therefore incomplete!",
1699
1738
  );
1700
1739
  }
1701
1740
  }
1702
1741
  }
1703
- // gradle
1704
- const gradleFiles = getAllFiles(
1705
- path,
1706
- `${options.multiProject ? "**/" : ""}build.gradle*`,
1707
- options,
1708
- );
1709
1742
  const allProjects = [];
1710
1743
  const allProjectsAddedPurls = [];
1711
1744
  const rootDependsOn = new Set();
@@ -1923,9 +1956,14 @@ export async function createJavaBom(path, options) {
1923
1956
  "from this gradle project. De-duping this list ...",
1924
1957
  );
1925
1958
  } else {
1926
- console.log(
1927
- "No packages found. Set the environment variable 'CDXGEN_DEBUG_MODE=debug' to troubleshoot any gradle related errors.",
1959
+ thoughtLog(
1960
+ "**GRADLE:** SBOM is incomplete. I recommend troubleshooting the issue to improve the BOM precision.",
1928
1961
  );
1962
+ if (!DEBUG_MODE) {
1963
+ console.log(
1964
+ "No packages found. Set the environment variable 'CDXGEN_DEBUG_MODE=debug' to troubleshoot any gradle related errors.",
1965
+ );
1966
+ }
1929
1967
  options.failOnError && process.exit(1);
1930
1968
  }
1931
1969
  // Should we attempt to resolve class names
@@ -3317,6 +3355,9 @@ export async function createPythonBom(path, options) {
3317
3355
  }
3318
3356
  // Fallback to parsing manually
3319
3357
  if (!pkgList.length || !frozen) {
3358
+ thoughtLog(
3359
+ `Manually parsing ${f}. The result would include only direct dependencies.`,
3360
+ );
3320
3361
  if (DEBUG_MODE) {
3321
3362
  console.log(
3322
3363
  `Manually parsing ${f}. The result would include only direct dependencies.`,
@@ -4005,10 +4046,12 @@ export async function createRustBom(path, options) {
4005
4046
  }
4006
4047
  }
4007
4048
  // After running cargo check, .d files would get created
4008
- const makeDFiles = getAllFiles(path, "target/**/*.d", options);
4009
4049
  let pkgFilesMap = {};
4010
- for (const dfile of makeDFiles) {
4011
- pkgFilesMap = { ...pkgFilesMap, ...parseMakeDFile(dfile) };
4050
+ if (options.deep || ["build", "post-build"].includes(options.lifecycle)) {
4051
+ const makeDFiles = getAllFiles(path, "target/**/*.d", options);
4052
+ for (const dfile of makeDFiles) {
4053
+ pkgFilesMap = { ...pkgFilesMap, ...parseMakeDFile(dfile) };
4054
+ }
4012
4055
  }
4013
4056
  const cargoLockFiles = getAllFiles(
4014
4057
  path,
@@ -6205,7 +6248,16 @@ export async function createMultiXBom(pathList, options) {
6205
6248
  );
6206
6249
  if (DEBUG_MODE) {
6207
6250
  console.log(
6208
- `Found ${osPackages.length} OS packages at ${options.allLayersExplodedDir}`,
6251
+ `**OS**: Found ${osPackages.length} OS packages at ${options.allLayersExplodedDir}`,
6252
+ );
6253
+ }
6254
+ if (osPackages.length) {
6255
+ thoughtLog(
6256
+ `I found ${osPackages.length} OS packages at ${options.allLayersExplodedDir}`,
6257
+ );
6258
+ } else {
6259
+ thoughtLog(
6260
+ `I couldn't find any OS packages at ${options.allLayersExplodedDir}. Perhaps the binary plugin wasn't available, or the architecture is unsupported.`,
6209
6261
  );
6210
6262
  }
6211
6263
  if (allTypes?.length) {
@@ -6230,6 +6282,11 @@ export async function createMultiXBom(pathList, options) {
6230
6282
  if (DEBUG_MODE) {
6231
6283
  console.log(`Found ${bomData.bomJson.components.length} OS components`);
6232
6284
  }
6285
+ if (bomData.bomJson.components.length) {
6286
+ thoughtLog(
6287
+ `I found ${bomData.bomJson.components.length} OS packages 😎.`,
6288
+ );
6289
+ }
6233
6290
  components = components.concat(bomData.bomJson.components);
6234
6291
  }
6235
6292
  }
@@ -6237,10 +6294,19 @@ export async function createMultiXBom(pathList, options) {
6237
6294
  if (DEBUG_MODE) {
6238
6295
  console.log("Scanning", path);
6239
6296
  }
6297
+ if (pathList.length > 2) {
6298
+ thoughtLog(`Let's thoroughly check the path ${path}.`);
6299
+ }
6240
6300
  // Node.js
6241
6301
  if (hasAnyProjectType(["oci", "js"], options)) {
6302
+ thoughtLog(
6303
+ "**JS**: Now looking for JavaScript projects (npm, yarn, pnpm) and files.",
6304
+ );
6242
6305
  bomData = await createNodejsBom(path, options);
6243
6306
  if (bomData?.bomJson?.components?.length) {
6307
+ thoughtLog(
6308
+ `I found ${bomData.bomJson.components.length} npm packages. Let's keep looking.`,
6309
+ );
6244
6310
  if (DEBUG_MODE) {
6245
6311
  console.log(
6246
6312
  `Found ${bomData.bomJson.components.length} npm packages at ${path}`,
@@ -6268,8 +6334,14 @@ export async function createMultiXBom(pathList, options) {
6268
6334
  }
6269
6335
  // Java
6270
6336
  if (hasAnyProjectType(["oci", "java"], options)) {
6337
+ thoughtLog(
6338
+ "**JAVA**: Looking for Java projects (e.g., Maven, Gradle, SBT). I hope all configurations—from Java version to individual build settings—are correctly aligned.",
6339
+ );
6271
6340
  bomData = await createJavaBom(path, options);
6272
6341
  if (bomData?.bomJson?.components?.length) {
6342
+ thoughtLog(
6343
+ `I found ${bomData.bomJson.components.length} java packages.`,
6344
+ );
6273
6345
  if (DEBUG_MODE) {
6274
6346
  console.log(
6275
6347
  `Found ${bomData.bomJson.components.length} java packages at ${path}`,
@@ -6291,6 +6363,9 @@ export async function createMultiXBom(pathList, options) {
6291
6363
  if (bomData.parentComponent.components?.length) {
6292
6364
  let bomSubComponents = bomData.parentComponent.components;
6293
6365
  if (["true", "1"].includes(process.env.GRADLE_RESOLVE_FROM_NODE)) {
6366
+ thoughtLog(
6367
+ "Wait, the user wants me to resolve gradle projects from npm.",
6368
+ );
6294
6369
  const allRefs = components.map((c) => c["bom-ref"]);
6295
6370
  const duplicateComponents = bomSubComponents.filter((c) =>
6296
6371
  allRefs.includes(c["bom-ref"]),
@@ -6309,8 +6384,19 @@ export async function createMultiXBom(pathList, options) {
6309
6384
  }
6310
6385
  }
6311
6386
  if (hasAnyProjectType(["oci", "py"], options)) {
6387
+ thoughtLog(
6388
+ "**PYTHON**: Looking for Python projects with package managers such as pip, poetry, uv, etc. Wish me good luck!",
6389
+ );
6390
+ if (process.env?.CDXGEN_IN_CONTAINER !== "true") {
6391
+ thoughtLog(
6392
+ "I'm running in a non-container environment. Let's hope the correct build tools are available ✌️.",
6393
+ );
6394
+ }
6312
6395
  bomData = await createPythonBom(path, options);
6313
6396
  if (bomData?.bomJson?.components?.length) {
6397
+ thoughtLog(
6398
+ `I found ${bomData.bomJson.components.length} python packages.`,
6399
+ );
6314
6400
  if (DEBUG_MODE) {
6315
6401
  console.log(
6316
6402
  `Found ${bomData.bomJson.components.length} python packages at ${path}`,
@@ -6330,8 +6416,12 @@ export async function createMultiXBom(pathList, options) {
6330
6416
  }
6331
6417
  }
6332
6418
  if (hasAnyProjectType(["oci", "go"], options)) {
6419
+ thoughtLog(
6420
+ "**GO**: Looking for go projects. I need to be cautious about purl namespaces and potential failures with the 'go list' command.",
6421
+ );
6333
6422
  bomData = await createGoBom(path, options);
6334
6423
  if (bomData?.bomJson?.components?.length) {
6424
+ thoughtLog(`I found ${bomData.bomJson.components.length} go packages.`);
6335
6425
  if (DEBUG_MODE) {
6336
6426
  console.log(
6337
6427
  `Found ${bomData.bomJson.components.length} go packages at ${path}`,
@@ -6351,8 +6441,14 @@ export async function createMultiXBom(pathList, options) {
6351
6441
  }
6352
6442
  }
6353
6443
  if (hasAnyProjectType(["oci", "rust"], options)) {
6444
+ thoughtLog(
6445
+ "**RUST**: Let's search for Cargo/Rust projects. Should I warn the user that we don't support Cargo 'features' and native dependencies, which may lead to both false positives and false negatives? 🤔?",
6446
+ );
6354
6447
  bomData = await createRustBom(path, options);
6355
6448
  if (bomData?.bomJson?.components?.length) {
6449
+ thoughtLog(
6450
+ `I found ${bomData.bomJson.components.length} rust packages.`,
6451
+ );
6356
6452
  if (DEBUG_MODE) {
6357
6453
  console.log(
6358
6454
  `Found ${bomData.bomJson.components.length} rust packages at ${path}`,
@@ -6379,8 +6475,14 @@ export async function createMultiXBom(pathList, options) {
6379
6475
  }
6380
6476
  }
6381
6477
  if (hasAnyProjectType(["oci", "php"], options)) {
6478
+ thoughtLog(
6479
+ "**PHP**: About to search for Composer-based projects. I hope lock files are available; otherwise, the 'composer install' command might fail for various reasons.",
6480
+ );
6382
6481
  bomData = createPHPBom(path, options);
6383
6482
  if (bomData?.bomJson?.components?.length) {
6483
+ thoughtLog(
6484
+ `I found ${bomData.bomJson.components.length} php packages.`,
6485
+ );
6384
6486
  if (DEBUG_MODE) {
6385
6487
  console.log(
6386
6488
  `Found ${bomData.bomJson.components.length} php packages at ${path}`,
@@ -6407,8 +6509,14 @@ export async function createMultiXBom(pathList, options) {
6407
6509
  }
6408
6510
  }
6409
6511
  if (hasAnyProjectType(["oci", "ruby"], options)) {
6512
+ thoughtLog(
6513
+ "**RUBY**: Are there any Ruby projects in this path? There's only one way to know.",
6514
+ );
6410
6515
  bomData = await createRubyBom(path, options);
6411
6516
  if (bomData?.bomJson?.components?.length) {
6517
+ thoughtLog(
6518
+ `We got ${bomData.bomJson.components.length} ruby packages.`,
6519
+ );
6412
6520
  if (DEBUG_MODE) {
6413
6521
  console.log(
6414
6522
  `Found ${bomData.bomJson.components.length} ruby packages at ${path}`,
@@ -6436,8 +6544,12 @@ export async function createMultiXBom(pathList, options) {
6436
6544
  }
6437
6545
  }
6438
6546
  if (hasAnyProjectType(["oci", "csharp"], options)) {
6547
+ thoughtLog("**CSHARP**: What about csharp and fsharp projects?");
6439
6548
  bomData = await createCsharpBom(path, options);
6440
6549
  if (bomData?.bomJson?.components?.length) {
6550
+ thoughtLog(
6551
+ `There are ${bomData.bomJson.components.length} csharp packages.`,
6552
+ );
6441
6553
  if (DEBUG_MODE) {
6442
6554
  console.log(
6443
6555
  `Found ${bomData.bomJson.components.length} csharp packages at ${path}`,
@@ -6464,8 +6576,14 @@ export async function createMultiXBom(pathList, options) {
6464
6576
  }
6465
6577
  }
6466
6578
  if (hasAnyProjectType(["oci", "dart"], options)) {
6579
+ thoughtLog(
6580
+ "**DART**: Looking for Dart projects. These are rare ones. Should I inform the user that they can pass the types argument via the command-line to speed things up?",
6581
+ );
6467
6582
  bomData = await createDartBom(path, options);
6468
6583
  if (bomData?.bomJson?.components?.length) {
6584
+ thoughtLog(
6585
+ `I found ${bomData.bomJson.components.length} pub packages.`,
6586
+ );
6469
6587
  if (DEBUG_MODE) {
6470
6588
  console.log(
6471
6589
  `Found ${bomData.bomJson.components.length} pub packages at ${path}`,
@@ -6485,8 +6603,14 @@ export async function createMultiXBom(pathList, options) {
6485
6603
  }
6486
6604
  }
6487
6605
  if (hasAnyProjectType(["oci", "haskell"], options)) {
6606
+ thoughtLog(
6607
+ "**HASKELL**: Looking for Haskell projects. They're rarely encountered.",
6608
+ );
6488
6609
  bomData = createHaskellBom(path, options);
6489
6610
  if (bomData?.bomJson?.components?.length) {
6611
+ thoughtLog(
6612
+ `I found ${bomData.bomJson.components.length} hackage packages.`,
6613
+ );
6490
6614
  if (DEBUG_MODE) {
6491
6615
  console.log(
6492
6616
  `Found ${bomData.bomJson.components.length} hackage packages at ${path}`,
@@ -6506,8 +6630,14 @@ export async function createMultiXBom(pathList, options) {
6506
6630
  }
6507
6631
  }
6508
6632
  if (hasAnyProjectType(["oci", "elixir"], options)) {
6633
+ thoughtLog(
6634
+ "**ELIXIR**: Looking for Elixir projects—they're quite rare as well.",
6635
+ );
6509
6636
  bomData = createElixirBom(path, options);
6510
6637
  if (bomData?.bomJson?.components?.length) {
6638
+ thoughtLog(
6639
+ `I found ${bomData.bomJson.components.length} mix packages.`,
6640
+ );
6511
6641
  if (DEBUG_MODE) {
6512
6642
  console.log(
6513
6643
  `Found ${bomData.bomJson.components.length} mix packages at ${path}`,
@@ -6527,8 +6657,14 @@ export async function createMultiXBom(pathList, options) {
6527
6657
  }
6528
6658
  }
6529
6659
  if (hasAnyProjectType(["oci", "c"], options)) {
6660
+ thoughtLog(
6661
+ "**C/C++**: Looking for C/C++ projects. Should I warn the user that the generated SBOM might have low accuracy and contain errors?",
6662
+ );
6530
6663
  bomData = createCppBom(path, options);
6531
6664
  if (bomData?.bomJson?.components?.length) {
6665
+ thoughtLog(
6666
+ `I found ${bomData.bomJson.components.length} cpp packages.`,
6667
+ );
6532
6668
  if (DEBUG_MODE) {
6533
6669
  console.log(
6534
6670
  `Found ${bomData.bomJson.components.length} cpp packages at ${path}`,
@@ -6548,8 +6684,14 @@ export async function createMultiXBom(pathList, options) {
6548
6684
  }
6549
6685
  }
6550
6686
  if (hasAnyProjectType(["oci", "clojure"], options)) {
6687
+ thoughtLog(
6688
+ "**CLOJURE**: Looking for Clojure projects. Should I warn the user that the purl namespace 'clojars' isn't widely supported by tools like Dependency-Track?",
6689
+ );
6551
6690
  bomData = createClojureBom(path, options);
6552
6691
  if (bomData?.bomJson?.components?.length) {
6692
+ thoughtLog(
6693
+ `I found ${bomData.bomJson.components.length} clojure packages.`,
6694
+ );
6553
6695
  if (DEBUG_MODE) {
6554
6696
  console.log(
6555
6697
  `Found ${bomData.bomJson.components.length} clojure packages at ${path}`,
@@ -6569,8 +6711,12 @@ export async function createMultiXBom(pathList, options) {
6569
6711
  }
6570
6712
  }
6571
6713
  if (hasAnyProjectType(["oci", "github"], options)) {
6714
+ thoughtLog("**GITHUB**: Looking for any github packages and workflows.");
6572
6715
  bomData = createGitHubBom(path, options);
6573
6716
  if (bomData?.bomJson?.components?.length) {
6717
+ thoughtLog(
6718
+ `I found ${bomData.bomJson.components.length} github action packages as well. Should I convert these to formulation instead 🤔`,
6719
+ );
6574
6720
  if (DEBUG_MODE) {
6575
6721
  console.log(
6576
6722
  `Found ${bomData.bomJson.components.length} GitHub action packages at ${path}`,
@@ -6590,8 +6736,14 @@ export async function createMultiXBom(pathList, options) {
6590
6736
  }
6591
6737
  }
6592
6738
  if (hasAnyProjectType(["oci", "cloudbuild"], options)) {
6739
+ thoughtLog(
6740
+ "**CLOUDBUILD**: Let's check for CloudBuild configuration files that include package dependencies.",
6741
+ );
6593
6742
  bomData = createCloudBuildBom(path, options);
6594
6743
  if (bomData?.bomJson?.components?.length) {
6744
+ thoughtLog(
6745
+ `I found ${bomData.bomJson.components.length} cloudbuild packages.`,
6746
+ );
6595
6747
  if (DEBUG_MODE) {
6596
6748
  console.log(
6597
6749
  `Found ${bomData.bomJson.components.length} CloudBuild configuration at ${path}`,
@@ -6611,8 +6763,14 @@ export async function createMultiXBom(pathList, options) {
6611
6763
  }
6612
6764
  }
6613
6765
  if (hasAnyProjectType(["oci", "swift"], options)) {
6766
+ thoughtLog(
6767
+ "**SWIFT**: Now checking for Swift projects. We don't support CocoaPods, Objective-C, or pure Xcode projects, so the SBOM will be incomplete.",
6768
+ );
6614
6769
  bomData = await createSwiftBom(path, options);
6615
6770
  if (bomData?.bomJson?.components?.length) {
6771
+ thoughtLog(
6772
+ `I found ${bomData.bomJson.components.length} swift packages here.`,
6773
+ );
6616
6774
  if (DEBUG_MODE) {
6617
6775
  console.log(
6618
6776
  `Found ${bomData.bomJson.components.length} Swift packages at ${path}`,
@@ -6632,8 +6790,14 @@ export async function createMultiXBom(pathList, options) {
6632
6790
  }
6633
6791
  }
6634
6792
  if (hasAnyProjectType(["oci", "jar", "war", "ear"], options)) {
6793
+ thoughtLog(
6794
+ "**JAR**: Let's check for any bundled jar/war/ear files to improve the SBOM accuracy.",
6795
+ );
6635
6796
  bomData = await createJarBom(path, options);
6636
6797
  if (bomData?.bomJson?.components?.length) {
6798
+ thoughtLog(
6799
+ `I found ${bomData.bomJson.components.length} jar packages as well.`,
6800
+ );
6637
6801
  if (DEBUG_MODE) {
6638
6802
  console.log(
6639
6803
  `Found ${bomData.bomJson.components.length} jar packages at ${path}`,
@@ -6654,8 +6818,14 @@ export async function createMultiXBom(pathList, options) {
6654
6818
  }
6655
6819
  // Collect any crypto keys
6656
6820
  if (options.specVersion >= 1.6 && options.includeCrypto) {
6821
+ thoughtLog(
6822
+ "**CBOM**: Wait, the user wants me to look for cryptographic assets. Let's check thoroughly.",
6823
+ );
6657
6824
  bomData = await createCryptoCertsBom(path, options);
6658
6825
  if (bomData?.bomJson?.components?.length) {
6826
+ thoughtLog(
6827
+ `I found ${bomData.bomJson.components.length} crypto assets.`,
6828
+ );
6659
6829
  if (DEBUG_MODE) {
6660
6830
  console.log(
6661
6831
  `Found ${bomData.bomJson.components.length} crypto assets at ${path}`,
@@ -6693,6 +6863,7 @@ export async function createMultiXBom(pathList, options) {
6693
6863
  }
6694
6864
  // Retain the components of parent component
6695
6865
  if (parentSubComponents.length) {
6866
+ thoughtLog("**METADATA**: Tweaking the parent component hierarchy.");
6696
6867
  if (!parentComponent || !Object.keys(parentComponent).length) {
6697
6868
  parentComponent = parentSubComponents[0];
6698
6869
  }
@@ -6724,7 +6895,10 @@ export async function createMultiXBom(pathList, options) {
6724
6895
  parentDependencies["dependsOn"] = [];
6725
6896
  }
6726
6897
  for (const parentSub of parentSubComponents) {
6727
- parentDependencies["dependsOn"].push(parentSub["bom-ref"]);
6898
+ // Issue: 1622. We might have already captured this parent component dependency
6899
+ if (!parentDependencies["dependsOn"].includes(parentSub["bom-ref"])) {
6900
+ parentDependencies["dependsOn"].push(parentSub["bom-ref"]);
6901
+ }
6728
6902
  }
6729
6903
  }
6730
6904
  // some cleanup, but not complete
@@ -7193,9 +7367,17 @@ export async function createBom(path, options) {
7193
7367
  projectType = ["java"];
7194
7368
  }
7195
7369
  if (projectType.length > 1) {
7370
+ thoughtLog(
7371
+ `The user has specified multiple project types: projectType.join(", "). Let's focus on the types one at a time.`,
7372
+ );
7196
7373
  console.log("Generate BOM for project types:", projectType.join(", "));
7197
7374
  return await createMultiXBom(path, options);
7198
7375
  }
7376
+ if (projectType.length === 1) {
7377
+ thoughtLog(
7378
+ `The user wants me to focus on a single type, '${projectType}'. Could there be an issue with auto-detection, or might they use another tool like cyclonedx-cli to merge all the generated BOMs later?`,
7379
+ );
7380
+ }
7199
7381
  // Use the project type alias to return any singular BOM
7200
7382
  if (PROJECT_TYPE_ALIASES["java"].includes(projectType[0])) {
7201
7383
  return await createJavaBom(path, options);