@cyclonedx/cdxgen 12.3.3 → 12.4.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 +69 -25
- package/bin/audit.js +21 -7
- package/bin/cdxgen.js +270 -127
- package/bin/convert.js +34 -15
- package/bin/hbom.js +495 -0
- package/bin/repl.js +592 -37
- package/bin/validate.js +31 -4
- package/bin/verify.js +18 -5
- package/data/README.md +298 -25
- package/data/component-tags.json +6 -0
- package/data/crypto-oid.json +16 -0
- package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
- package/data/predictive-audit-allowlist.json +11 -0
- package/data/queries-darwin.json +12 -1
- package/data/queries-win.json +7 -1
- package/data/queries.json +39 -2
- package/data/rules/ai-agent-governance.yaml +16 -0
- package/data/rules/asar-archives.yaml +150 -0
- package/data/rules/chrome-extensions.yaml +8 -0
- package/data/rules/ci-permissions.yaml +42 -18
- package/data/rules/container-risk.yaml +14 -7
- package/data/rules/dependency-sources.yaml +11 -0
- package/data/rules/hbom-compliance.yaml +325 -0
- package/data/rules/hbom-performance.yaml +307 -0
- package/data/rules/hbom-security.yaml +248 -0
- package/data/rules/host-topology.yaml +165 -0
- package/data/rules/mcp-servers.yaml +18 -3
- package/data/rules/obom-runtime.yaml +907 -22
- package/data/rules/package-integrity.yaml +14 -0
- package/data/rules/rootfs-hardening.yaml +179 -0
- package/data/rules/vscode-extensions.yaml +9 -0
- package/lib/audit/index.js +210 -8
- package/lib/audit/index.poku.js +332 -0
- package/lib/audit/reporters.js +222 -0
- package/lib/audit/targets.js +146 -1
- package/lib/audit/targets.poku.js +186 -0
- package/lib/cli/asar.poku.js +328 -0
- package/lib/cli/index.js +527 -99
- package/lib/cli/index.poku.js +1469 -212
- package/lib/evinser/evinser.js +14 -9
- package/lib/helpers/analyzer.js +1406 -29
- package/lib/helpers/analyzer.poku.js +342 -0
- package/lib/helpers/analyzerScope.js +712 -0
- package/lib/helpers/asarutils.js +1556 -0
- package/lib/helpers/asarutils.poku.js +443 -0
- package/lib/helpers/auditCategories.js +12 -0
- package/lib/helpers/auditCategories.poku.js +32 -0
- package/lib/helpers/bomUtils.js +155 -1
- package/lib/helpers/bomUtils.poku.js +79 -1
- package/lib/helpers/cbomutils.js +271 -1
- package/lib/helpers/cbomutils.poku.js +248 -5
- package/lib/helpers/display.js +291 -1
- package/lib/helpers/display.poku.js +149 -0
- package/lib/helpers/evidenceUtils.js +58 -0
- package/lib/helpers/evidenceUtils.poku.js +54 -0
- package/lib/helpers/exportUtils.js +9 -0
- package/lib/helpers/gtfobins.js +142 -8
- package/lib/helpers/gtfobins.poku.js +24 -1
- package/lib/helpers/hbom.js +710 -0
- package/lib/helpers/hbom.poku.js +496 -0
- package/lib/helpers/hbomAnalysis.js +268 -0
- package/lib/helpers/hbomAnalysis.poku.js +249 -0
- package/lib/helpers/hbomLoader.js +35 -0
- package/lib/helpers/hostTopology.js +803 -0
- package/lib/helpers/hostTopology.poku.js +363 -0
- package/lib/helpers/inventoryStats.js +69 -0
- package/lib/helpers/inventoryStats.poku.js +86 -0
- package/lib/helpers/lolbas.js +19 -1
- package/lib/helpers/lolbas.poku.js +23 -0
- package/lib/helpers/osqueryTransform.js +47 -0
- package/lib/helpers/osqueryTransform.poku.js +47 -0
- package/lib/helpers/plugins.js +350 -0
- package/lib/helpers/plugins.poku.js +57 -0
- package/lib/helpers/protobom.js +209 -45
- package/lib/helpers/protobom.poku.js +183 -5
- package/lib/helpers/protobomLoader.js +43 -0
- package/lib/helpers/protobomLoader.poku.js +31 -0
- package/lib/helpers/remote/dependency-track.js +36 -3
- package/lib/helpers/remote/dependency-track.poku.js +44 -0
- package/lib/helpers/source.js +24 -0
- package/lib/helpers/source.poku.js +32 -0
- package/lib/helpers/utils.js +1438 -93
- package/lib/helpers/utils.poku.js +846 -4
- package/lib/managers/binary.e2e.poku.js +367 -0
- package/lib/managers/binary.js +2293 -353
- package/lib/managers/binary.poku.js +1699 -1
- package/lib/managers/docker.js +201 -79
- package/lib/managers/docker.poku.js +337 -12
- package/lib/server/server.js +4 -28
- package/lib/stages/postgen/annotator.js +38 -0
- package/lib/stages/postgen/annotator.poku.js +107 -1
- package/lib/stages/postgen/auditBom.js +121 -18
- package/lib/stages/postgen/auditBom.poku.js +1366 -31
- package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
- package/lib/stages/postgen/postgen.js +406 -8
- package/lib/stages/postgen/postgen.poku.js +484 -0
- package/lib/stages/postgen/ruleEngine.js +116 -0
- package/lib/stages/pregen/envAudit.js +14 -3
- package/lib/validator/bomValidator.js +90 -38
- package/lib/validator/bomValidator.poku.js +90 -0
- package/lib/validator/complianceRules.js +4 -2
- package/lib/validator/index.poku.js +14 -0
- package/package.json +23 -21
- package/types/bin/hbom.d.ts +3 -0
- package/types/bin/hbom.d.ts.map +1 -0
- package/types/bin/repl.d.ts +1 -1
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +44 -0
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/reporters.d.ts +16 -0
- package/types/lib/audit/reporters.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +16 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +4 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts +33 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/analyzerScope.d.ts +11 -0
- package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
- package/types/lib/helpers/asarutils.d.ts +34 -0
- package/types/lib/helpers/asarutils.d.ts.map +1 -0
- package/types/lib/helpers/auditCategories.d.ts +5 -0
- package/types/lib/helpers/auditCategories.d.ts.map +1 -1
- package/types/lib/helpers/bomUtils.d.ts +10 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -1
- package/types/lib/helpers/cbomutils.d.ts +3 -2
- package/types/lib/helpers/cbomutils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/evidenceUtils.d.ts +8 -0
- package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -1
- package/types/lib/helpers/gtfobins.d.ts +8 -0
- package/types/lib/helpers/gtfobins.d.ts.map +1 -1
- package/types/lib/helpers/hbom.d.ts +49 -0
- package/types/lib/helpers/hbom.d.ts.map +1 -0
- package/types/lib/helpers/hbomAnalysis.d.ts +76 -0
- package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
- package/types/lib/helpers/hbomLoader.d.ts +7 -0
- package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
- package/types/lib/helpers/hostTopology.d.ts +12 -0
- package/types/lib/helpers/hostTopology.d.ts.map +1 -0
- package/types/lib/helpers/inventoryStats.d.ts +11 -0
- package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
- package/types/lib/helpers/lolbas.d.ts.map +1 -1
- package/types/lib/helpers/osqueryTransform.d.ts +3 -0
- package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
- package/types/lib/helpers/plugins.d.ts +58 -0
- package/types/lib/helpers/plugins.d.ts.map +1 -0
- package/types/lib/helpers/protobom.d.ts +5 -4
- package/types/lib/helpers/protobom.d.ts.map +1 -1
- package/types/lib/helpers/protobomLoader.d.ts +17 -0
- package/types/lib/helpers/protobomLoader.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
- package/types/lib/helpers/source.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +45 -8
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +5 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +2 -1
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts +26 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +2 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/node.d.ts +23 -0
- package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/data/spdx-model-v3.0.1.jsonld +0 -15999
package/bin/cdxgen.js
CHANGED
|
@@ -20,6 +20,7 @@ import { hideBin } from "yargs/helpers";
|
|
|
20
20
|
|
|
21
21
|
import { createBom, submitBom } from "../lib/cli/index.js";
|
|
22
22
|
import { signBom, verifyBom } from "../lib/helpers/bomSigner.js";
|
|
23
|
+
import { isCycloneDxBom } from "../lib/helpers/bomUtils.js";
|
|
23
24
|
import {
|
|
24
25
|
displaySelfThreatModel,
|
|
25
26
|
printActivitySummary,
|
|
@@ -38,7 +39,14 @@ import {
|
|
|
38
39
|
createOutputPlan,
|
|
39
40
|
getOutputDirectory,
|
|
40
41
|
} from "../lib/helpers/exportUtils.js";
|
|
42
|
+
import {
|
|
43
|
+
ensureNoMixedHbomProjectTypes,
|
|
44
|
+
ensureSupportedHbomSpecVersion,
|
|
45
|
+
hasHbomProjectType,
|
|
46
|
+
isHbomOnlyProjectTypes,
|
|
47
|
+
} from "../lib/helpers/hbom.js";
|
|
41
48
|
import { TRACE_MODE, thoughtEnd, thoughtLog } from "../lib/helpers/logger.js";
|
|
49
|
+
import { importProtobomModule } from "../lib/helpers/protobomLoader.js";
|
|
42
50
|
import {
|
|
43
51
|
cleanupSourceDir,
|
|
44
52
|
findGitRefForPurlVersion,
|
|
@@ -57,7 +65,9 @@ import {
|
|
|
57
65
|
import {
|
|
58
66
|
commandsExecuted,
|
|
59
67
|
DEBUG_MODE,
|
|
68
|
+
getDefaultBomAuditCategories,
|
|
60
69
|
getTmpDir,
|
|
70
|
+
isAllowedHttpHost,
|
|
61
71
|
isBun,
|
|
62
72
|
isDeno,
|
|
63
73
|
isDryRun,
|
|
@@ -65,7 +75,9 @@ import {
|
|
|
65
75
|
isNode,
|
|
66
76
|
isSecureMode,
|
|
67
77
|
isWin,
|
|
78
|
+
readEnvironmentVariable,
|
|
68
79
|
recordActivity,
|
|
80
|
+
recordSensitiveFileRead,
|
|
69
81
|
remoteHostsAccessed,
|
|
70
82
|
retrieveCdxgenVersion,
|
|
71
83
|
safeExistsSync,
|
|
@@ -73,6 +85,7 @@ import {
|
|
|
73
85
|
safeWriteSync,
|
|
74
86
|
setActivityContext,
|
|
75
87
|
setDryRunMode,
|
|
88
|
+
shouldRunPredictiveBomAudit,
|
|
76
89
|
toCamel,
|
|
77
90
|
} from "../lib/helpers/utils.js";
|
|
78
91
|
import { postProcess } from "../lib/stages/postgen/postgen.js";
|
|
@@ -118,6 +131,10 @@ for (const configPattern of configPaths) {
|
|
|
118
131
|
}
|
|
119
132
|
|
|
120
133
|
const _yargs = yargs(hideBin(process.argv));
|
|
134
|
+
const invokedCommandName = basename(process.argv[1] || "cdxgen").replace(
|
|
135
|
+
/\.(?:[cm]?js|exe)$/u,
|
|
136
|
+
"",
|
|
137
|
+
);
|
|
121
138
|
|
|
122
139
|
const args = _yargs
|
|
123
140
|
.env("CDXGEN")
|
|
@@ -244,6 +261,12 @@ const args = _yargs
|
|
|
244
261
|
description:
|
|
245
262
|
"Read-only mode. cdxgen only performs file reads and reports blocked writes, command execution, temp creation, network access, and submissions.",
|
|
246
263
|
})
|
|
264
|
+
.option("include-runtime", {
|
|
265
|
+
type: "boolean",
|
|
266
|
+
default: false,
|
|
267
|
+
description:
|
|
268
|
+
"For HBOM runs, also collect OBOM runtime inventory and emit a merged host view with strict hardware/runtime topology links.",
|
|
269
|
+
})
|
|
247
270
|
.option("activity-report", {
|
|
248
271
|
choices: ["json", "jsonl"],
|
|
249
272
|
description: "Render the activity report as JSON or JSON Lines.",
|
|
@@ -321,7 +344,7 @@ const args = _yargs
|
|
|
321
344
|
description: "CycloneDX Specification version to use. Defaults to 1.7",
|
|
322
345
|
default: 1.7,
|
|
323
346
|
type: "number",
|
|
324
|
-
choices: [1.4, 1.5, 1.6, 1.7],
|
|
347
|
+
choices: [1.4, 1.5, 1.6, 1.7, 2.0],
|
|
325
348
|
})
|
|
326
349
|
.option("filter", {
|
|
327
350
|
description:
|
|
@@ -556,6 +579,7 @@ const args = _yargs
|
|
|
556
579
|
"$0 -t java -t js .",
|
|
557
580
|
"Generate a SBOM for Java and JavaScript in the current directory",
|
|
558
581
|
],
|
|
582
|
+
["$0 -t hbom .", "Generate an HBOM for the current host"],
|
|
559
583
|
[
|
|
560
584
|
"$0 -t java --profile ml .",
|
|
561
585
|
"Generate a Java SBOM for machine learning purposes.",
|
|
@@ -568,7 +592,7 @@ const args = _yargs
|
|
|
568
592
|
])
|
|
569
593
|
.epilogue("for documentation, visit https://cdxgen.github.io/cdxgen")
|
|
570
594
|
.config(config)
|
|
571
|
-
.scriptName("cdxgen")
|
|
595
|
+
.scriptName(invokedCommandName || "cdxgen")
|
|
572
596
|
.version(retrieveCdxgenVersion())
|
|
573
597
|
.alias("v", "version")
|
|
574
598
|
.help(false)
|
|
@@ -648,13 +672,13 @@ if (
|
|
|
648
672
|
}
|
|
649
673
|
}
|
|
650
674
|
// Support for obom/cbom aliases
|
|
651
|
-
if (
|
|
652
|
-
args.type = "os";
|
|
675
|
+
if (invokedCommandName.includes("obom") && !args.type) {
|
|
676
|
+
args.type = ["os"];
|
|
653
677
|
thoughtLog(
|
|
654
678
|
"Ok, the user wants to generate an Operations Bill-of-Materials (OBOM).",
|
|
655
679
|
);
|
|
656
680
|
}
|
|
657
|
-
if (
|
|
681
|
+
if (invokedCommandName.includes("spdxgen") && !args.format) {
|
|
658
682
|
args.format = "spdx";
|
|
659
683
|
thoughtLog("Ok, defaulting the export format to SPDX.");
|
|
660
684
|
}
|
|
@@ -699,19 +723,28 @@ for (const outputFile of Object.values(outputPlan.outputs)) {
|
|
|
699
723
|
if (options.projectType && Array.isArray(options.projectType)) {
|
|
700
724
|
options.projectType = Array.from(new Set(options.projectType));
|
|
701
725
|
}
|
|
726
|
+
try {
|
|
727
|
+
ensureNoMixedHbomProjectTypes(options.projectType);
|
|
728
|
+
if (hasHbomProjectType(options.projectType)) {
|
|
729
|
+
ensureSupportedHbomSpecVersion(options.specVersion);
|
|
730
|
+
}
|
|
731
|
+
} catch (error) {
|
|
732
|
+
console.error(error.message);
|
|
733
|
+
process.exit(1);
|
|
734
|
+
}
|
|
702
735
|
if (!options.projectType) {
|
|
703
736
|
thoughtLog(
|
|
704
737
|
"Ok, the user wants me to identify all the project types and generate a consolidated BOM document.",
|
|
705
738
|
);
|
|
706
739
|
}
|
|
707
740
|
// Handle dedicated cbom and saasbom commands
|
|
708
|
-
if (["cbom", "saasbom"].includes(
|
|
709
|
-
if (
|
|
741
|
+
if (["cbom", "saasbom"].includes(invokedCommandName)) {
|
|
742
|
+
if (invokedCommandName.includes("cbom")) {
|
|
710
743
|
thoughtLog(
|
|
711
744
|
"Ok, the user wants to generate Cryptographic Bill-of-Materials (CBOM).",
|
|
712
745
|
);
|
|
713
746
|
options.includeCrypto = true;
|
|
714
|
-
} else if (
|
|
747
|
+
} else if (invokedCommandName.includes("saasbom")) {
|
|
715
748
|
thoughtLog(
|
|
716
749
|
"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.",
|
|
717
750
|
);
|
|
@@ -725,7 +758,7 @@ if (["cbom", "saasbom"].includes(process.argv[1])) {
|
|
|
725
758
|
options.specVersion = 1.7;
|
|
726
759
|
options.deep = true;
|
|
727
760
|
}
|
|
728
|
-
if (
|
|
761
|
+
if (invokedCommandName.includes("cdxgen-secure")) {
|
|
729
762
|
thoughtLog(
|
|
730
763
|
"Ok, the user wants cdxgen to run in secure mode by default. Let's try and use the permissions api.",
|
|
731
764
|
);
|
|
@@ -744,7 +777,16 @@ if (isDryRun) {
|
|
|
744
777
|
if (options.standard) {
|
|
745
778
|
options.specVersion = 1.7;
|
|
746
779
|
}
|
|
747
|
-
|
|
780
|
+
const isHbomOnlyInvocation = isHbomOnlyProjectTypes(options.projectType);
|
|
781
|
+
if (options.includeFormulation && isHbomOnlyInvocation) {
|
|
782
|
+
thoughtLog(
|
|
783
|
+
"HBOM-only invocations do not benefit from formulation data. Let's ignore this option to keep the resulting document focused on hardware inventory.",
|
|
784
|
+
);
|
|
785
|
+
console.log(
|
|
786
|
+
"NOTE: Ignoring formulation collection for HBOM-only invocations because the resulting hardware BOM does not need workflow or dependency-tree enrichment.",
|
|
787
|
+
);
|
|
788
|
+
options.includeFormulation = false;
|
|
789
|
+
} else if (options.includeFormulation) {
|
|
748
790
|
if (options.serverUrl) {
|
|
749
791
|
thoughtLog(
|
|
750
792
|
"Wait, the user specified a server URL and wants to include formulation data. Let's warn about accidentally disclosing sensitive data to a remote server.",
|
|
@@ -898,16 +940,32 @@ const applyAdvancedOptions = (options) => {
|
|
|
898
940
|
);
|
|
899
941
|
}
|
|
900
942
|
if (options.bomAudit) {
|
|
901
|
-
if (
|
|
943
|
+
if (isHbomOnlyInvocation) {
|
|
944
|
+
thoughtLog(
|
|
945
|
+
"HBOM-only bom-audit runs should stay focused on hardware inventory. Skipping automatic formulation collection.",
|
|
946
|
+
);
|
|
947
|
+
} else if (!options.includeFormulation) {
|
|
902
948
|
console.log(
|
|
903
949
|
"NOTE: Automatically collecting formulation information. The section may include sensitive data such as emails and secrets.\nPlease review the generated SBOM before distribution or LLM training.\n",
|
|
904
950
|
);
|
|
951
|
+
options.includeFormulation = true;
|
|
905
952
|
}
|
|
906
|
-
options.includeFormulation = true;
|
|
907
953
|
}
|
|
908
954
|
return options;
|
|
909
955
|
};
|
|
910
956
|
applyAdvancedOptions(options);
|
|
957
|
+
if (options.bomAudit && !options.bomAuditCategories) {
|
|
958
|
+
const defaultBomAuditCategories = getDefaultBomAuditCategories(
|
|
959
|
+
options,
|
|
960
|
+
process.argv[1],
|
|
961
|
+
);
|
|
962
|
+
if (defaultBomAuditCategories) {
|
|
963
|
+
options.bomAuditCategories = defaultBomAuditCategories;
|
|
964
|
+
thoughtLog(
|
|
965
|
+
`Defaulting BOM audit categories to '${defaultBomAuditCategories}' for this OBOM or explicit os-only invocation.`,
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
911
969
|
|
|
912
970
|
const envAuditFindings = auditEnvironment();
|
|
913
971
|
if (options.envAudit) {
|
|
@@ -1063,11 +1121,27 @@ const checkPermissions = (filePath, options) => {
|
|
|
1063
1121
|
|
|
1064
1122
|
const needsBomSigning = ({ generateKeyAndSign }) =>
|
|
1065
1123
|
generateKeyAndSign ||
|
|
1066
|
-
(
|
|
1067
|
-
|
|
1068
|
-
(
|
|
1069
|
-
|
|
1070
|
-
|
|
1124
|
+
(() => {
|
|
1125
|
+
const sbomSignAlgorithm = readEnvironmentVariable("SBOM_SIGN_ALGORITHM");
|
|
1126
|
+
const sbomSignPrivateKey = readEnvironmentVariable(
|
|
1127
|
+
"SBOM_SIGN_PRIVATE_KEY",
|
|
1128
|
+
{
|
|
1129
|
+
sensitive: true,
|
|
1130
|
+
},
|
|
1131
|
+
);
|
|
1132
|
+
const sbomSignPrivateKeyBase64 = readEnvironmentVariable(
|
|
1133
|
+
"SBOM_SIGN_PRIVATE_KEY_BASE64",
|
|
1134
|
+
{
|
|
1135
|
+
sensitive: true,
|
|
1136
|
+
},
|
|
1137
|
+
);
|
|
1138
|
+
return (
|
|
1139
|
+
sbomSignAlgorithm &&
|
|
1140
|
+
sbomSignAlgorithm !== "none" &&
|
|
1141
|
+
((sbomSignPrivateKey && safeExistsSync(sbomSignPrivateKey)) ||
|
|
1142
|
+
sbomSignPrivateKeyBase64)
|
|
1143
|
+
);
|
|
1144
|
+
})();
|
|
1071
1145
|
|
|
1072
1146
|
const stringifyJson = (jsonPayload, jsonPretty) =>
|
|
1073
1147
|
typeof jsonPayload === "string" || jsonPayload instanceof String
|
|
@@ -1096,7 +1170,21 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1096
1170
|
});
|
|
1097
1171
|
return jsonPayload;
|
|
1098
1172
|
}
|
|
1099
|
-
|
|
1173
|
+
const sbomSignAlgorithm = readEnvironmentVariable("SBOM_SIGN_ALGORITHM");
|
|
1174
|
+
const sbomSignPrivateKey = readEnvironmentVariable("SBOM_SIGN_PRIVATE_KEY", {
|
|
1175
|
+
sensitive: true,
|
|
1176
|
+
});
|
|
1177
|
+
const sbomSignPrivateKeyBase64 = readEnvironmentVariable(
|
|
1178
|
+
"SBOM_SIGN_PRIVATE_KEY_BASE64",
|
|
1179
|
+
{
|
|
1180
|
+
sensitive: true,
|
|
1181
|
+
},
|
|
1182
|
+
);
|
|
1183
|
+
const sbomSignPublicKey = readEnvironmentVariable("SBOM_SIGN_PUBLIC_KEY");
|
|
1184
|
+
const sbomSignPublicKeyBase64 = readEnvironmentVariable(
|
|
1185
|
+
"SBOM_SIGN_PUBLIC_KEY_BASE64",
|
|
1186
|
+
);
|
|
1187
|
+
let alg = sbomSignAlgorithm || "RS512";
|
|
1100
1188
|
if (alg.includes("none")) {
|
|
1101
1189
|
alg = "RS512";
|
|
1102
1190
|
}
|
|
@@ -1134,31 +1222,25 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1134
1222
|
privateKeyToUse = privateKey;
|
|
1135
1223
|
jwkPublicKey = crypto.createPublicKey(publicKey).export({ format: "jwk" });
|
|
1136
1224
|
} else {
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
);
|
|
1142
|
-
} else if (
|
|
1225
|
+
if (sbomSignPrivateKey) {
|
|
1226
|
+
recordSensitiveFileRead(sbomSignPrivateKey, {
|
|
1227
|
+
label: "SBOM signing private key",
|
|
1228
|
+
});
|
|
1229
|
+
privateKeyToUse = fs.readFileSync(sbomSignPrivateKey, "utf8");
|
|
1230
|
+
} else if (sbomSignPrivateKeyBase64) {
|
|
1143
1231
|
privateKeyToUse = Buffer.from(
|
|
1144
|
-
|
|
1232
|
+
sbomSignPrivateKeyBase64,
|
|
1145
1233
|
"base64",
|
|
1146
1234
|
).toString("utf8");
|
|
1147
1235
|
}
|
|
1148
|
-
if (
|
|
1149
|
-
process.env.SBOM_SIGN_PUBLIC_KEY &&
|
|
1150
|
-
safeExistsSync(process.env.SBOM_SIGN_PUBLIC_KEY)
|
|
1151
|
-
) {
|
|
1236
|
+
if (sbomSignPublicKey && safeExistsSync(sbomSignPublicKey)) {
|
|
1152
1237
|
jwkPublicKey = crypto
|
|
1153
|
-
.createPublicKey(
|
|
1154
|
-
fs.readFileSync(process.env.SBOM_SIGN_PUBLIC_KEY, "utf8"),
|
|
1155
|
-
)
|
|
1238
|
+
.createPublicKey(fs.readFileSync(sbomSignPublicKey, "utf8"))
|
|
1156
1239
|
.export({ format: "jwk" });
|
|
1157
|
-
} else if (
|
|
1158
|
-
jwkPublicKey = Buffer.from(
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
).toString("utf8");
|
|
1240
|
+
} else if (sbomSignPublicKeyBase64) {
|
|
1241
|
+
jwkPublicKey = Buffer.from(sbomSignPublicKeyBase64, "base64").toString(
|
|
1242
|
+
"utf8",
|
|
1243
|
+
);
|
|
1162
1244
|
}
|
|
1163
1245
|
}
|
|
1164
1246
|
try {
|
|
@@ -1167,7 +1249,7 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1167
1249
|
privateKey: privateKeyToUse,
|
|
1168
1250
|
algorithm: alg,
|
|
1169
1251
|
publicKeyJwk: jwkPublicKey,
|
|
1170
|
-
mode:
|
|
1252
|
+
mode: readEnvironmentVariable("SBOM_SIGN_MODE") || "replace",
|
|
1171
1253
|
signComponents: true,
|
|
1172
1254
|
signServices: true,
|
|
1173
1255
|
signAnnotations: true,
|
|
@@ -1345,10 +1427,16 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1345
1427
|
cleanup = true;
|
|
1346
1428
|
}
|
|
1347
1429
|
setActivityContext({ sourcePath: srcDir });
|
|
1348
|
-
|
|
1430
|
+
if (!hasHbomProjectType(options.projectType)) {
|
|
1431
|
+
prepareEnv(srcDir, options);
|
|
1432
|
+
}
|
|
1349
1433
|
thoughtLog("Getting ready to generate the BOM ⚡️.");
|
|
1350
1434
|
const originalFetchPackageMetadata = process.env.CDXGEN_FETCH_PKG_METADATA;
|
|
1351
|
-
|
|
1435
|
+
const shouldRunPredictiveAudit = shouldRunPredictiveBomAudit(
|
|
1436
|
+
options,
|
|
1437
|
+
process.argv[1],
|
|
1438
|
+
);
|
|
1439
|
+
if (options.bomAudit && shouldRunPredictiveAudit) {
|
|
1352
1440
|
process.env.CDXGEN_FETCH_PKG_METADATA = "true";
|
|
1353
1441
|
}
|
|
1354
1442
|
let bomNSData;
|
|
@@ -1385,8 +1473,10 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1385
1473
|
);
|
|
1386
1474
|
const {
|
|
1387
1475
|
auditBom,
|
|
1476
|
+
formatDryRunSupportSummary,
|
|
1388
1477
|
formatAnnotations,
|
|
1389
1478
|
formatConsoleOutput,
|
|
1479
|
+
getBomAuditDryRunSupportSummary,
|
|
1390
1480
|
hasCriticalFindings,
|
|
1391
1481
|
} = await import("../lib/stages/postgen/auditBom.js");
|
|
1392
1482
|
thoughtLog("Let's run security audit...");
|
|
@@ -1396,6 +1486,15 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1396
1486
|
} else if (DEBUG_MODE) {
|
|
1397
1487
|
console.log("BOM audit: No findings");
|
|
1398
1488
|
}
|
|
1489
|
+
if (isDryRun) {
|
|
1490
|
+
const dryRunSupportSummary =
|
|
1491
|
+
await getBomAuditDryRunSupportSummary(options);
|
|
1492
|
+
const dryRunSupportMessage =
|
|
1493
|
+
formatDryRunSupportSummary(dryRunSupportSummary);
|
|
1494
|
+
if (dryRunSupportMessage) {
|
|
1495
|
+
console.log(dryRunSupportMessage);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1399
1498
|
if (postAuditFindings.length && options.specVersion >= 1.4) {
|
|
1400
1499
|
bomNSData.bomJson.annotations = [
|
|
1401
1500
|
...(bomNSData.bomJson.annotations || []),
|
|
@@ -1416,37 +1515,21 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1416
1515
|
process.exit(1);
|
|
1417
1516
|
}
|
|
1418
1517
|
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
: undefined;
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
],
|
|
1435
|
-
{
|
|
1436
|
-
scope: "required",
|
|
1437
|
-
trusted: predictiveAuditTrusted,
|
|
1438
|
-
},
|
|
1439
|
-
).targets.length;
|
|
1440
|
-
const predictiveAuditMaxTargets =
|
|
1441
|
-
typeof options.bomAuditMaxTargets === "number" &&
|
|
1442
|
-
options.bomAuditMaxTargets > 0
|
|
1443
|
-
? options.bomAuditMaxTargets
|
|
1444
|
-
: predictiveAuditScope === "required"
|
|
1445
|
-
? undefined
|
|
1446
|
-
: Math.max(50, requiredAuditTargetCount);
|
|
1447
|
-
let predictiveReport;
|
|
1448
|
-
try {
|
|
1449
|
-
predictiveReport = await runAuditFromBoms(
|
|
1518
|
+
if (!shouldRunPredictiveAudit) {
|
|
1519
|
+
thoughtLog(
|
|
1520
|
+
"Skipping predictive dependency audit for this OBOM or explicit os-only invocation.",
|
|
1521
|
+
);
|
|
1522
|
+
} else {
|
|
1523
|
+
thoughtLog("Let's run predictive dependency audit...");
|
|
1524
|
+
const progressTracker = createProgressTracker();
|
|
1525
|
+
const predictiveAuditScope =
|
|
1526
|
+
options.bomAuditScope === "required" ? "required" : undefined;
|
|
1527
|
+
const predictiveAuditTrusted = options.bomAuditOnlyTrusted
|
|
1528
|
+
? "only"
|
|
1529
|
+
: options.bomAuditIncludeTrusted
|
|
1530
|
+
? "include"
|
|
1531
|
+
: undefined;
|
|
1532
|
+
const requiredAuditTargetCount = collectAuditTargets(
|
|
1450
1533
|
[
|
|
1451
1534
|
{
|
|
1452
1535
|
bomJson: bomNSData.bomJson,
|
|
@@ -1454,66 +1537,90 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1454
1537
|
},
|
|
1455
1538
|
],
|
|
1456
1539
|
{
|
|
1457
|
-
|
|
1458
|
-
? options.bomAuditCategories
|
|
1459
|
-
.split(",")
|
|
1460
|
-
.map((category) => category.trim())
|
|
1461
|
-
.filter(Boolean)
|
|
1462
|
-
: undefined,
|
|
1463
|
-
failSeverity: options.bomAuditFailSeverity,
|
|
1464
|
-
maxTargets: predictiveAuditMaxTargets,
|
|
1465
|
-
minSeverity: options.bomAuditMinSeverity,
|
|
1466
|
-
onProgress: progressTracker.onProgress,
|
|
1467
|
-
scope: predictiveAuditScope,
|
|
1540
|
+
scope: "required",
|
|
1468
1541
|
trusted: predictiveAuditTrusted,
|
|
1469
|
-
trustedSelectionHelp:
|
|
1470
|
-
"Use --bom-audit-include-trusted to include them or --bom-audit-only-trusted to audit just those packages.",
|
|
1471
1542
|
},
|
|
1472
|
-
);
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1543
|
+
).targets.length;
|
|
1544
|
+
const predictiveAuditMaxTargets =
|
|
1545
|
+
typeof options.bomAuditMaxTargets === "number" &&
|
|
1546
|
+
options.bomAuditMaxTargets > 0
|
|
1547
|
+
? options.bomAuditMaxTargets
|
|
1548
|
+
: predictiveAuditScope === "required"
|
|
1549
|
+
? undefined
|
|
1550
|
+
: Math.max(50, requiredAuditTargetCount);
|
|
1551
|
+
let predictiveReport;
|
|
1552
|
+
try {
|
|
1553
|
+
predictiveReport = await runAuditFromBoms(
|
|
1554
|
+
[
|
|
1555
|
+
{
|
|
1556
|
+
bomJson: bomNSData.bomJson,
|
|
1557
|
+
source: filePath,
|
|
1558
|
+
},
|
|
1559
|
+
],
|
|
1560
|
+
{
|
|
1561
|
+
categories: options.bomAuditCategories
|
|
1562
|
+
? options.bomAuditCategories
|
|
1563
|
+
.split(",")
|
|
1564
|
+
.map((category) => category.trim())
|
|
1565
|
+
.filter(Boolean)
|
|
1566
|
+
: undefined,
|
|
1567
|
+
failSeverity: options.bomAuditFailSeverity,
|
|
1568
|
+
maxTargets: predictiveAuditMaxTargets,
|
|
1569
|
+
minSeverity: options.bomAuditMinSeverity,
|
|
1570
|
+
onProgress: progressTracker.onProgress,
|
|
1571
|
+
scope: predictiveAuditScope,
|
|
1572
|
+
trusted: predictiveAuditTrusted,
|
|
1573
|
+
trustedSelectionHelp:
|
|
1574
|
+
"Use --bom-audit-include-trusted to include them or --bom-audit-only-trusted to audit just those packages.",
|
|
1575
|
+
},
|
|
1576
|
+
);
|
|
1577
|
+
} finally {
|
|
1578
|
+
progressTracker.stop();
|
|
1579
|
+
}
|
|
1580
|
+
if (predictiveReport.summary.totalTargets > 0) {
|
|
1581
|
+
process.stderr.write(
|
|
1582
|
+
renderConsoleReport(predictiveReport, {
|
|
1583
|
+
minSeverity: options.bomAuditMinSeverity,
|
|
1584
|
+
}),
|
|
1585
|
+
);
|
|
1586
|
+
} else if (DEBUG_MODE) {
|
|
1587
|
+
console.log(
|
|
1588
|
+
"Predictive BOM audit: No supported npm/PyPI targets found",
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
const predictiveAnnotations = formatPredictiveAnnotations(
|
|
1592
|
+
predictiveReport,
|
|
1593
|
+
bomNSData.bomJson,
|
|
1594
|
+
{
|
|
1479
1595
|
minSeverity: options.bomAuditMinSeverity,
|
|
1480
|
-
}
|
|
1596
|
+
},
|
|
1481
1597
|
);
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1598
|
+
if (predictiveAnnotations.length && options.specVersion >= 1.4) {
|
|
1599
|
+
bomNSData.bomJson.annotations = [
|
|
1600
|
+
...(bomNSData.bomJson.annotations || []),
|
|
1601
|
+
...predictiveAnnotations,
|
|
1602
|
+
];
|
|
1603
|
+
thoughtLog(
|
|
1604
|
+
`Embedded ${predictiveAnnotations.length} predictive audit annotations`,
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
const predictiveResult = finalizeAuditReport(predictiveReport, {
|
|
1608
|
+
failSeverity: options.bomAuditFailSeverity,
|
|
1489
1609
|
minSeverity: options.bomAuditMinSeverity,
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
minSeverity: options.bomAuditMinSeverity,
|
|
1504
|
-
report: "console",
|
|
1505
|
-
});
|
|
1506
|
-
if (isSecureMode && predictiveResult.exitCode === 3) {
|
|
1507
|
-
console.error(
|
|
1508
|
-
"\nSecure mode: Predictive audit findings exceeded the configured threshold.",
|
|
1509
|
-
);
|
|
1510
|
-
console.error(
|
|
1511
|
-
"Review findings above or adjust --bom-audit-fail-severity to proceed.",
|
|
1512
|
-
);
|
|
1513
|
-
if (cleanup) {
|
|
1514
|
-
cleanupSourceDir(srcDir);
|
|
1610
|
+
report: "console",
|
|
1611
|
+
});
|
|
1612
|
+
if (isSecureMode && predictiveResult.exitCode === 3) {
|
|
1613
|
+
console.error(
|
|
1614
|
+
"\nSecure mode: Predictive audit findings exceeded the configured threshold.",
|
|
1615
|
+
);
|
|
1616
|
+
console.error(
|
|
1617
|
+
"Review findings above or adjust --bom-audit-fail-severity to proceed.",
|
|
1618
|
+
);
|
|
1619
|
+
if (cleanup) {
|
|
1620
|
+
cleanupSourceDir(srcDir);
|
|
1621
|
+
}
|
|
1622
|
+
process.exit(1);
|
|
1515
1623
|
}
|
|
1516
|
-
process.exit(1);
|
|
1517
1624
|
}
|
|
1518
1625
|
}
|
|
1519
1626
|
let internalCycloneDxInputPath = outputPlan.outputs.cyclonedx;
|
|
@@ -1609,7 +1716,7 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1609
1716
|
if (
|
|
1610
1717
|
outputPlan.formats.has("spdx") &&
|
|
1611
1718
|
bomNSData?.bomJson &&
|
|
1612
|
-
bomNSData
|
|
1719
|
+
isCycloneDxBom(bomNSData.bomJson)
|
|
1613
1720
|
) {
|
|
1614
1721
|
thoughtLog(
|
|
1615
1722
|
"Preparing the SPDX 3.0.1 export from the validated CycloneDX BOM.",
|
|
@@ -1684,6 +1791,27 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1684
1791
|
// Automatically submit the bom data
|
|
1685
1792
|
// biome-ignore lint/suspicious/noDoubleEquals: yargs passes true for empty values
|
|
1686
1793
|
if (options.serverUrl && options.serverUrl != true && options.apiKey) {
|
|
1794
|
+
if (isSecureMode) {
|
|
1795
|
+
let serverHostname;
|
|
1796
|
+
try {
|
|
1797
|
+
serverHostname = new URL(options.serverUrl).hostname;
|
|
1798
|
+
} catch (err) {
|
|
1799
|
+
console.log("Invalid Dependency-Track server URL", err);
|
|
1800
|
+
process.exit(1);
|
|
1801
|
+
}
|
|
1802
|
+
if (!isAllowedHttpHost(serverHostname)) {
|
|
1803
|
+
recordActivity({
|
|
1804
|
+
kind: "submit",
|
|
1805
|
+
reason: "The URL host is not allowed as per the allowlist.",
|
|
1806
|
+
status: "blocked",
|
|
1807
|
+
target: options.serverUrl,
|
|
1808
|
+
});
|
|
1809
|
+
console.log(
|
|
1810
|
+
`Dependency-Track server host '${serverHostname}' is not allowed by CDXGEN_ALLOWED_HOSTS.`,
|
|
1811
|
+
);
|
|
1812
|
+
process.exit(1);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1687
1815
|
if (isDryRun) {
|
|
1688
1816
|
recordActivity({
|
|
1689
1817
|
kind: "submit",
|
|
@@ -1718,7 +1846,22 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1718
1846
|
target: options.protoBinFile,
|
|
1719
1847
|
});
|
|
1720
1848
|
} else {
|
|
1721
|
-
const protobomModule = await
|
|
1849
|
+
const protobomModule = await importProtobomModule(
|
|
1850
|
+
invokedCommandName || "cdxgen",
|
|
1851
|
+
"protobuf export",
|
|
1852
|
+
);
|
|
1853
|
+
try {
|
|
1854
|
+
protobomModule.assertProtoSupportedSpecVersion(
|
|
1855
|
+
bomNSData?.bomJson?.specVersion || options.specVersion,
|
|
1856
|
+
"protobuf export",
|
|
1857
|
+
);
|
|
1858
|
+
} catch (error) {
|
|
1859
|
+
console.error(error.message);
|
|
1860
|
+
if (cleanup) {
|
|
1861
|
+
cleanupSourceDir(srcDir);
|
|
1862
|
+
}
|
|
1863
|
+
process.exit(1);
|
|
1864
|
+
}
|
|
1722
1865
|
protobomModule.writeBinary(bomNSData.bomJson, options.protoBinFile);
|
|
1723
1866
|
thoughtLog("BOM file is also available in .proto format!");
|
|
1724
1867
|
}
|