@cyclonedx/cdxgen 12.3.2 → 12.4.0
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 +70 -22
- package/bin/audit.js +21 -7
- package/bin/cdxgen.js +238 -116
- package/bin/convert.js +28 -13
- package/bin/hbom.js +490 -0
- package/bin/repl.js +580 -29
- package/bin/validate.js +34 -4
- package/bin/verify.js +40 -5
- package/data/README.md +298 -25
- package/data/component-tags.json +6 -0
- package/data/crypto-oid.json +16 -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 +171 -15
- package/data/rules/container-risk.yaml +14 -7
- package/data/rules/dependency-sources.yaml +76 -5
- 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 +36 -0
- package/data/rules/rootfs-hardening.yaml +179 -0
- package/data/rules/vscode-extensions.yaml +9 -0
- package/lib/audit/index.js +209 -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 +647 -127
- package/lib/cli/index.poku.js +1905 -187
- package/lib/evinser/evinser.js +14 -9
- package/lib/helpers/agentFormulationParser.js +6 -2
- package/lib/helpers/agentFormulationParser.poku.js +42 -0
- package/lib/helpers/analyzer.js +1444 -38
- package/lib/helpers/analyzer.poku.js +409 -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/cbomutils.js +271 -1
- package/lib/helpers/cbomutils.poku.js +248 -5
- package/lib/helpers/chromextutils.js +25 -3
- package/lib/helpers/chromextutils.poku.js +68 -0
- package/lib/helpers/ciParsers/githubActions.js +79 -0
- package/lib/helpers/ciParsers/githubActions.poku.js +103 -0
- package/lib/helpers/communityAiConfigParser.js +15 -5
- package/lib/helpers/communityAiConfigParser.poku.js +71 -0
- package/lib/helpers/depsUtils.js +5 -0
- package/lib/helpers/depsUtils.poku.js +55 -0
- package/lib/helpers/display.js +336 -23
- package/lib/helpers/display.poku.js +179 -43
- 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/mcpConfigParser.js +21 -5
- package/lib/helpers/mcpConfigParser.poku.js +39 -2
- package/lib/helpers/osqueryTransform.js +47 -0
- package/lib/helpers/osqueryTransform.poku.js +47 -0
- package/lib/helpers/plugins.js +349 -0
- package/lib/helpers/plugins.poku.js +57 -0
- package/lib/helpers/propertySanitizer.js +121 -0
- package/lib/helpers/protobom.js +156 -45
- package/lib/helpers/protobom.poku.js +140 -5
- 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 +2454 -198
- package/lib/helpers/utils.poku.js +1798 -74
- package/lib/managers/binary.e2e.poku.js +367 -0
- package/lib/managers/binary.js +2306 -350
- package/lib/managers/binary.poku.js +1700 -1
- package/lib/managers/docker.js +441 -95
- package/lib/managers/docker.poku.js +1479 -14
- package/lib/server/server.js +2 -24
- package/lib/server/server.poku.js +36 -1
- 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 +2967 -990
- package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
- package/lib/stages/postgen/postgen.js +192 -1
- package/lib/stages/postgen/postgen.poku.js +321 -0
- package/lib/stages/postgen/ruleEngine.js +116 -0
- package/lib/stages/pregen/envAudit.js +14 -3
- package/package.json +24 -21
- package/types/bin/hbom.d.ts +3 -0
- package/types/bin/hbom.d.ts.map +1 -0
- 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/agentFormulationParser.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/cbomutils.d.ts +3 -2
- package/types/lib/helpers/cbomutils.d.ts.map +1 -1
- package/types/lib/helpers/chromextutils.d.ts.map +1 -1
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/helpers/depsUtils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts +1 -0
- 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 +62 -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/mcpConfigParser.d.ts +1 -1
- package/types/lib/helpers/mcpConfigParser.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/propertySanitizer.d.ts +3 -0
- package/types/lib/helpers/propertySanitizer.d.ts.map +1 -0
- package/types/lib/helpers/protobom.d.ts +3 -4
- package/types/lib/helpers/protobom.d.ts.map +1 -1
- 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 +74 -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 +3 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +2 -0
- 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/data/spdx-model-v3.0.1.jsonld +0 -15999
package/bin/cdxgen.js
CHANGED
|
@@ -38,6 +38,12 @@ import {
|
|
|
38
38
|
createOutputPlan,
|
|
39
39
|
getOutputDirectory,
|
|
40
40
|
} from "../lib/helpers/exportUtils.js";
|
|
41
|
+
import {
|
|
42
|
+
ensureNoMixedHbomProjectTypes,
|
|
43
|
+
ensureSupportedHbomSpecVersion,
|
|
44
|
+
hasHbomProjectType,
|
|
45
|
+
isHbomOnlyProjectTypes,
|
|
46
|
+
} from "../lib/helpers/hbom.js";
|
|
41
47
|
import { TRACE_MODE, thoughtEnd, thoughtLog } from "../lib/helpers/logger.js";
|
|
42
48
|
import {
|
|
43
49
|
cleanupSourceDir,
|
|
@@ -57,7 +63,9 @@ import {
|
|
|
57
63
|
import {
|
|
58
64
|
commandsExecuted,
|
|
59
65
|
DEBUG_MODE,
|
|
66
|
+
getDefaultBomAuditCategories,
|
|
60
67
|
getTmpDir,
|
|
68
|
+
isAllowedHttpHost,
|
|
61
69
|
isBun,
|
|
62
70
|
isDeno,
|
|
63
71
|
isDryRun,
|
|
@@ -65,7 +73,9 @@ import {
|
|
|
65
73
|
isNode,
|
|
66
74
|
isSecureMode,
|
|
67
75
|
isWin,
|
|
76
|
+
readEnvironmentVariable,
|
|
68
77
|
recordActivity,
|
|
78
|
+
recordSensitiveFileRead,
|
|
69
79
|
remoteHostsAccessed,
|
|
70
80
|
retrieveCdxgenVersion,
|
|
71
81
|
safeExistsSync,
|
|
@@ -73,6 +83,7 @@ import {
|
|
|
73
83
|
safeWriteSync,
|
|
74
84
|
setActivityContext,
|
|
75
85
|
setDryRunMode,
|
|
86
|
+
shouldRunPredictiveBomAudit,
|
|
76
87
|
toCamel,
|
|
77
88
|
} from "../lib/helpers/utils.js";
|
|
78
89
|
import { postProcess } from "../lib/stages/postgen/postgen.js";
|
|
@@ -244,6 +255,12 @@ const args = _yargs
|
|
|
244
255
|
description:
|
|
245
256
|
"Read-only mode. cdxgen only performs file reads and reports blocked writes, command execution, temp creation, network access, and submissions.",
|
|
246
257
|
})
|
|
258
|
+
.option("include-runtime", {
|
|
259
|
+
type: "boolean",
|
|
260
|
+
default: false,
|
|
261
|
+
description:
|
|
262
|
+
"For HBOM runs, also collect OBOM runtime inventory and emit a merged host view with strict hardware/runtime topology links.",
|
|
263
|
+
})
|
|
247
264
|
.option("activity-report", {
|
|
248
265
|
choices: ["json", "jsonl"],
|
|
249
266
|
description: "Render the activity report as JSON or JSON Lines.",
|
|
@@ -556,6 +573,7 @@ const args = _yargs
|
|
|
556
573
|
"$0 -t java -t js .",
|
|
557
574
|
"Generate a SBOM for Java and JavaScript in the current directory",
|
|
558
575
|
],
|
|
576
|
+
["$0 -t hbom .", "Generate an HBOM for the current host"],
|
|
559
577
|
[
|
|
560
578
|
"$0 -t java --profile ml .",
|
|
561
579
|
"Generate a Java SBOM for machine learning purposes.",
|
|
@@ -699,6 +717,15 @@ for (const outputFile of Object.values(outputPlan.outputs)) {
|
|
|
699
717
|
if (options.projectType && Array.isArray(options.projectType)) {
|
|
700
718
|
options.projectType = Array.from(new Set(options.projectType));
|
|
701
719
|
}
|
|
720
|
+
try {
|
|
721
|
+
ensureNoMixedHbomProjectTypes(options.projectType);
|
|
722
|
+
if (hasHbomProjectType(options.projectType)) {
|
|
723
|
+
ensureSupportedHbomSpecVersion(options.specVersion);
|
|
724
|
+
}
|
|
725
|
+
} catch (error) {
|
|
726
|
+
console.error(error.message);
|
|
727
|
+
process.exit(1);
|
|
728
|
+
}
|
|
702
729
|
if (!options.projectType) {
|
|
703
730
|
thoughtLog(
|
|
704
731
|
"Ok, the user wants me to identify all the project types and generate a consolidated BOM document.",
|
|
@@ -744,7 +771,16 @@ if (isDryRun) {
|
|
|
744
771
|
if (options.standard) {
|
|
745
772
|
options.specVersion = 1.7;
|
|
746
773
|
}
|
|
747
|
-
|
|
774
|
+
const isHbomOnlyInvocation = isHbomOnlyProjectTypes(options.projectType);
|
|
775
|
+
if (options.includeFormulation && isHbomOnlyInvocation) {
|
|
776
|
+
thoughtLog(
|
|
777
|
+
"HBOM-only invocations do not benefit from formulation data. Let's ignore this option to keep the resulting document focused on hardware inventory.",
|
|
778
|
+
);
|
|
779
|
+
console.log(
|
|
780
|
+
"NOTE: Ignoring formulation collection for HBOM-only invocations because the resulting hardware BOM does not need workflow or dependency-tree enrichment.",
|
|
781
|
+
);
|
|
782
|
+
options.includeFormulation = false;
|
|
783
|
+
} else if (options.includeFormulation) {
|
|
748
784
|
if (options.serverUrl) {
|
|
749
785
|
thoughtLog(
|
|
750
786
|
"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 +934,32 @@ const applyAdvancedOptions = (options) => {
|
|
|
898
934
|
);
|
|
899
935
|
}
|
|
900
936
|
if (options.bomAudit) {
|
|
901
|
-
if (
|
|
937
|
+
if (isHbomOnlyInvocation) {
|
|
938
|
+
thoughtLog(
|
|
939
|
+
"HBOM-only bom-audit runs should stay focused on hardware inventory. Skipping automatic formulation collection.",
|
|
940
|
+
);
|
|
941
|
+
} else if (!options.includeFormulation) {
|
|
902
942
|
console.log(
|
|
903
943
|
"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
944
|
);
|
|
945
|
+
options.includeFormulation = true;
|
|
905
946
|
}
|
|
906
|
-
options.includeFormulation = true;
|
|
907
947
|
}
|
|
908
948
|
return options;
|
|
909
949
|
};
|
|
910
950
|
applyAdvancedOptions(options);
|
|
951
|
+
if (options.bomAudit && !options.bomAuditCategories) {
|
|
952
|
+
const defaultBomAuditCategories = getDefaultBomAuditCategories(
|
|
953
|
+
options,
|
|
954
|
+
process.argv[1],
|
|
955
|
+
);
|
|
956
|
+
if (defaultBomAuditCategories) {
|
|
957
|
+
options.bomAuditCategories = defaultBomAuditCategories;
|
|
958
|
+
thoughtLog(
|
|
959
|
+
`Defaulting BOM audit categories to '${defaultBomAuditCategories}' for this OBOM or explicit os-only invocation.`,
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
911
963
|
|
|
912
964
|
const envAuditFindings = auditEnvironment();
|
|
913
965
|
if (options.envAudit) {
|
|
@@ -1063,11 +1115,27 @@ const checkPermissions = (filePath, options) => {
|
|
|
1063
1115
|
|
|
1064
1116
|
const needsBomSigning = ({ generateKeyAndSign }) =>
|
|
1065
1117
|
generateKeyAndSign ||
|
|
1066
|
-
(
|
|
1067
|
-
|
|
1068
|
-
(
|
|
1069
|
-
|
|
1070
|
-
|
|
1118
|
+
(() => {
|
|
1119
|
+
const sbomSignAlgorithm = readEnvironmentVariable("SBOM_SIGN_ALGORITHM");
|
|
1120
|
+
const sbomSignPrivateKey = readEnvironmentVariable(
|
|
1121
|
+
"SBOM_SIGN_PRIVATE_KEY",
|
|
1122
|
+
{
|
|
1123
|
+
sensitive: true,
|
|
1124
|
+
},
|
|
1125
|
+
);
|
|
1126
|
+
const sbomSignPrivateKeyBase64 = readEnvironmentVariable(
|
|
1127
|
+
"SBOM_SIGN_PRIVATE_KEY_BASE64",
|
|
1128
|
+
{
|
|
1129
|
+
sensitive: true,
|
|
1130
|
+
},
|
|
1131
|
+
);
|
|
1132
|
+
return (
|
|
1133
|
+
sbomSignAlgorithm &&
|
|
1134
|
+
sbomSignAlgorithm !== "none" &&
|
|
1135
|
+
((sbomSignPrivateKey && safeExistsSync(sbomSignPrivateKey)) ||
|
|
1136
|
+
sbomSignPrivateKeyBase64)
|
|
1137
|
+
);
|
|
1138
|
+
})();
|
|
1071
1139
|
|
|
1072
1140
|
const stringifyJson = (jsonPayload, jsonPretty) =>
|
|
1073
1141
|
typeof jsonPayload === "string" || jsonPayload instanceof String
|
|
@@ -1096,7 +1164,21 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1096
1164
|
});
|
|
1097
1165
|
return jsonPayload;
|
|
1098
1166
|
}
|
|
1099
|
-
|
|
1167
|
+
const sbomSignAlgorithm = readEnvironmentVariable("SBOM_SIGN_ALGORITHM");
|
|
1168
|
+
const sbomSignPrivateKey = readEnvironmentVariable("SBOM_SIGN_PRIVATE_KEY", {
|
|
1169
|
+
sensitive: true,
|
|
1170
|
+
});
|
|
1171
|
+
const sbomSignPrivateKeyBase64 = readEnvironmentVariable(
|
|
1172
|
+
"SBOM_SIGN_PRIVATE_KEY_BASE64",
|
|
1173
|
+
{
|
|
1174
|
+
sensitive: true,
|
|
1175
|
+
},
|
|
1176
|
+
);
|
|
1177
|
+
const sbomSignPublicKey = readEnvironmentVariable("SBOM_SIGN_PUBLIC_KEY");
|
|
1178
|
+
const sbomSignPublicKeyBase64 = readEnvironmentVariable(
|
|
1179
|
+
"SBOM_SIGN_PUBLIC_KEY_BASE64",
|
|
1180
|
+
);
|
|
1181
|
+
let alg = sbomSignAlgorithm || "RS512";
|
|
1100
1182
|
if (alg.includes("none")) {
|
|
1101
1183
|
alg = "RS512";
|
|
1102
1184
|
}
|
|
@@ -1134,31 +1216,25 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1134
1216
|
privateKeyToUse = privateKey;
|
|
1135
1217
|
jwkPublicKey = crypto.createPublicKey(publicKey).export({ format: "jwk" });
|
|
1136
1218
|
} else {
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
);
|
|
1142
|
-
} else if (
|
|
1219
|
+
if (sbomSignPrivateKey) {
|
|
1220
|
+
recordSensitiveFileRead(sbomSignPrivateKey, {
|
|
1221
|
+
label: "SBOM signing private key",
|
|
1222
|
+
});
|
|
1223
|
+
privateKeyToUse = fs.readFileSync(sbomSignPrivateKey, "utf8");
|
|
1224
|
+
} else if (sbomSignPrivateKeyBase64) {
|
|
1143
1225
|
privateKeyToUse = Buffer.from(
|
|
1144
|
-
|
|
1226
|
+
sbomSignPrivateKeyBase64,
|
|
1145
1227
|
"base64",
|
|
1146
1228
|
).toString("utf8");
|
|
1147
1229
|
}
|
|
1148
|
-
if (
|
|
1149
|
-
process.env.SBOM_SIGN_PUBLIC_KEY &&
|
|
1150
|
-
safeExistsSync(process.env.SBOM_SIGN_PUBLIC_KEY)
|
|
1151
|
-
) {
|
|
1230
|
+
if (sbomSignPublicKey && safeExistsSync(sbomSignPublicKey)) {
|
|
1152
1231
|
jwkPublicKey = crypto
|
|
1153
|
-
.createPublicKey(
|
|
1154
|
-
fs.readFileSync(process.env.SBOM_SIGN_PUBLIC_KEY, "utf8"),
|
|
1155
|
-
)
|
|
1232
|
+
.createPublicKey(fs.readFileSync(sbomSignPublicKey, "utf8"))
|
|
1156
1233
|
.export({ format: "jwk" });
|
|
1157
|
-
} else if (
|
|
1158
|
-
jwkPublicKey = Buffer.from(
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
).toString("utf8");
|
|
1234
|
+
} else if (sbomSignPublicKeyBase64) {
|
|
1235
|
+
jwkPublicKey = Buffer.from(sbomSignPublicKeyBase64, "base64").toString(
|
|
1236
|
+
"utf8",
|
|
1237
|
+
);
|
|
1162
1238
|
}
|
|
1163
1239
|
}
|
|
1164
1240
|
try {
|
|
@@ -1167,7 +1243,7 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1167
1243
|
privateKey: privateKeyToUse,
|
|
1168
1244
|
algorithm: alg,
|
|
1169
1245
|
publicKeyJwk: jwkPublicKey,
|
|
1170
|
-
mode:
|
|
1246
|
+
mode: readEnvironmentVariable("SBOM_SIGN_MODE") || "replace",
|
|
1171
1247
|
signComponents: true,
|
|
1172
1248
|
signServices: true,
|
|
1173
1249
|
signAnnotations: true,
|
|
@@ -1345,10 +1421,16 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1345
1421
|
cleanup = true;
|
|
1346
1422
|
}
|
|
1347
1423
|
setActivityContext({ sourcePath: srcDir });
|
|
1348
|
-
|
|
1424
|
+
if (!hasHbomProjectType(options.projectType)) {
|
|
1425
|
+
prepareEnv(srcDir, options);
|
|
1426
|
+
}
|
|
1349
1427
|
thoughtLog("Getting ready to generate the BOM ⚡️.");
|
|
1350
1428
|
const originalFetchPackageMetadata = process.env.CDXGEN_FETCH_PKG_METADATA;
|
|
1351
|
-
|
|
1429
|
+
const shouldRunPredictiveAudit = shouldRunPredictiveBomAudit(
|
|
1430
|
+
options,
|
|
1431
|
+
process.argv[1],
|
|
1432
|
+
);
|
|
1433
|
+
if (options.bomAudit && shouldRunPredictiveAudit) {
|
|
1352
1434
|
process.env.CDXGEN_FETCH_PKG_METADATA = "true";
|
|
1353
1435
|
}
|
|
1354
1436
|
let bomNSData;
|
|
@@ -1385,8 +1467,10 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1385
1467
|
);
|
|
1386
1468
|
const {
|
|
1387
1469
|
auditBom,
|
|
1470
|
+
formatDryRunSupportSummary,
|
|
1388
1471
|
formatAnnotations,
|
|
1389
1472
|
formatConsoleOutput,
|
|
1473
|
+
getBomAuditDryRunSupportSummary,
|
|
1390
1474
|
hasCriticalFindings,
|
|
1391
1475
|
} = await import("../lib/stages/postgen/auditBom.js");
|
|
1392
1476
|
thoughtLog("Let's run security audit...");
|
|
@@ -1396,6 +1480,15 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1396
1480
|
} else if (DEBUG_MODE) {
|
|
1397
1481
|
console.log("BOM audit: No findings");
|
|
1398
1482
|
}
|
|
1483
|
+
if (isDryRun) {
|
|
1484
|
+
const dryRunSupportSummary =
|
|
1485
|
+
await getBomAuditDryRunSupportSummary(options);
|
|
1486
|
+
const dryRunSupportMessage =
|
|
1487
|
+
formatDryRunSupportSummary(dryRunSupportSummary);
|
|
1488
|
+
if (dryRunSupportMessage) {
|
|
1489
|
+
console.log(dryRunSupportMessage);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1399
1492
|
if (postAuditFindings.length && options.specVersion >= 1.4) {
|
|
1400
1493
|
bomNSData.bomJson.annotations = [
|
|
1401
1494
|
...(bomNSData.bomJson.annotations || []),
|
|
@@ -1416,37 +1509,21 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1416
1509
|
process.exit(1);
|
|
1417
1510
|
}
|
|
1418
1511
|
|
|
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(
|
|
1512
|
+
if (!shouldRunPredictiveAudit) {
|
|
1513
|
+
thoughtLog(
|
|
1514
|
+
"Skipping predictive dependency audit for this OBOM or explicit os-only invocation.",
|
|
1515
|
+
);
|
|
1516
|
+
} else {
|
|
1517
|
+
thoughtLog("Let's run predictive dependency audit...");
|
|
1518
|
+
const progressTracker = createProgressTracker();
|
|
1519
|
+
const predictiveAuditScope =
|
|
1520
|
+
options.bomAuditScope === "required" ? "required" : undefined;
|
|
1521
|
+
const predictiveAuditTrusted = options.bomAuditOnlyTrusted
|
|
1522
|
+
? "only"
|
|
1523
|
+
: options.bomAuditIncludeTrusted
|
|
1524
|
+
? "include"
|
|
1525
|
+
: undefined;
|
|
1526
|
+
const requiredAuditTargetCount = collectAuditTargets(
|
|
1450
1527
|
[
|
|
1451
1528
|
{
|
|
1452
1529
|
bomJson: bomNSData.bomJson,
|
|
@@ -1454,66 +1531,90 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1454
1531
|
},
|
|
1455
1532
|
],
|
|
1456
1533
|
{
|
|
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,
|
|
1534
|
+
scope: "required",
|
|
1468
1535
|
trusted: predictiveAuditTrusted,
|
|
1469
|
-
trustedSelectionHelp:
|
|
1470
|
-
"Use --bom-audit-include-trusted to include them or --bom-audit-only-trusted to audit just those packages.",
|
|
1471
1536
|
},
|
|
1472
|
-
);
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1537
|
+
).targets.length;
|
|
1538
|
+
const predictiveAuditMaxTargets =
|
|
1539
|
+
typeof options.bomAuditMaxTargets === "number" &&
|
|
1540
|
+
options.bomAuditMaxTargets > 0
|
|
1541
|
+
? options.bomAuditMaxTargets
|
|
1542
|
+
: predictiveAuditScope === "required"
|
|
1543
|
+
? undefined
|
|
1544
|
+
: Math.max(50, requiredAuditTargetCount);
|
|
1545
|
+
let predictiveReport;
|
|
1546
|
+
try {
|
|
1547
|
+
predictiveReport = await runAuditFromBoms(
|
|
1548
|
+
[
|
|
1549
|
+
{
|
|
1550
|
+
bomJson: bomNSData.bomJson,
|
|
1551
|
+
source: filePath,
|
|
1552
|
+
},
|
|
1553
|
+
],
|
|
1554
|
+
{
|
|
1555
|
+
categories: options.bomAuditCategories
|
|
1556
|
+
? options.bomAuditCategories
|
|
1557
|
+
.split(",")
|
|
1558
|
+
.map((category) => category.trim())
|
|
1559
|
+
.filter(Boolean)
|
|
1560
|
+
: undefined,
|
|
1561
|
+
failSeverity: options.bomAuditFailSeverity,
|
|
1562
|
+
maxTargets: predictiveAuditMaxTargets,
|
|
1563
|
+
minSeverity: options.bomAuditMinSeverity,
|
|
1564
|
+
onProgress: progressTracker.onProgress,
|
|
1565
|
+
scope: predictiveAuditScope,
|
|
1566
|
+
trusted: predictiveAuditTrusted,
|
|
1567
|
+
trustedSelectionHelp:
|
|
1568
|
+
"Use --bom-audit-include-trusted to include them or --bom-audit-only-trusted to audit just those packages.",
|
|
1569
|
+
},
|
|
1570
|
+
);
|
|
1571
|
+
} finally {
|
|
1572
|
+
progressTracker.stop();
|
|
1573
|
+
}
|
|
1574
|
+
if (predictiveReport.summary.totalTargets > 0) {
|
|
1575
|
+
process.stderr.write(
|
|
1576
|
+
renderConsoleReport(predictiveReport, {
|
|
1577
|
+
minSeverity: options.bomAuditMinSeverity,
|
|
1578
|
+
}),
|
|
1579
|
+
);
|
|
1580
|
+
} else if (DEBUG_MODE) {
|
|
1581
|
+
console.log(
|
|
1582
|
+
"Predictive BOM audit: No supported npm/PyPI targets found",
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
const predictiveAnnotations = formatPredictiveAnnotations(
|
|
1586
|
+
predictiveReport,
|
|
1587
|
+
bomNSData.bomJson,
|
|
1588
|
+
{
|
|
1479
1589
|
minSeverity: options.bomAuditMinSeverity,
|
|
1480
|
-
}
|
|
1590
|
+
},
|
|
1481
1591
|
);
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1592
|
+
if (predictiveAnnotations.length && options.specVersion >= 1.4) {
|
|
1593
|
+
bomNSData.bomJson.annotations = [
|
|
1594
|
+
...(bomNSData.bomJson.annotations || []),
|
|
1595
|
+
...predictiveAnnotations,
|
|
1596
|
+
];
|
|
1597
|
+
thoughtLog(
|
|
1598
|
+
`Embedded ${predictiveAnnotations.length} predictive audit annotations`,
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1601
|
+
const predictiveResult = finalizeAuditReport(predictiveReport, {
|
|
1602
|
+
failSeverity: options.bomAuditFailSeverity,
|
|
1489
1603
|
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);
|
|
1604
|
+
report: "console",
|
|
1605
|
+
});
|
|
1606
|
+
if (isSecureMode && predictiveResult.exitCode === 3) {
|
|
1607
|
+
console.error(
|
|
1608
|
+
"\nSecure mode: Predictive audit findings exceeded the configured threshold.",
|
|
1609
|
+
);
|
|
1610
|
+
console.error(
|
|
1611
|
+
"Review findings above or adjust --bom-audit-fail-severity to proceed.",
|
|
1612
|
+
);
|
|
1613
|
+
if (cleanup) {
|
|
1614
|
+
cleanupSourceDir(srcDir);
|
|
1615
|
+
}
|
|
1616
|
+
process.exit(1);
|
|
1515
1617
|
}
|
|
1516
|
-
process.exit(1);
|
|
1517
1618
|
}
|
|
1518
1619
|
}
|
|
1519
1620
|
let internalCycloneDxInputPath = outputPlan.outputs.cyclonedx;
|
|
@@ -1684,6 +1785,27 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
|
|
|
1684
1785
|
// Automatically submit the bom data
|
|
1685
1786
|
// biome-ignore lint/suspicious/noDoubleEquals: yargs passes true for empty values
|
|
1686
1787
|
if (options.serverUrl && options.serverUrl != true && options.apiKey) {
|
|
1788
|
+
if (isSecureMode) {
|
|
1789
|
+
let serverHostname;
|
|
1790
|
+
try {
|
|
1791
|
+
serverHostname = new URL(options.serverUrl).hostname;
|
|
1792
|
+
} catch (err) {
|
|
1793
|
+
console.log("Invalid Dependency-Track server URL", err);
|
|
1794
|
+
process.exit(1);
|
|
1795
|
+
}
|
|
1796
|
+
if (!isAllowedHttpHost(serverHostname)) {
|
|
1797
|
+
recordActivity({
|
|
1798
|
+
kind: "submit",
|
|
1799
|
+
reason: "The URL host is not allowed as per the allowlist.",
|
|
1800
|
+
status: "blocked",
|
|
1801
|
+
target: options.serverUrl,
|
|
1802
|
+
});
|
|
1803
|
+
console.log(
|
|
1804
|
+
`Dependency-Track server host '${serverHostname}' is not allowed by CDXGEN_ALLOWED_HOSTS.`,
|
|
1805
|
+
);
|
|
1806
|
+
process.exit(1);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1687
1809
|
if (isDryRun) {
|
|
1688
1810
|
recordActivity({
|
|
1689
1811
|
kind: "submit",
|
package/bin/convert.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
retrieveCdxgenVersion,
|
|
17
17
|
safeExistsSync,
|
|
18
18
|
safeMkdirSync,
|
|
19
|
+
safeWriteSync,
|
|
19
20
|
} from "../lib/helpers/utils.js";
|
|
20
21
|
import { convertCycloneDxToSpdx } from "../lib/stages/postgen/spdxConverter.js";
|
|
21
22
|
import { validateSpdx } from "../lib/validator/bomValidator.js";
|
|
@@ -26,7 +27,7 @@ const args = _yargs
|
|
|
26
27
|
.option("input", {
|
|
27
28
|
alias: "i",
|
|
28
29
|
default: "bom.json",
|
|
29
|
-
description: "Input CycloneDX BOM JSON file.",
|
|
30
|
+
description: "Input CycloneDX BOM JSON or protobuf file.",
|
|
30
31
|
})
|
|
31
32
|
.option("output", {
|
|
32
33
|
alias: "o",
|
|
@@ -50,18 +51,32 @@ const args = _yargs
|
|
|
50
51
|
.help()
|
|
51
52
|
.wrap(Math.min(120, yargs().terminalWidth())).argv;
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
const loadCycloneDxBom = async (inputPath) => {
|
|
55
|
+
if (!safeExistsSync(inputPath)) {
|
|
56
|
+
console.error(`Input file '${inputPath}' not found.`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const normalizedInputPath = `${inputPath}`.toLowerCase();
|
|
60
|
+
const isProtoInput =
|
|
61
|
+
normalizedInputPath.endsWith(".cdx") ||
|
|
62
|
+
normalizedInputPath.endsWith(".cdx.bin") ||
|
|
63
|
+
normalizedInputPath.endsWith(".proto");
|
|
64
|
+
try {
|
|
65
|
+
if (isProtoInput) {
|
|
66
|
+
const { readBinary } = await import("../lib/helpers/protobom.js");
|
|
67
|
+
return readBinary(inputPath, true);
|
|
68
|
+
}
|
|
69
|
+
return JSON.parse(fs.readFileSync(inputPath, "utf8"));
|
|
70
|
+
} catch (error) {
|
|
71
|
+
const inputType = isProtoInput ? "protobuf" : "JSON";
|
|
72
|
+
console.error(
|
|
73
|
+
`Failed to parse '${inputPath}' as CycloneDX ${inputType}: ${error.message}`,
|
|
74
|
+
);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
57
78
|
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
bomJson = JSON.parse(fs.readFileSync(args.input, "utf8"));
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error(`Failed to parse '${args.input}' as JSON: ${error.message}`);
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
79
|
+
const bomJson = await loadCycloneDxBom(args.input);
|
|
65
80
|
|
|
66
81
|
if (!isCycloneDxBom(bomJson)) {
|
|
67
82
|
console.error(getNonCycloneDxErrorMessage(bomJson, "cdx-convert"));
|
|
@@ -92,7 +107,7 @@ if (outputParent && outputParent !== "." && !safeExistsSync(outputParent)) {
|
|
|
92
107
|
safeMkdirSync(outputParent, { recursive: true });
|
|
93
108
|
}
|
|
94
109
|
|
|
95
|
-
|
|
110
|
+
safeWriteSync(
|
|
96
111
|
outputPath,
|
|
97
112
|
JSON.stringify(spdxJson, null, args.jsonPretty ? 2 : null),
|
|
98
113
|
);
|