@cdxgen/cdxgen 13.4.0 → 13.5.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 +7 -7
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +25 -5
- package/bin/evinse.js +5 -0
- package/bin/hbom.js +15 -7
- package/bin/repl.js +29 -0
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +283 -1
- package/data/rules/cbom-security.yaml +235 -4
- package/data/rules/go-binaries.yaml +46 -0
- package/data/rules/golem-go.yaml +80 -0
- package/data/rules/hbom-compliance.yaml +40 -22
- package/data/rules/hbom-performance.yaml +7 -1
- package/data/rules/host-topology.yaml +1 -1
- package/data/rules/obom-runtime.yaml +31 -2
- package/data/rules/php-runtime.yaml +143 -0
- package/lib/audit/index.js +14 -0
- package/lib/cli/bomAssembly.js +28 -3
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +73 -4
- package/lib/cli/jvmBom.js +126 -20
- package/lib/cli/managedBom.js +711 -71
- package/lib/cli/nativeBom.js +225 -28
- package/lib/core/env.js +30 -8
- package/lib/core/fs.js +28 -19
- package/lib/core/httpClient.js +74 -45
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- package/lib/ecosystems/golem.js +550 -6
- package/lib/ecosystems/gradleutils.js +30 -20
- package/lib/ecosystems/parsers-dotnet.js +117 -18
- package/lib/ecosystems/parsers-go.js +1090 -367
- package/lib/ecosystems/parsers-jvm.js +259 -17
- package/lib/ecosystems/parsers-misc.js +229 -44
- package/lib/ecosystems/parsers-python.js +11 -10
- package/lib/ecosystems/rubyutils.js +6 -3
- package/lib/ecosystems/rusi.js +10 -0
- package/lib/ecosystems/scalacliutils.js +2 -1
- package/lib/ecosystems/scalasem.js +3 -1
- package/lib/evinser/evinser.js +126 -18
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +33 -0
- package/lib/inventory/auditCategories.js +3 -3
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +6 -4
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +500 -58
- package/lib/inventory/dynamic.js +45 -3
- package/lib/inventory/envcontext.js +168 -5
- package/lib/inventory/evidenceUtils.js +436 -23
- package/lib/inventory/formulationParsers.js +3 -2
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +89 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/phpBuildInfo.js +487 -0
- package/lib/inventory/phpHostProbe.js +641 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/source.js +207 -3
- package/lib/inventory/spdx.js +144 -0
- package/lib/inventory/traceRunner.js +5 -1
- package/lib/inventory/tracedCrypto.js +129 -0
- package/lib/managers/binary.js +529 -183
- package/lib/managers/docker.js +166 -12
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +34 -2
- package/lib/server/server.js +186 -1
- package/lib/stages/postgen/annotator.js +17 -1
- package/lib/stages/postgen/introspection/reflect.js +4 -1
- package/lib/stages/postgen/postgen.js +5 -5
- package/lib/stages/postgen/ruleEngine.js +58 -1
- package/lib/stages/postgen/specVersionCompat.js +30 -0
- package/lib/stages/pregen/pregen.js +117 -5
- package/lib/validator/bomValidator.js +26 -5
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +26 -7
- package/lib/validator/generated/validate-1.7.mjs +31 -12
- package/lib/validator/generated/validate-2.0.mjs +30 -11
- package/package.json +17 -17
- package/types/bin/cdxgen-secure.d.ts +3 -0
- package/types/bin/cdxgen-secure.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +9 -2
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +43 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +20 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +18 -2
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/projectTooling.d.ts +41 -0
- package/types/lib/core/projectTooling.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +26 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
- package/types/lib/ecosystems/golem.d.ts +81 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +10 -8
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +85 -13
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +18 -58
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +4 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +25 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +10 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
- package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
- package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
- package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +14 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/certificates.d.ts +205 -0
- package/types/lib/inventory/certificates.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts +2 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts +18 -0
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +145 -6
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +6 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts +40 -2
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +29 -10
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/goBuildInfo.d.ts +10 -0
- package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/inventory/hostTopology.d.ts.map +1 -1
- package/types/lib/inventory/osPackagePurl.d.ts +99 -0
- package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +6 -2
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/osinfo.d.ts +31 -6
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/osqueryTransform.d.ts +51 -0
- package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/phpHostProbe.d.ts +35 -0
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rootfsCerts.d.ts +27 -0
- package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +28 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +15 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +4 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +22 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +10 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/email.d.ts +19 -0
- package/types/lib/parsers/email.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +13 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +8 -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/introspection/reflect.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +14 -1
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/cli/cliOptions.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
METADATA_PROPERTY_CONFIG_KEYS,
|
|
20
20
|
parseMetadataProperties,
|
|
21
21
|
} from "../core/metadataProperties.js";
|
|
22
|
+
import { toCamel } from "../core/paths.js";
|
|
22
23
|
import {
|
|
23
24
|
DEFAULT_CDX_SPEC_VERSION,
|
|
24
25
|
normalizeCycloneDxComponentTypeFilter,
|
|
@@ -124,6 +125,106 @@ export function isUserProvided(value, yaDefault) {
|
|
|
124
125
|
return value !== undefined && value !== yaDefault;
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
/**
|
|
129
|
+
* The value the operator gave an option on the command line or through its
|
|
130
|
+
* CDXGEN_* environment variable. yargs also fills options in from defaults
|
|
131
|
+
* and from a config file, which cannot be told apart in its output.
|
|
132
|
+
*
|
|
133
|
+
* @param {string} option kebab-case option name
|
|
134
|
+
* @param {object} [context]
|
|
135
|
+
* @param {string[]} [context.argv] Raw command-line arguments
|
|
136
|
+
* @param {object} [context.env] Environment variables
|
|
137
|
+
* @param {boolean} [context.boolean] Read the option as a boolean flag
|
|
138
|
+
* @returns {string|boolean|undefined} The operator's value, or undefined
|
|
139
|
+
*/
|
|
140
|
+
export function operatorOptionChoice(
|
|
141
|
+
option,
|
|
142
|
+
{ argv = process.argv.slice(2), env = process.env, boolean = false } = {},
|
|
143
|
+
) {
|
|
144
|
+
const spellings = [...new Set([option, toCamel(option)])];
|
|
145
|
+
const toBoolean = (value) =>
|
|
146
|
+
["true", "1"].includes(`${value}`.trim().toLowerCase());
|
|
147
|
+
let choice;
|
|
148
|
+
for (let i = 0; i < argv.length; i++) {
|
|
149
|
+
const arg = `${argv[i]}`;
|
|
150
|
+
if (arg === "--") {
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
for (const name of spellings) {
|
|
154
|
+
if (boolean && arg === `--no-${name}`) {
|
|
155
|
+
choice = false;
|
|
156
|
+
} else if (arg === `--${name}`) {
|
|
157
|
+
const next = argv[i + 1] === undefined ? "" : `${argv[i + 1]}`;
|
|
158
|
+
if (!boolean) {
|
|
159
|
+
choice = next;
|
|
160
|
+
i++;
|
|
161
|
+
} else if (["true", "false"].includes(next.toLowerCase())) {
|
|
162
|
+
choice = toBoolean(next);
|
|
163
|
+
i++;
|
|
164
|
+
} else {
|
|
165
|
+
choice = true;
|
|
166
|
+
}
|
|
167
|
+
} else if (arg.startsWith(`--${name}=`)) {
|
|
168
|
+
const value = arg.slice(name.length + 3);
|
|
169
|
+
choice = boolean ? toBoolean(value) : value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (choice !== undefined) {
|
|
174
|
+
return choice;
|
|
175
|
+
}
|
|
176
|
+
const envValue =
|
|
177
|
+
env[`CDXGEN_${option.toUpperCase().replaceAll("-", "_")}`] ?? "";
|
|
178
|
+
if (`${envValue}`.trim() === "") {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
return boolean ? toBoolean(envValue) : `${envValue}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Make the operator's choice about running the scanned project's own code
|
|
186
|
+
* final. A config file is read from the working directory, which is usually
|
|
187
|
+
* the scanned project, so it carries that project's trust. Neither the config
|
|
188
|
+
* file nor a profile or lifecycle may turn installs on when the operator
|
|
189
|
+
* disabled them, and secure mode keeps installs off unless the operator
|
|
190
|
+
* passes --install-deps. Introspection keeps the project's build tools
|
|
191
|
+
* running with installs off, which is likewise the operator's decision, so
|
|
192
|
+
* introspection asked for only by the config file does not do that.
|
|
193
|
+
*
|
|
194
|
+
* **Mutates `options` in place.**
|
|
195
|
+
*
|
|
196
|
+
* @param {object} options The options object, after applyAdvancedOptions
|
|
197
|
+
* @param {object} [context]
|
|
198
|
+
* @param {object} [context.projectConfig] Sanitized config file contents
|
|
199
|
+
* @param {boolean} [context.isSecureMode] Whether cdxgen runs in secure mode
|
|
200
|
+
* @param {string[]} [context.argv] Raw command-line arguments
|
|
201
|
+
* @param {object} [context.env] Environment variables
|
|
202
|
+
* @returns {void}
|
|
203
|
+
*/
|
|
204
|
+
export function applyOperatorToolingChoice(options, context = {}) {
|
|
205
|
+
const { projectConfig, argv, env } = context;
|
|
206
|
+
const secureMode = context.isSecureMode ?? isSecureMode;
|
|
207
|
+
const installChoice = operatorOptionChoice("install-deps", {
|
|
208
|
+
argv,
|
|
209
|
+
env,
|
|
210
|
+
boolean: true,
|
|
211
|
+
});
|
|
212
|
+
if (installChoice === false || (secureMode && installChoice !== true)) {
|
|
213
|
+
options.installDeps = false;
|
|
214
|
+
}
|
|
215
|
+
const configAsksIntrospect =
|
|
216
|
+
["true", "1"].includes(`${projectConfig?.introspect}`.toLowerCase()) ||
|
|
217
|
+
projectConfig?.profile === "introspect";
|
|
218
|
+
if (
|
|
219
|
+
options.introspect === true &&
|
|
220
|
+
configAsksIntrospect &&
|
|
221
|
+
operatorOptionChoice("introspect", { argv, env, boolean: true }) !== true &&
|
|
222
|
+
operatorOptionChoice("profile", { argv, env }) !== "introspect"
|
|
223
|
+
) {
|
|
224
|
+
options.introspectFromConfigFile = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
127
228
|
/**
|
|
128
229
|
* Combine the custom metadata properties of every source (discussion 4391):
|
|
129
230
|
* the `--metadata-property` flag, the CDXGEN_METADATA_PROPERTY and
|
|
@@ -588,7 +689,9 @@ export function applyAdvancedOptions(options, context = {}) {
|
|
|
588
689
|
options.deep = true;
|
|
589
690
|
options.evidence = false;
|
|
590
691
|
options.includeCrypto = false;
|
|
591
|
-
|
|
692
|
+
// A profile may skip installs but never turn them back on once they
|
|
693
|
+
// were disabled, which would run the scanned project's code.
|
|
694
|
+
options.installDeps = !secureMode && options.installDeps !== false;
|
|
592
695
|
break;
|
|
593
696
|
case "deep-learning":
|
|
594
697
|
case "ml-deep":
|
|
@@ -596,7 +699,7 @@ export function applyAdvancedOptions(options, context = {}) {
|
|
|
596
699
|
options.deep = true;
|
|
597
700
|
options.evidence = true;
|
|
598
701
|
options.includeCrypto = true;
|
|
599
|
-
options.installDeps = !secureMode;
|
|
702
|
+
options.installDeps = !secureMode && options.installDeps !== false;
|
|
600
703
|
options.bomAudit = true;
|
|
601
704
|
break;
|
|
602
705
|
default:
|
|
@@ -619,7 +722,11 @@ export function applyAdvancedOptions(options, context = {}) {
|
|
|
619
722
|
"PREVIEW: post-build lifecycle SBOM generation is supported only for limited project types.",
|
|
620
723
|
});
|
|
621
724
|
}
|
|
622
|
-
|
|
725
|
+
// The project is already built, so installs are on only where they
|
|
726
|
+
// would have been anyway: a disabled install stays disabled.
|
|
727
|
+
if (options.installDeps !== false) {
|
|
728
|
+
options.installDeps = true;
|
|
729
|
+
}
|
|
623
730
|
break;
|
|
624
731
|
default:
|
|
625
732
|
break;
|
package/lib/cli/index.js
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
getDependencyTrackBomApiUrl,
|
|
52
52
|
} from "../helpers/remote/dependency-track.js";
|
|
53
53
|
import { optionIncludesAiInventoryProjectType } from "../inventory/aiInventory.js";
|
|
54
|
+
import { readApkInstalledDb } from "../inventory/apkInstalledDb.js";
|
|
54
55
|
import { isCycloneDxComponentTypeEnabled } from "../inventory/bomUtils.js";
|
|
55
56
|
import {
|
|
56
57
|
mergeDependencies,
|
|
@@ -58,7 +59,10 @@ import {
|
|
|
58
59
|
trimComponents,
|
|
59
60
|
} from "../inventory/depsUtils.js";
|
|
60
61
|
import { buildDynamicComponents } from "../inventory/dynamic.js";
|
|
61
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
convertApkPackages,
|
|
64
|
+
convertOSQueryResults,
|
|
65
|
+
} from "../inventory/evidenceUtils.js";
|
|
62
66
|
import {
|
|
63
67
|
createHbomDocument,
|
|
64
68
|
ensureHbomRuntimeSupport,
|
|
@@ -67,6 +71,7 @@ import {
|
|
|
67
71
|
} from "../inventory/hbom.js";
|
|
68
72
|
import { mergeHostInventoryBoms } from "../inventory/hostTopology.js";
|
|
69
73
|
import { orderParentCandidates } from "../inventory/parentSelection.js";
|
|
74
|
+
import { probeHostPhp } from "../inventory/phpHostProbe.js";
|
|
70
75
|
// Imported directly rather than through the utils.js barrel: purl validity is
|
|
71
76
|
// checked in a hot path and the barrel is already the root of several import
|
|
72
77
|
// cycles.
|
|
@@ -86,6 +91,7 @@ import {
|
|
|
86
91
|
exportArchive,
|
|
87
92
|
exportImage,
|
|
88
93
|
getPkgPathList,
|
|
94
|
+
isFollowableManifestImage,
|
|
89
95
|
isImageReference,
|
|
90
96
|
parseImageName,
|
|
91
97
|
} from "../managers/docker.js";
|
|
@@ -272,6 +278,37 @@ export function createOSBom(_path, options) {
|
|
|
272
278
|
);
|
|
273
279
|
}
|
|
274
280
|
} // for
|
|
281
|
+
// osquery has no apk table, so the packages of an Alpine, Wolfi or Chainguard
|
|
282
|
+
// host come from the apk installed database, after the osquery categories and
|
|
283
|
+
// as a category of their own. They are all components: the OS itself is the
|
|
284
|
+
// first row of the first osquery category, as before.
|
|
285
|
+
if (_platform() === "linux") {
|
|
286
|
+
const apkInventory = readApkInstalledDb();
|
|
287
|
+
if (apkInventory.state === "unreadable") {
|
|
288
|
+
recordDegradation("os.apk-installed-db.unreadable", {
|
|
289
|
+
ecosystem: "generic",
|
|
290
|
+
tool: "apk",
|
|
291
|
+
impact: "components",
|
|
292
|
+
path: apkInventory.path,
|
|
293
|
+
detail:
|
|
294
|
+
"The apk installed database exists but could not be read in full, so the packages it lists are missing from the OBOM.",
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
if (apkInventory.packages.length) {
|
|
298
|
+
pkgList = pkgList.concat(
|
|
299
|
+
convertApkPackages(apkInventory.packages).sort((a, b) =>
|
|
300
|
+
a.name.localeCompare(b.name),
|
|
301
|
+
),
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// The package tables cannot see a PHP that no package manager installed. Its
|
|
306
|
+
// edges stay out: an OBOM has no dependency graph, and a partial one makes
|
|
307
|
+
// post-processing infer the OS root's dependencies from these files alone.
|
|
308
|
+
const hostPhp = probeHostPhp({ executeOsQuery, packageComponents: pkgList });
|
|
309
|
+
if (hostPhp.components.length) {
|
|
310
|
+
pkgList = pkgList.concat(hostPhp.components);
|
|
311
|
+
}
|
|
275
312
|
const hostTrustInventory = enrichOSComponentsWithTrustData(pkgList);
|
|
276
313
|
if (hostTrustInventory?.components?.length) {
|
|
277
314
|
pkgList = hostTrustInventory.components;
|
|
@@ -515,11 +552,14 @@ export async function createContainerSpecLikeBom(path, options) {
|
|
|
515
552
|
if (imageBomData?.bomJson?.components) {
|
|
516
553
|
components = components.concat(imageBomData.bomJson.components);
|
|
517
554
|
}
|
|
555
|
+
doneimages.push(img.image);
|
|
556
|
+
if (!isFollowableManifestImage(img.image, imageObj)) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
518
559
|
const bomData = await createBom(img.image, {
|
|
519
560
|
specVersion: options.specVersion,
|
|
520
561
|
projectType: ["oci"],
|
|
521
562
|
});
|
|
522
|
-
doneimages.push(img.image);
|
|
523
563
|
if (bomData) {
|
|
524
564
|
if (bomData.components?.length) {
|
|
525
565
|
// Inject properties
|
|
@@ -734,6 +774,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
734
774
|
let dependencies = [];
|
|
735
775
|
let bomData;
|
|
736
776
|
let formulationList = [];
|
|
777
|
+
let metadataProperties = [];
|
|
737
778
|
let parentComponent = determineParentComponent(options) || {};
|
|
738
779
|
let parentSubComponents = [];
|
|
739
780
|
const setProjectTypeActivityContext = (projectType, sourcePath) => {
|
|
@@ -867,6 +908,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
867
908
|
dependencies: [],
|
|
868
909
|
options: { ...sharedOptions },
|
|
869
910
|
parentSubComponents: [],
|
|
911
|
+
metadataProperties: [],
|
|
870
912
|
};
|
|
871
913
|
}
|
|
872
914
|
};
|
|
@@ -879,6 +921,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
879
921
|
let components = [];
|
|
880
922
|
let dependencies = [];
|
|
881
923
|
let parentSubComponents = [];
|
|
924
|
+
let metadataProperties = [];
|
|
882
925
|
// Each builder detects the project of its own ecosystem. A builder that sets
|
|
883
926
|
// options.parentComponent to force its project into its own BOM must not
|
|
884
927
|
// make that project the next builder's too, or the next ecosystem's project
|
|
@@ -1163,6 +1206,12 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1163
1206
|
}
|
|
1164
1207
|
setProjectTypeActivityContext("php", path);
|
|
1165
1208
|
bomData = await withOwnParent(() => createPHPBom(path, options));
|
|
1209
|
+
// A lock with no packages can still hold the project's platform data.
|
|
1210
|
+
if (bomData?.metadataProperties?.length) {
|
|
1211
|
+
metadataProperties = metadataProperties.concat(
|
|
1212
|
+
bomData.metadataProperties,
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1166
1215
|
if (bomData?.bomJson?.components?.length) {
|
|
1167
1216
|
thoughtLog(
|
|
1168
1217
|
`I found ${bomData.bomJson.components.length} php packages.`,
|
|
@@ -1236,7 +1285,12 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1236
1285
|
}
|
|
1237
1286
|
setProjectTypeActivityContext("csharp", path);
|
|
1238
1287
|
bomData = await withOwnParent(() => createCsharpBom(path, options));
|
|
1239
|
-
|
|
1288
|
+
// Every project the scan read has a dependency entry of its own, so a
|
|
1289
|
+
// project that uses no packages is kept along with its parent.
|
|
1290
|
+
if (
|
|
1291
|
+
bomData?.bomJson?.components?.length ||
|
|
1292
|
+
bomData?.bomJson?.dependencies?.length
|
|
1293
|
+
) {
|
|
1240
1294
|
thoughtLog(
|
|
1241
1295
|
`There are ${bomData.bomJson.components.length} csharp packages.`,
|
|
1242
1296
|
);
|
|
@@ -1753,9 +1807,18 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1753
1807
|
);
|
|
1754
1808
|
}
|
|
1755
1809
|
components = components.concat(bomData.bomJson.components);
|
|
1810
|
+
if (bomData.dependencies?.length) {
|
|
1811
|
+
dependencies = mergeDependencies(dependencies, bomData.dependencies);
|
|
1812
|
+
}
|
|
1756
1813
|
}
|
|
1757
1814
|
}
|
|
1758
|
-
return {
|
|
1815
|
+
return {
|
|
1816
|
+
components,
|
|
1817
|
+
dependencies,
|
|
1818
|
+
options,
|
|
1819
|
+
parentSubComponents,
|
|
1820
|
+
metadataProperties,
|
|
1821
|
+
};
|
|
1759
1822
|
};
|
|
1760
1823
|
const pathResults = [];
|
|
1761
1824
|
// A container scan walks hundreds of paths, so the phase carries a bar and
|
|
@@ -1818,6 +1881,9 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1818
1881
|
parentSubComponents = parentSubComponents.concat(
|
|
1819
1882
|
pathResult.parentSubComponents,
|
|
1820
1883
|
);
|
|
1884
|
+
metadataProperties = metadataProperties.concat(
|
|
1885
|
+
pathResult.metadataProperties,
|
|
1886
|
+
);
|
|
1821
1887
|
if (pathResult.options.services?.length) {
|
|
1822
1888
|
sharedOptions.services = mergeServices(
|
|
1823
1889
|
sharedOptions.services || [],
|
|
@@ -1907,6 +1973,7 @@ export async function createMultiXBom(pathList, options) {
|
|
|
1907
1973
|
components,
|
|
1908
1974
|
parentComponent,
|
|
1909
1975
|
dependencies,
|
|
1976
|
+
metadataProperties,
|
|
1910
1977
|
);
|
|
1911
1978
|
if (formulationList.length) {
|
|
1912
1979
|
multiResult.formulationList = formulationList;
|
|
@@ -1978,6 +2045,7 @@ export async function createDynamicBom(path, options) {
|
|
|
1978
2045
|
const {
|
|
1979
2046
|
components: pkgList,
|
|
1980
2047
|
services: traceServices,
|
|
2048
|
+
dependencies: traceDependencies,
|
|
1981
2049
|
dryRunSummary,
|
|
1982
2050
|
} = await buildDynamicComponents(commandStr, workingDir, traceOptions);
|
|
1983
2051
|
|
|
@@ -1986,6 +2054,7 @@ export async function createDynamicBom(path, options) {
|
|
|
1986
2054
|
projectType: "dynamic",
|
|
1987
2055
|
filename: commandStr,
|
|
1988
2056
|
services: traceServices,
|
|
2057
|
+
dependencies: traceDependencies || [],
|
|
1989
2058
|
});
|
|
1990
2059
|
// A dry run denies every side effect, so the attempted-operation counts are
|
|
1991
2060
|
// the result. Only counts are recorded, never the paths or targets.
|
package/lib/cli/jvmBom.js
CHANGED
|
@@ -7,11 +7,7 @@ import process from "node:process";
|
|
|
7
7
|
import { build } from "@cdxgen/cdx-purl";
|
|
8
8
|
import { gte, lte } from "semver";
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
DEBUG_MODE,
|
|
12
|
-
isSecureMode,
|
|
13
|
-
readEnvironmentVariable,
|
|
14
|
-
} from "../core/activity.js";
|
|
10
|
+
import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
|
|
15
11
|
import { commandOutputText, recordDegradation } from "../core/buildLedger.js";
|
|
16
12
|
import {
|
|
17
13
|
deferFailOnError,
|
|
@@ -39,6 +35,10 @@ import {
|
|
|
39
35
|
} from "../core/fs.js";
|
|
40
36
|
import { thoughtLog } from "../core/logger.js";
|
|
41
37
|
import { dirNameStr } from "../core/paths.js";
|
|
38
|
+
import {
|
|
39
|
+
isSecureModeActive,
|
|
40
|
+
reportProjectToolingSkipped,
|
|
41
|
+
} from "../core/projectTooling.js";
|
|
42
42
|
import { extractJarArchive, getMvnMetadata } from "../ecosystems/ecosystems.js";
|
|
43
43
|
import {
|
|
44
44
|
buildGradleCommandArguments,
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
getMillCommand,
|
|
52
52
|
getMillVersion,
|
|
53
53
|
hasGradleInvocationFailure,
|
|
54
|
+
isProjectToolingAllowed,
|
|
54
55
|
parseGradleDep,
|
|
55
56
|
parseGradleInfoLogsForUrls,
|
|
56
57
|
parseGradleProperties,
|
|
@@ -72,6 +73,7 @@ import {
|
|
|
72
73
|
parseMavenTreeJson,
|
|
73
74
|
parseMillDependency,
|
|
74
75
|
parsePom,
|
|
76
|
+
parsePomWithLocalParents,
|
|
75
77
|
readMavenModuleTrees,
|
|
76
78
|
} from "../ecosystems/parsers-jvm.js";
|
|
77
79
|
import {
|
|
@@ -495,6 +497,63 @@ export function discoverSbtProjectsFromCmd(
|
|
|
495
497
|
return discoverSbtProjects(basePath);
|
|
496
498
|
}
|
|
497
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Read one pom.xml without running Maven: the module it declares and its
|
|
502
|
+
* direct dependencies. Versions resolve from the pom's properties and
|
|
503
|
+
* dependencyManagement and from parent poms inside the scanned directory.
|
|
504
|
+
* Versions that an imported BOM or a parent outside the directory manages
|
|
505
|
+
* stay empty, and transitive dependencies are unknown.
|
|
506
|
+
*
|
|
507
|
+
* No dependency edges are produced. The build-fidelity rules (BF-JVM-001,
|
|
508
|
+
* BF-GEN-001) recognise a pom read without Maven by its missing graph, and a
|
|
509
|
+
* graph of direct edges alone would pass for a resolved one.
|
|
510
|
+
*
|
|
511
|
+
* @param {string} pomFile pom.xml to read
|
|
512
|
+
* @param {string} rootPath Scanned directory
|
|
513
|
+
* @param {Map} [cache] Parent pom results shared by the poms of one scan
|
|
514
|
+
* @returns {{moduleComponent: Object|undefined, pkgList: Object[]}}
|
|
515
|
+
*/
|
|
516
|
+
export function readMavenModuleStatically(pomFile, rootPath, cache) {
|
|
517
|
+
let pomMap;
|
|
518
|
+
try {
|
|
519
|
+
pomMap = parsePomWithLocalParents(pomFile, rootPath, cache);
|
|
520
|
+
} catch (_err) {
|
|
521
|
+
return { moduleComponent: undefined, pkgList: [] };
|
|
522
|
+
}
|
|
523
|
+
const pkgList = [];
|
|
524
|
+
for (const dep of pomMap?.dependencies || []) {
|
|
525
|
+
const purl = mavenPurl(
|
|
526
|
+
dep.group || undefined,
|
|
527
|
+
dep.name,
|
|
528
|
+
dep.version || undefined,
|
|
529
|
+
dep.qualifiers,
|
|
530
|
+
);
|
|
531
|
+
pkgList.push(
|
|
532
|
+
purl ? { ...dep, purl, "bom-ref": decodeURIComponent(purl) } : dep,
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
const { artifactId, groupId, packaging, version } = pomMap?.properties || {};
|
|
536
|
+
const modulePurl = artifactId
|
|
537
|
+
? mavenPurl(groupId || undefined, artifactId, version || undefined, {
|
|
538
|
+
type: packaging || "jar",
|
|
539
|
+
})
|
|
540
|
+
: undefined;
|
|
541
|
+
if (!modulePurl) {
|
|
542
|
+
return { moduleComponent: undefined, pkgList };
|
|
543
|
+
}
|
|
544
|
+
const moduleComponent = {
|
|
545
|
+
group: groupId || "",
|
|
546
|
+
name: artifactId,
|
|
547
|
+
version: version || "",
|
|
548
|
+
qualifiers: { type: packaging || "jar" },
|
|
549
|
+
type: "application",
|
|
550
|
+
purl: modulePurl,
|
|
551
|
+
"bom-ref": decodeURIComponent(modulePurl),
|
|
552
|
+
properties: [{ name: "internal:SrcFile", value: pomFile }],
|
|
553
|
+
};
|
|
554
|
+
return { moduleComponent, pkgList };
|
|
555
|
+
}
|
|
556
|
+
|
|
498
557
|
/**
|
|
499
558
|
* Function to create bom string for Java projects
|
|
500
559
|
*
|
|
@@ -654,6 +713,55 @@ export async function createJavaBom(path, options) {
|
|
|
654
713
|
}
|
|
655
714
|
const firstPom = pomFiles.length ? pomFiles[0] : undefined;
|
|
656
715
|
let mavenCmd = getMavenCommand(path, path, options);
|
|
716
|
+
// Executing Maven evaluates the pom's build section, loads `.mvn/`
|
|
717
|
+
// configuration such as jvm.config or extensions.xml, and resolves
|
|
718
|
+
// build extensions, so it belongs to the dependency-tooling surface
|
|
719
|
+
// disabled by --no-install-deps and the pre-build lifecycle. The scan
|
|
720
|
+
// then keeps only the direct dependencies each pom declares.
|
|
721
|
+
const mavenToolingAllowed = isProjectToolingAllowed(options);
|
|
722
|
+
if (!mavenToolingAllowed) {
|
|
723
|
+
reportProjectToolingSkipped(
|
|
724
|
+
options,
|
|
725
|
+
"jvm.maven.tooling-disabled",
|
|
726
|
+
"Skipping Maven since dependency installation is disabled. Only the direct dependencies declared in pom.xml files will be used.",
|
|
727
|
+
{
|
|
728
|
+
ecosystem: "java",
|
|
729
|
+
tool: "maven",
|
|
730
|
+
impact: "transitive-deps",
|
|
731
|
+
detail:
|
|
732
|
+
"Dependency installation is disabled, so Maven was not run and only the direct dependencies parsed from pom.xml are available.",
|
|
733
|
+
},
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
// Poms read without Maven: what their local parents define is shared
|
|
737
|
+
// across the scan, and each pom adds its module and direct dependencies.
|
|
738
|
+
const staticPomCache = new Map();
|
|
739
|
+
const addStaticMavenModule = (pomFile) => {
|
|
740
|
+
const staticModule = readMavenModuleStatically(
|
|
741
|
+
pomFile,
|
|
742
|
+
path,
|
|
743
|
+
staticPomCache,
|
|
744
|
+
);
|
|
745
|
+
if (staticModule.pkgList.length) {
|
|
746
|
+
pkgList = pkgList.concat(staticModule.pkgList);
|
|
747
|
+
}
|
|
748
|
+
const moduleComponent = staticModule.moduleComponent;
|
|
749
|
+
if (!moduleComponent) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (!Object.keys(parentComponent).length) {
|
|
753
|
+
parentComponent = moduleComponent;
|
|
754
|
+
parentComponent.components = [];
|
|
755
|
+
} else if (
|
|
756
|
+
parentComponent["bom-ref"] !== moduleComponent["bom-ref"] &&
|
|
757
|
+
!parentComponent.components?.some(
|
|
758
|
+
(c) => c["bom-ref"] === moduleComponent["bom-ref"],
|
|
759
|
+
)
|
|
760
|
+
) {
|
|
761
|
+
parentComponent.components = parentComponent.components || [];
|
|
762
|
+
parentComponent.components.push(moduleComponent);
|
|
763
|
+
}
|
|
764
|
+
};
|
|
657
765
|
// Pom files whose own tree a recursive run of their aggregator already
|
|
658
766
|
// returned. Each is skipped when its turn comes, so a reactor is run
|
|
659
767
|
// once from its aggregator instead of once per member.
|
|
@@ -668,6 +776,12 @@ export async function createJavaBom(path, options) {
|
|
|
668
776
|
}
|
|
669
777
|
continue;
|
|
670
778
|
}
|
|
779
|
+
// Without Maven every pom is read on its own, Quarkus modules too:
|
|
780
|
+
// the plugin run that would have covered them does not happen.
|
|
781
|
+
if (!mavenToolingAllowed) {
|
|
782
|
+
addStaticMavenModule(f);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
671
785
|
if (
|
|
672
786
|
isQuarkus &&
|
|
673
787
|
!options.deep &&
|
|
@@ -688,10 +802,7 @@ export async function createJavaBom(path, options) {
|
|
|
688
802
|
`Parsing ${f} without Maven: the repository rate limited Maven in this scan.`,
|
|
689
803
|
);
|
|
690
804
|
}
|
|
691
|
-
|
|
692
|
-
if (dlist.length) {
|
|
693
|
-
pkgList = pkgList.concat(dlist);
|
|
694
|
-
}
|
|
805
|
+
addStaticMavenModule(f);
|
|
695
806
|
continue;
|
|
696
807
|
}
|
|
697
808
|
if (mavenCmd?.endsWith("mvn")) {
|
|
@@ -701,7 +812,7 @@ export async function createJavaBom(path, options) {
|
|
|
701
812
|
// user's own settings.xml, with its mirrors and credentials, still
|
|
702
813
|
// applies on top of it.
|
|
703
814
|
const projectSettings = projectSettingsArgs(basePath, {
|
|
704
|
-
secureMode:
|
|
815
|
+
secureMode: isSecureModeActive(),
|
|
705
816
|
});
|
|
706
817
|
if (projectSettings.args.length) {
|
|
707
818
|
console.log(
|
|
@@ -1031,11 +1142,7 @@ export async function createJavaBom(path, options) {
|
|
|
1031
1142
|
thoughtLog(
|
|
1032
1143
|
"**MAVEN**: There appear to be build errors, so the SBOM will be incomplete.",
|
|
1033
1144
|
);
|
|
1034
|
-
|
|
1035
|
-
const dlist = pomMap?.dependencies || [];
|
|
1036
|
-
if (dlist.length) {
|
|
1037
|
-
pkgList = pkgList.concat(dlist);
|
|
1038
|
-
}
|
|
1145
|
+
addStaticMavenModule(f);
|
|
1039
1146
|
} else {
|
|
1040
1147
|
// The text trees exist only when the JSON run failed and was
|
|
1041
1148
|
// retried. Each reactor module has its own tree, and a member
|
|
@@ -1312,8 +1419,7 @@ export async function createJavaBom(path, options) {
|
|
|
1312
1419
|
// `gradle properties` evaluates the project's build scripts, so it is
|
|
1313
1420
|
// part of the dependency-tooling surface gated by --no-install-deps
|
|
1314
1421
|
// (and the pre-build lifecycle).
|
|
1315
|
-
const gradleToolingAllowed =
|
|
1316
|
-
options.installDeps !== false || options.introspect === true;
|
|
1422
|
+
const gradleToolingAllowed = isProjectToolingAllowed(options);
|
|
1317
1423
|
if (gradleToolingAllowed) {
|
|
1318
1424
|
parallelPropTaskOut = executeParallelGradleProperties(
|
|
1319
1425
|
gradleRootPath,
|
|
@@ -1941,7 +2047,7 @@ export async function createJavaBom(path, options) {
|
|
|
1941
2047
|
pkgList = pkgList.concat(dlist);
|
|
1942
2048
|
}
|
|
1943
2049
|
}
|
|
1944
|
-
} else if (
|
|
2050
|
+
} else if (!isProjectToolingAllowed(options)) {
|
|
1945
2051
|
// Executing sbt evaluates the project's build definition (arbitrary
|
|
1946
2052
|
// Scala), so it belongs to the dependency-tooling surface disabled
|
|
1947
2053
|
// by --no-install-deps and the pre-build lifecycle.
|
|
@@ -2363,7 +2469,7 @@ export async function createJavaBom(path, options) {
|
|
|
2363
2469
|
|
|
2364
2470
|
if (
|
|
2365
2471
|
millFiles?.length &&
|
|
2366
|
-
(options
|
|
2472
|
+
isProjectToolingAllowed(options) &&
|
|
2367
2473
|
isPackageManagerAllowed(
|
|
2368
2474
|
"mill",
|
|
2369
2475
|
["bazel", "sbt", "gradle", "maven"],
|
|
@@ -2572,7 +2678,7 @@ export async function createJavaBom(path, options) {
|
|
|
2572
2678
|
} finally {
|
|
2573
2679
|
// A build this scan ran can have started a Mill daemon of its own
|
|
2574
2680
|
// without --no-server; stop those even when a later step failed.
|
|
2575
|
-
stopMillDaemons(path, millDaemonsBefore);
|
|
2681
|
+
stopMillDaemons(path, millDaemonsBefore, options);
|
|
2576
2682
|
}
|
|
2577
2683
|
// A Scala.js build bundles npm packages: scalajs-bundler installs them
|
|
2578
2684
|
// under target, and a bundler workspace keeps its lock file beside the
|