@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 +2 -2
- package/bin/cdxgen.js +48 -1
- package/lib/cli/index.js +197 -15
- package/lib/helpers/logger.js +52 -0
- package/lib/helpers/utils.js +298 -272
- package/lib/helpers/utils.test.js +6 -6
- package/lib/helpers/validator.js +48 -0
- package/package.json +4 -3
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/logger.d.ts +4 -0
- package/types/lib/helpers/logger.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +1 -4
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/helpers/validator.d.ts.map +1 -1
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
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
1927
|
-
"
|
|
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
|
-
|
|
4011
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|