@cdxgen/cdxgen 13.4.0 → 13.6.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 +9 -9
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +29 -5
- package/bin/evinse.js +8 -0
- package/bin/hbom.js +15 -7
- package/bin/repl.js +29 -0
- package/bin/validate.js +6 -0
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/license-aliases.json +24 -2
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +407 -1
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -0
- 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/data/rules/vscode-extensions.yaml +75 -0
- package/data/spdx-license-list.json +121 -7
- package/data/spdx-licenses.json +69 -0
- package/data/spdx.schema.json +845 -2
- package/lib/audit/index.js +14 -0
- package/lib/cli/bomAssembly.js +120 -6
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +182 -6
- package/lib/cli/jsBom.js +153 -64
- package/lib/cli/jvmBom.js +363 -66
- package/lib/cli/managedBom.js +853 -87
- package/lib/cli/nativeBom.js +267 -33
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +38 -8
- package/lib/core/fs.js +40 -19
- package/lib/core/httpClient.js +172 -46
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +5 -0
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- package/lib/core/toolRegistry.js +170 -0
- package/lib/ecosystems/cppEvidence.js +263 -106
- package/lib/ecosystems/cppIncludeResolver.js +16 -5
- package/lib/ecosystems/ecosystems.js +25 -10
- package/lib/ecosystems/gemHostProbe.js +301 -0
- package/lib/ecosystems/golem.js +550 -6
- package/lib/ecosystems/gradleutils.js +858 -244
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-dotnet.js +117 -18
- package/lib/ecosystems/parsers-go.js +1090 -367
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-jvm.js +259 -17
- package/lib/ecosystems/parsers-misc.js +438 -64
- package/lib/ecosystems/parsers-python.js +100 -12
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/rubyutils.js +544 -34
- package/lib/ecosystems/rusi.js +10 -0
- package/lib/ecosystems/scalacliutils.js +4 -1
- package/lib/ecosystems/scalasem.js +798 -151
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +472 -61
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/helpers/agenticToolutils.js +413 -93
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +110 -0
- package/lib/inventory/auditCategories.js +3 -3
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +41 -4
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +639 -63
- package/lib/inventory/dotnetRuntime.js +217 -0
- package/lib/inventory/dynamic.js +45 -3
- package/lib/inventory/envcontext.js +475 -18
- package/lib/inventory/evidenceUtils.js +447 -23
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +32 -3
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/jvmLocalRepos.js +9 -2
- package/lib/inventory/jvmToolEnv.js +29 -0
- package/lib/inventory/licenseEnhancer.js +12 -0
- package/lib/inventory/mavenCentralMirror.js +534 -0
- package/lib/inventory/nodeBuildInfo.js +218 -0
- package/lib/inventory/nodeHostProbe.js +202 -0
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +271 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/phpBuildInfo.js +438 -0
- package/lib/inventory/phpHostProbe.js +185 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/rubyBuildInfo.js +972 -0
- package/lib/inventory/rubyHostProbe.js +218 -0
- package/lib/inventory/scalaCoords.js +4 -3
- package/lib/inventory/scalaDedupe.js +440 -0
- package/lib/inventory/source.js +256 -5
- 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 +866 -186
- package/lib/managers/docker.js +245 -12
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +62 -13
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +198 -2
- 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 +71 -6
- package/lib/stages/postgen/specVersionCompat.js +30 -0
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +231 -62
- package/lib/validator/bomValidator.js +32 -6
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +27 -8
- package/lib/validator/generated/validate-1.7.mjs +32 -13
- package/lib/validator/generated/validate-2.0.mjs +31 -12
- package/lib/validator/index.js +1 -1
- package/package.json +19 -18
- 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 +22 -3
- 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/jsBom.d.ts +1 -17
- package/types/lib/cli/jsBom.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/boundedFile.d.ts +70 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -0
- package/types/lib/core/brotliProbe.d.ts +37 -0
- package/types/lib/core/brotliProbe.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +22 -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 +24 -0
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/lockPaths.d.ts +19 -0
- package/types/lib/core/lockPaths.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +2 -0
- package/types/lib/core/processTree.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/core/toolRegistry.d.ts +36 -0
- package/types/lib/core/toolRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
- package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
- 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 +57 -21
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-bazel.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-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.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 +37 -58
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +14 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +19 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +144 -12
- 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/ecosystems/vendoredCode.d.ts +26 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
- package/types/lib/ecosystems/venvProbe.d.ts +40 -0
- package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +88 -8
- 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 +33 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/brotliDecoder.d.ts +58 -0
- package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
- package/types/lib/inventory/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.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 +162 -7
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
- 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 +92 -8
- 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/gtfobins.d.ts.map +1 -1
- 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/hostRuntimeProbe.d.ts +44 -0
- package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
- package/types/lib/inventory/hostTopology.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
- package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
- package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
- package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
- package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
- 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 +44 -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/rubyBuildInfo.d.ts +271 -0
- package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
- package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +38 -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 +24 -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 +17 -3
- 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/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +26 -9
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
|
@@ -165,7 +165,18 @@ function hasProperty(obj, propName, expectedValue) {
|
|
|
165
165
|
if (expectedValue === undefined) {
|
|
166
166
|
return value !== null;
|
|
167
167
|
}
|
|
168
|
-
|
|
168
|
+
// A repeatable property (cdx:crypto:sourceType on a certificate found by
|
|
169
|
+
// several producers) matches when any of its values does.
|
|
170
|
+
return extractPropertyValues(obj, propName).includes(String(expectedValue));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function extractPropertyValues(obj, propName) {
|
|
174
|
+
if (!Array.isArray(obj?.properties)) {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
return obj.properties
|
|
178
|
+
.filter((p) => p?.name === propName && p.value !== undefined)
|
|
179
|
+
.map((p) => String(p.value));
|
|
169
180
|
}
|
|
170
181
|
|
|
171
182
|
/**
|
|
@@ -315,6 +326,9 @@ function registerCdxHelpers(expression, helpers = {}) {
|
|
|
315
326
|
expression.registerFunction("prop", (obj, propName) =>
|
|
316
327
|
extractProperty(obj, propName),
|
|
317
328
|
);
|
|
329
|
+
expression.registerFunction("propValues", (obj, propName) =>
|
|
330
|
+
extractPropertyValues(obj, propName),
|
|
331
|
+
);
|
|
318
332
|
expression.registerFunction("nullSafeProp", (obj, propName) => {
|
|
319
333
|
const value = extractProperty(obj, propName);
|
|
320
334
|
return value === null ? "" : value;
|
|
@@ -385,6 +399,13 @@ function registerCdxHelpers(expression, helpers = {}) {
|
|
|
385
399
|
expression.registerFunction("safeStr", (val) => {
|
|
386
400
|
return val === null || val === undefined ? "" : String(val).trim();
|
|
387
401
|
});
|
|
402
|
+
// End of validity in epoch milliseconds. A date-only value (2026-01-31)
|
|
403
|
+
// is valid through the end of that day in UTC; a full RFC 3339 timestamp
|
|
404
|
+
// is taken as is; anything else, or an impossible date, gives no value so
|
|
405
|
+
// the rule skips the component.
|
|
406
|
+
expression.registerFunction("certEndMillis", (val) =>
|
|
407
|
+
parseCertificateEndMillis(val),
|
|
408
|
+
);
|
|
388
409
|
expression.registerFunction("parseSizeBytes", (val) => {
|
|
389
410
|
if (val === null || val === undefined || val === "") {
|
|
390
411
|
return null;
|
|
@@ -519,6 +540,42 @@ function registerCdxHelpers(expression, helpers = {}) {
|
|
|
519
540
|
* @param {string} rulesDir - Path to directory containing .yaml rule files
|
|
520
541
|
* @returns {Promise<Array>} Array of parsed rule objects
|
|
521
542
|
*/
|
|
543
|
+
const FULL_TIMESTAMP_RE =
|
|
544
|
+
/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]([01]\d|2[0-3]):[0-5]\d)$/;
|
|
545
|
+
const DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Parse a certificate notValidAfter value.
|
|
549
|
+
*
|
|
550
|
+
* @param {*} value timestamp or date-only string
|
|
551
|
+
* @returns {number|undefined} epoch milliseconds, or undefined when the value
|
|
552
|
+
* does not parse
|
|
553
|
+
*/
|
|
554
|
+
export function parseCertificateEndMillis(value) {
|
|
555
|
+
if (typeof value !== "string") {
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
const text = value.trim();
|
|
559
|
+
const dateOnly = DATE_ONLY_RE.exec(text);
|
|
560
|
+
if (dateOnly) {
|
|
561
|
+
const [year, month, day] = dateOnly.slice(1).map(Number);
|
|
562
|
+
const start = new Date(Date.UTC(year, month - 1, day));
|
|
563
|
+
if (
|
|
564
|
+
start.getUTCFullYear() !== year ||
|
|
565
|
+
start.getUTCMonth() !== month - 1 ||
|
|
566
|
+
start.getUTCDate() !== day
|
|
567
|
+
) {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
return start.getTime() + 24 * 60 * 60 * 1000 - 1;
|
|
571
|
+
}
|
|
572
|
+
if (!FULL_TIMESTAMP_RE.test(text)) {
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
const millis = Date.parse(text);
|
|
576
|
+
return Number.isNaN(millis) ? undefined : millis;
|
|
577
|
+
}
|
|
578
|
+
|
|
522
579
|
export async function loadRules(rulesDir) {
|
|
523
580
|
const rules = [];
|
|
524
581
|
if (!safeExistsSync(rulesDir)) {
|
|
@@ -615,16 +672,21 @@ async function interpolateTemplate(template, context, options = {}) {
|
|
|
615
672
|
return template;
|
|
616
673
|
}
|
|
617
674
|
const templateRegex = /\{\{\s*([^}]+)\s*}}/g;
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
675
|
+
// The template is rebuilt from its own slices in one pass, so a value taken
|
|
676
|
+
// from the BOM is never scanned for {{ }} again and never read as a
|
|
677
|
+
// String.replace pattern such as `$&`.
|
|
678
|
+
let result = "";
|
|
679
|
+
let cursor = 0;
|
|
680
|
+
for (const match of template.matchAll(templateRegex)) {
|
|
621
681
|
const [fullMatch, expr] = match;
|
|
682
|
+
let replacement = fullMatch;
|
|
622
683
|
try {
|
|
623
684
|
const expression = jsonata(expr.trim());
|
|
624
685
|
registerCdxHelpers(expression, options);
|
|
625
686
|
const value = await safeEvaluate(expression, context);
|
|
626
|
-
|
|
627
|
-
|
|
687
|
+
if (value !== undefined) {
|
|
688
|
+
replacement = String(value);
|
|
689
|
+
}
|
|
628
690
|
} catch (err) {
|
|
629
691
|
if (DEBUG_MODE) {
|
|
630
692
|
console.warn(
|
|
@@ -633,7 +695,10 @@ async function interpolateTemplate(template, context, options = {}) {
|
|
|
633
695
|
);
|
|
634
696
|
}
|
|
635
697
|
}
|
|
698
|
+
result += template.slice(cursor, match.index) + replacement;
|
|
699
|
+
cursor = match.index + fullMatch.length;
|
|
636
700
|
}
|
|
701
|
+
result += template.slice(cursor);
|
|
637
702
|
return result;
|
|
638
703
|
}
|
|
639
704
|
|
|
@@ -604,9 +604,39 @@ function downgradeSubjectForSpecVersion(subject, specVersion, parentKey) {
|
|
|
604
604
|
certificateProperties.certificateExtension =
|
|
605
605
|
certificateProperties.certificateFileExtension;
|
|
606
606
|
}
|
|
607
|
+
// `relatedCryptographicAssets` and `certificateExtensions` are 1.7-only;
|
|
608
|
+
// the deprecated `subjectPublicKeyRef`/`signatureAlgorithmRef` (valid in
|
|
609
|
+
// 1.6, see data/bom-1.6.schema.json) carry the same links, and the
|
|
610
|
+
// extension facts survive as `cdx:crypto:` properties.
|
|
611
|
+
const relatedAssets = certificateProperties.relatedCryptographicAssets;
|
|
612
|
+
if (Array.isArray(relatedAssets)) {
|
|
613
|
+
for (const asset of relatedAssets) {
|
|
614
|
+
if (
|
|
615
|
+
asset?.type === "publicKey" &&
|
|
616
|
+
!certificateProperties.subjectPublicKeyRef
|
|
617
|
+
) {
|
|
618
|
+
certificateProperties.subjectPublicKeyRef = asset.ref;
|
|
619
|
+
} else if (
|
|
620
|
+
asset?.type === "algorithm" &&
|
|
621
|
+
!certificateProperties.signatureAlgorithmRef
|
|
622
|
+
) {
|
|
623
|
+
certificateProperties.signatureAlgorithmRef = asset.ref;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
delete certificateProperties.relatedCryptographicAssets;
|
|
627
|
+
}
|
|
628
|
+
delete certificateProperties.certificateExtensions;
|
|
607
629
|
delete certificateProperties.serialNumber;
|
|
608
630
|
delete certificateProperties.certificateFileExtension;
|
|
609
631
|
delete certificateProperties.fingerprint;
|
|
632
|
+
} else if (
|
|
633
|
+
specVersion < 1.7 &&
|
|
634
|
+
subject.cryptoProperties?.assetType === "related-crypto-material" &&
|
|
635
|
+
subject.cryptoProperties.relatedCryptoMaterialProperties
|
|
636
|
+
) {
|
|
637
|
+
// The public-key `fingerprint` object is 1.7-only; the SPKI digest stays
|
|
638
|
+
// available as the `cdx:crypto:spkiSha256` property.
|
|
639
|
+
delete subject.cryptoProperties.relatedCryptoMaterialProperties.fingerprint;
|
|
610
640
|
}
|
|
611
641
|
if (specVersion < 1.7 && subject.cryptoProperties?.algorithmProperties) {
|
|
612
642
|
// `algorithmFamily` and `ellipticCurve` are CycloneDX 1.7 additions to
|
|
@@ -28,6 +28,15 @@ import {
|
|
|
28
28
|
TIMEOUT_MS,
|
|
29
29
|
} from "../../core/fs.js";
|
|
30
30
|
import { isMac, isWin } from "../../core/paths.js";
|
|
31
|
+
import { isProjectToolingAllowed } from "../../core/projectTooling.js";
|
|
32
|
+
import { recordToolVersion } from "../../core/toolRegistry.js";
|
|
33
|
+
import { collectProjectRubyDeclarations } from "../../ecosystems/rubyutils.js";
|
|
34
|
+
import {
|
|
35
|
+
dosaiDotnetRequirement,
|
|
36
|
+
dosaiDotnetRequirementAdvice,
|
|
37
|
+
dosaiDotnetRequirementDetail,
|
|
38
|
+
markDosaiLaunchFailureReported,
|
|
39
|
+
} from "../../inventory/dosai.js";
|
|
31
40
|
import {
|
|
32
41
|
bundleInstallWithDocker,
|
|
33
42
|
collectJavaInfo,
|
|
@@ -38,6 +47,7 @@ import {
|
|
|
38
47
|
installSdkmanTool,
|
|
39
48
|
isNvmAvailable,
|
|
40
49
|
isRbenvAvailable,
|
|
50
|
+
isRubyVersionActive,
|
|
41
51
|
isSdkmanAvailable,
|
|
42
52
|
isSdkmanToolAvailable,
|
|
43
53
|
performBundleInstall,
|
|
@@ -58,8 +68,10 @@ import {
|
|
|
58
68
|
readSbtBuildPropertiesVersion,
|
|
59
69
|
readToolVersionEnvHint,
|
|
60
70
|
readToolVersionOverride,
|
|
71
|
+
resolveGradleVersionForJava,
|
|
61
72
|
resolvePartialVersion,
|
|
62
73
|
} from "../../inventory/jvmToolEnv.js";
|
|
74
|
+
import { resolvePluginBinary } from "../../inventory/plugins.js";
|
|
63
75
|
import {
|
|
64
76
|
classifyProbeResult,
|
|
65
77
|
describeSpawnRestriction,
|
|
@@ -74,12 +86,20 @@ import {
|
|
|
74
86
|
* @param {Object} options CLI options
|
|
75
87
|
*/
|
|
76
88
|
export function prepareEnv(filePath, options) {
|
|
77
|
-
if (
|
|
89
|
+
if (isSecureMode) {
|
|
78
90
|
return;
|
|
79
91
|
}
|
|
80
92
|
if (filePath) {
|
|
81
93
|
filePath = resolve(filePath);
|
|
82
94
|
}
|
|
95
|
+
// Runs before the project type check: a .NET tree is also found when cdxgen
|
|
96
|
+
// detects the project types itself.
|
|
97
|
+
if (!isDryRun) {
|
|
98
|
+
prepareDotnetEnv(filePath, options);
|
|
99
|
+
}
|
|
100
|
+
if (!options.projectType) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
83
103
|
// The JVM preparation provisions the JDK ahead of the build tools, so an
|
|
84
104
|
// alias it already installed must not be installed a second time here.
|
|
85
105
|
// It runs under dry-run too, where it reports the toolchain it would set up
|
|
@@ -104,6 +124,91 @@ export function prepareEnv(filePath, options) {
|
|
|
104
124
|
prepareRubyEnv(filePath, options);
|
|
105
125
|
}
|
|
106
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Check that dosai, which deep runs use for .NET evidence, can start. The
|
|
129
|
+
* dosai bundled with cdxgen-plugins-bin is framework-dependent, so it needs a
|
|
130
|
+
* .NET runtime at least as new as the one it targets (.NET 11 from dosai
|
|
131
|
+
* 5.0.0). Running it lets the .NET host apply its own version rules. Skipped
|
|
132
|
+
* in the container images, which ship a dosai that works with their SDK, and
|
|
133
|
+
* when DOSAI_CMD names another build, such as the self-contained -full one.
|
|
134
|
+
*
|
|
135
|
+
* @param {String} filePath Project path
|
|
136
|
+
* @param {Object} options CLI options
|
|
137
|
+
*/
|
|
138
|
+
export function prepareDotnetEnv(filePath, options) {
|
|
139
|
+
if (
|
|
140
|
+
!options.deep ||
|
|
141
|
+
!filePath ||
|
|
142
|
+
readEnvironmentVariable("CDXGEN_IN_CONTAINER") === "true" ||
|
|
143
|
+
readEnvironmentVariable("DOSAI_CMD") ||
|
|
144
|
+
!isDotnetInScope(filePath, options)
|
|
145
|
+
) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const dosaiBin = resolvePluginBinary("dosai");
|
|
149
|
+
if (!dosaiBin) {
|
|
150
|
+
// A missing plugin is reported where dosai runs
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const result = safeSpawnSync(dosaiBin, ["--version"], {
|
|
154
|
+
encoding: "utf-8",
|
|
155
|
+
timeout: TIMEOUT_MS,
|
|
156
|
+
});
|
|
157
|
+
if (result?.status === 0 && !result.error) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const output = commandOutputText(result) || "";
|
|
161
|
+
const requirement = dosaiDotnetRequirement(output);
|
|
162
|
+
const detail = requirement
|
|
163
|
+
? dosaiDotnetRequirementDetail(requirement)
|
|
164
|
+
: "dosai could not start";
|
|
165
|
+
console.log(
|
|
166
|
+
`WARNING: ${detail}, so this deep run has no dosai evidence for .NET (call graph, reachability, crypto). ${dosaiDotnetRequirementAdvice(requirement)}`,
|
|
167
|
+
);
|
|
168
|
+
if (requirement) {
|
|
169
|
+
markDosaiLaunchFailureReported(dosaiBin);
|
|
170
|
+
}
|
|
171
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.EVIDENCE_DEGRADED, {
|
|
172
|
+
ecosystem: "csharp",
|
|
173
|
+
tool: "dosai",
|
|
174
|
+
detail: `${detail}; .NET evidence from dosai is unavailable.`,
|
|
175
|
+
impact: LEDGER_EVENT_IMPACTS.COMPONENTS,
|
|
176
|
+
});
|
|
177
|
+
deferFailOnError(options, {
|
|
178
|
+
ecosystem: "csharp",
|
|
179
|
+
tool: "dosai",
|
|
180
|
+
detail,
|
|
181
|
+
exitCode: typeof result?.status === "number" ? result.status : undefined,
|
|
182
|
+
command: `${dosaiBin} --version`,
|
|
183
|
+
outputExcerpt: output.trim() || undefined,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Whether the run covers .NET: a .NET project type, or, when cdxgen detects
|
|
189
|
+
* the types itself, a .NET project or solution file in the tree.
|
|
190
|
+
*
|
|
191
|
+
* @param {String} filePath Project path
|
|
192
|
+
* @param {Object} options CLI options
|
|
193
|
+
*
|
|
194
|
+
* @returns {Boolean}
|
|
195
|
+
*/
|
|
196
|
+
function isDotnetInScope(filePath, options) {
|
|
197
|
+
if (options.projectType?.length) {
|
|
198
|
+
return hasAnyProjectType(["csharp"], options, false);
|
|
199
|
+
}
|
|
200
|
+
if (!hasAnyProjectType(["csharp"], options, true)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return (
|
|
204
|
+
getAllFiles(
|
|
205
|
+
filePath,
|
|
206
|
+
`${options.multiProject ? "**/" : ""}*.{csproj,vbproj,fsproj,sln,slnx}`,
|
|
207
|
+
options,
|
|
208
|
+
).length > 0
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
107
212
|
/**
|
|
108
213
|
* Record the tool requirements the project declares through its well-known
|
|
109
214
|
* tool pin files (`.tool-versions`, `.nvmrc`, `package.json`, `go.mod`, …),
|
|
@@ -215,6 +320,27 @@ const JVM_TOOL_DEFAULT_PREFIXES = {
|
|
|
215
320
|
maven: "3.9",
|
|
216
321
|
};
|
|
217
322
|
|
|
323
|
+
/**
|
|
324
|
+
* JDK major a versioned java project type names, such as 27 for `java27`.
|
|
325
|
+
*
|
|
326
|
+
* @param {string} projectType Project type
|
|
327
|
+
* @returns {number|undefined} The major, or undefined for any other type
|
|
328
|
+
*/
|
|
329
|
+
export function javaMajorFromProjectType(projectType) {
|
|
330
|
+
if (typeof projectType !== "string" || !projectType.startsWith("java")) {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
const major = Number.parseInt(projectType.slice(4), 10);
|
|
334
|
+
return Number.isInteger(major) && `java${major}` === projectType
|
|
335
|
+
? major
|
|
336
|
+
: undefined;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Command each provisioned sdkman candidate is invoked as. */
|
|
340
|
+
const JVM_TOOL_COMMANDS = {
|
|
341
|
+
maven: "mvn",
|
|
342
|
+
};
|
|
343
|
+
|
|
218
344
|
/** sdkman candidates cdxgen can provision for JVM builds. */
|
|
219
345
|
const JVM_BUILD_TOOLS = ["maven", "gradle", "sbt", "scala"];
|
|
220
346
|
|
|
@@ -299,10 +425,26 @@ export function prepareJvmBuildEnv(filePath, options) {
|
|
|
299
425
|
return;
|
|
300
426
|
}
|
|
301
427
|
// Resolve partial versions (e.g. maven3.9 -> 3.9.16) before touching the JDK
|
|
302
|
-
// so the compatibility matrix sees the real versions.
|
|
428
|
+
// so the compatibility matrix sees the real versions. The Gradle default
|
|
429
|
+
// install is constrained by the JDK the project declared or asked for, so a
|
|
430
|
+
// Java 27 project never gets a Gradle that cannot run on it.
|
|
431
|
+
// A versioned java type counts even without an sdkman alias: java27 has
|
|
432
|
+
// none until the broker serves a Java 27 build, but the project still asked
|
|
433
|
+
// for that JDK.
|
|
434
|
+
const explicitJavaMajor = Math.max(
|
|
435
|
+
0,
|
|
436
|
+
...(options.projectType || []).map((pt) =>
|
|
437
|
+
SDKMAN_JAVA_TOOL_ALIASES[pt]
|
|
438
|
+
? extractJavaMajor(SDKMAN_JAVA_TOOL_ALIASES[pt]) || 0
|
|
439
|
+
: javaMajorFromProjectType(pt) || 0,
|
|
440
|
+
),
|
|
441
|
+
);
|
|
442
|
+
const gradleJavaMajor =
|
|
443
|
+
Math.max(detected.javaVersionHint || 0, explicitJavaMajor || 0) ||
|
|
444
|
+
undefined;
|
|
303
445
|
const resolvedPins = [];
|
|
304
446
|
for (const pin of installPins) {
|
|
305
|
-
const resolved = resolveJvmToolPinVersion(pin);
|
|
447
|
+
const resolved = resolveJvmToolPinVersion(pin, gradleJavaMajor);
|
|
306
448
|
if (resolved) {
|
|
307
449
|
resolvedPins.push(resolved);
|
|
308
450
|
}
|
|
@@ -321,6 +463,11 @@ export function prepareJvmBuildEnv(filePath, options) {
|
|
|
321
463
|
const installed = provisionJvmTool(pin, options);
|
|
322
464
|
if (installed) {
|
|
323
465
|
setJvmToolCommandEnv(pin);
|
|
466
|
+
if (pin.version) {
|
|
467
|
+
// The resolved version is already known, so metadata.tools can carry
|
|
468
|
+
// it without spawning another probe, once the tool runs (issue #598).
|
|
469
|
+
recordToolVersion(JVM_TOOL_COMMANDS[pin.tool] || pin.tool, pin.version);
|
|
470
|
+
}
|
|
324
471
|
if (pin.source === "cli") {
|
|
325
472
|
pinnedToolSelected = true;
|
|
326
473
|
}
|
|
@@ -630,11 +777,14 @@ function recordDetectedJvmPins(installPins, wrapperVersions) {
|
|
|
630
777
|
* and to pick defaults.
|
|
631
778
|
*
|
|
632
779
|
* @param {Object} pin `{ tool, version?, source }`
|
|
780
|
+
* @param {Number|undefined} javaMajor JDK major the project declared or
|
|
781
|
+
* configured, when known. Constrains the default Gradle install so a
|
|
782
|
+
* project on a new JDK never resolves a Gradle that cannot run on it.
|
|
633
783
|
*
|
|
634
784
|
* @returns {Object|undefined} Pin with an exact version, or undefined when no
|
|
635
785
|
* version could be determined.
|
|
636
786
|
*/
|
|
637
|
-
export function resolveJvmToolPinVersion(pin) {
|
|
787
|
+
export function resolveJvmToolPinVersion(pin, javaMajor) {
|
|
638
788
|
if (pin.version && isSdkmanToolAvailable(pin.tool, pin.version)) {
|
|
639
789
|
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_RESOLVED, {
|
|
640
790
|
ecosystem: "java",
|
|
@@ -665,20 +815,30 @@ export function resolveJvmToolPinVersion(pin) {
|
|
|
665
815
|
return undefined;
|
|
666
816
|
}
|
|
667
817
|
const prefix = pin.version || JVM_TOOL_DEFAULT_PREFIXES[pin.tool] || "";
|
|
668
|
-
const resolved =
|
|
818
|
+
const resolved =
|
|
819
|
+
pin.tool === "gradle" && !pin.version
|
|
820
|
+
? resolveGradleVersionForJava(versionList, javaMajor)
|
|
821
|
+
: resolvePartialVersion(versionList, prefix);
|
|
669
822
|
if (!resolved) {
|
|
670
823
|
if (pin.version) {
|
|
671
824
|
return pin;
|
|
672
825
|
}
|
|
826
|
+
const gradleMinimum =
|
|
827
|
+
pin.tool === "gradle"
|
|
828
|
+
? minimumGradleVersionForJava(javaMajor)
|
|
829
|
+
: undefined;
|
|
830
|
+
const missingDetail = gradleMinimum
|
|
831
|
+
? `No stable gradle version at or above ${gradleMinimum}, the first that runs on Java ${javaMajor}, is listed by sdkman.`
|
|
832
|
+
: `No stable ${pin.tool} version matched the default prefix '${prefix}'.`;
|
|
673
833
|
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_MISSING, {
|
|
674
834
|
ecosystem: "java",
|
|
675
835
|
tool: pin.tool,
|
|
676
|
-
wanted: prefix,
|
|
836
|
+
wanted: gradleMinimum ? `>=${gradleMinimum}` : prefix,
|
|
677
837
|
source: "sdkman",
|
|
678
|
-
detail:
|
|
838
|
+
detail: missingDetail,
|
|
679
839
|
});
|
|
680
840
|
console.log(
|
|
681
|
-
|
|
841
|
+
`${missingDetail} Pass an exact version identifier via -t ${pin.tool}<version>.`,
|
|
682
842
|
);
|
|
683
843
|
return undefined;
|
|
684
844
|
}
|
|
@@ -1205,7 +1365,7 @@ export function prepareNodeEnv(filePath, options) {
|
|
|
1205
1365
|
}
|
|
1206
1366
|
}
|
|
1207
1367
|
if (nvmNodePath) {
|
|
1208
|
-
doNpmInstall(filePath, nvmNodePath);
|
|
1368
|
+
doNpmInstall(filePath, nvmNodePath, options);
|
|
1209
1369
|
npmInstallAttempted = true;
|
|
1210
1370
|
} else {
|
|
1211
1371
|
console.log(
|
|
@@ -1228,7 +1388,7 @@ export function prepareNodeEnv(filePath, options) {
|
|
|
1228
1388
|
);
|
|
1229
1389
|
continue;
|
|
1230
1390
|
}
|
|
1231
|
-
doNpmInstall(filePath, nvmNodePath);
|
|
1391
|
+
doNpmInstall(filePath, nvmNodePath, options);
|
|
1232
1392
|
npmInstallAttempted = true;
|
|
1233
1393
|
}
|
|
1234
1394
|
}
|
|
@@ -1302,12 +1462,18 @@ export function tryLoadNvmAndInstallTool(nodeVersion) {
|
|
|
1302
1462
|
*
|
|
1303
1463
|
* @param {String} filePath Path
|
|
1304
1464
|
* @param {String} nvmNodePath Path to node version in nvm
|
|
1465
|
+
* @param {Object} [options] CLI options
|
|
1305
1466
|
*/
|
|
1306
|
-
export function doNpmInstall(filePath, nvmNodePath) {
|
|
1467
|
+
export function doNpmInstall(filePath, nvmNodePath, options = {}) {
|
|
1307
1468
|
// we do not install if INSTALL_ARGS set false
|
|
1308
1469
|
if (["0", "false"].includes(readEnvironmentVariable("NODE_INSTALL_ARGS"))) {
|
|
1309
1470
|
return;
|
|
1310
1471
|
}
|
|
1472
|
+
// npm install runs the project's own lifecycle scripts, such as preinstall,
|
|
1473
|
+
// even with --package-lock-only.
|
|
1474
|
+
if (options.installDeps === false) {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1311
1477
|
const newPath = `${nvmNodePath}${delimiter}${readEnvironmentVariable("PATH")}`;
|
|
1312
1478
|
let installArgs =
|
|
1313
1479
|
readEnvironmentVariable("NPM_INSTALL_ARGS") || "--package-lock-only";
|
|
@@ -1386,7 +1552,12 @@ export function prepareSwiftEnv(filePath, options) {
|
|
|
1386
1552
|
"INFO: Swift for Linux has known issues on non x64 machines. Run the cdxgen container image with --platform=linux/amd64 for best experience.",
|
|
1387
1553
|
);
|
|
1388
1554
|
}
|
|
1389
|
-
|
|
1555
|
+
// `swift package resolve` runs the project's Package.swift, so it waits for
|
|
1556
|
+
// the same permission as the scan's other project tooling.
|
|
1557
|
+
if (
|
|
1558
|
+
(options.deep || options?.lifecycle?.includes("post-build")) &&
|
|
1559
|
+
isProjectToolingAllowed(options)
|
|
1560
|
+
) {
|
|
1390
1561
|
const swiftFiles = getAllFiles(
|
|
1391
1562
|
filePath,
|
|
1392
1563
|
`${options.multiProject ? "**/" : ""}Package*.swift`,
|
|
@@ -1470,6 +1641,17 @@ export function prepareSwiftEnv(filePath, options) {
|
|
|
1470
1641
|
* @param {String} filePath Path
|
|
1471
1642
|
* @param {Object} options CLI Options
|
|
1472
1643
|
*/
|
|
1644
|
+
// The order pregen reads the Ruby to install in.
|
|
1645
|
+
const PREGEN_RUBY_SOURCE_ORDER = [
|
|
1646
|
+
".ruby-version",
|
|
1647
|
+
".tool-versions",
|
|
1648
|
+
"mise.toml",
|
|
1649
|
+
".mise.toml",
|
|
1650
|
+
".config/mise.toml",
|
|
1651
|
+
"Gemfile.lock",
|
|
1652
|
+
"Gemfile",
|
|
1653
|
+
];
|
|
1654
|
+
|
|
1473
1655
|
export function prepareRubyEnv(filePath, options) {
|
|
1474
1656
|
// Skip preparation early
|
|
1475
1657
|
if (
|
|
@@ -1515,53 +1697,39 @@ export function prepareRubyEnv(filePath, options) {
|
|
|
1515
1697
|
readEnvironmentVariable("GEM_HOME") ||
|
|
1516
1698
|
safeMkdtempSync(join(getTmpDir(), "cdxgen-gem-home-"));
|
|
1517
1699
|
process.env.CDXGEN_GEM_HOME = cdxgenGemHome;
|
|
1518
|
-
//
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1700
|
+
// The Ruby to install is the one developers select: .ruby-version, then the
|
|
1701
|
+
// asdf and mise pins, then the Ruby the lockfile was resolved with, then the
|
|
1702
|
+
// Gemfile directive. Only an exact MRI version can be installed with rbenv.
|
|
1703
|
+
const declarations = collectProjectRubyDeclarations(filePath);
|
|
1704
|
+
for (const declaration of declarations) {
|
|
1705
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_EXPECTED, {
|
|
1706
|
+
ecosystem: "ruby",
|
|
1707
|
+
tool: declaration.engine === "ruby" ? "ruby" : declaration.engine,
|
|
1708
|
+
wanted: declaration.version || declaration.requirement,
|
|
1709
|
+
source: declaration.source,
|
|
1710
|
+
path: declaration.path,
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
const installable = PREGEN_RUBY_SOURCE_ORDER.map((source) =>
|
|
1714
|
+
declarations.find(
|
|
1715
|
+
(declaration) => declaration.source === source && declaration.version,
|
|
1716
|
+
),
|
|
1717
|
+
).find(Boolean);
|
|
1718
|
+
if (installable && installable.engine !== "ruby") {
|
|
1719
|
+
console.log(
|
|
1720
|
+
`This project selects ${installable.engine} ${installable.version} (${installable.source}). cdxgen installs only MRI Ruby with rbenv; build the project with ${installable.engine} first and then invoke cdxgen with the arguments "--lifecycle pre-build".`,
|
|
1721
|
+
);
|
|
1722
|
+
} else if (installable?.prerelease) {
|
|
1723
|
+
console.log(
|
|
1724
|
+
`This project was locked with a pre-release of Ruby ${installable.version}, whose tag the lockfile does not record. Install it before invoking cdxgen.`,
|
|
1725
|
+
);
|
|
1726
|
+
} else if (installable) {
|
|
1727
|
+
rubyVersionNeeded = installable.version;
|
|
1728
|
+
}
|
|
1729
|
+
if (safeExistsSync(join(filePath, "Gemfile.lock"))) {
|
|
1536
1730
|
const gemlockData = readFileSync(join(filePath, "Gemfile.lock"), {
|
|
1537
1731
|
encoding: "utf-8",
|
|
1538
1732
|
});
|
|
1539
|
-
let rubyVersionMarker = false;
|
|
1540
|
-
for (let l of gemlockData.split("\n")) {
|
|
1541
|
-
l = l.replaceAll("\r", "").trim();
|
|
1542
|
-
if (l.includes("RUBY VERSION")) {
|
|
1543
|
-
rubyVersionMarker = true;
|
|
1544
|
-
}
|
|
1545
|
-
if (rubyVersionMarker && l.includes("ruby ")) {
|
|
1546
|
-
const possibleVersion = l
|
|
1547
|
-
.split("ruby ")
|
|
1548
|
-
.pop()
|
|
1549
|
-
.split("p")[0]
|
|
1550
|
-
.split("d")[0];
|
|
1551
|
-
if (/^\d+/.test(possibleVersion)) {
|
|
1552
|
-
rubyVersionNeeded = possibleVersion;
|
|
1553
|
-
break;
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
if (rubyVersionNeeded) {
|
|
1558
|
-
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_EXPECTED, {
|
|
1559
|
-
ecosystem: "ruby",
|
|
1560
|
-
tool: "ruby",
|
|
1561
|
-
wanted: rubyVersionNeeded,
|
|
1562
|
-
source: "Gemfile.lock",
|
|
1563
|
-
});
|
|
1564
|
-
}
|
|
1565
1733
|
const bundlerVersion = parseGemfileLockBundlerVersion(gemlockData);
|
|
1566
1734
|
if (bundlerVersion) {
|
|
1567
1735
|
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_EXPECTED, {
|
|
@@ -1571,7 +1739,8 @@ export function prepareRubyEnv(filePath, options) {
|
|
|
1571
1739
|
source: "Gemfile.lock",
|
|
1572
1740
|
});
|
|
1573
1741
|
}
|
|
1574
|
-
}
|
|
1742
|
+
}
|
|
1743
|
+
if (!installable) {
|
|
1575
1744
|
// Is the user invoking with a ruby with custom version type. Eg: -t ruby2.5.4
|
|
1576
1745
|
let projectTypes = options.projectType;
|
|
1577
1746
|
if (
|
|
@@ -1604,13 +1773,13 @@ export function prepareRubyEnv(filePath, options) {
|
|
|
1604
1773
|
const existingRuby = collectRubyInfo(filePath);
|
|
1605
1774
|
if (
|
|
1606
1775
|
rubyVersionNeeded &&
|
|
1607
|
-
existingRuby
|
|
1776
|
+
isRubyVersionActive(existingRuby, rubyVersionNeeded)
|
|
1608
1777
|
) {
|
|
1609
1778
|
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_RESOLVED, {
|
|
1610
1779
|
ecosystem: "ruby",
|
|
1611
1780
|
tool: "ruby",
|
|
1612
1781
|
wanted: rubyVersionNeeded,
|
|
1613
|
-
found: existingRuby.version,
|
|
1782
|
+
found: existingRuby.description || existingRuby.version,
|
|
1614
1783
|
source: "env",
|
|
1615
1784
|
detail: `The active interpreter is the required Ruby ${rubyVersionNeeded}.`,
|
|
1616
1785
|
});
|
|
@@ -1642,15 +1811,15 @@ export function prepareRubyEnv(filePath, options) {
|
|
|
1642
1811
|
});
|
|
1643
1812
|
} else if (
|
|
1644
1813
|
rubyVersionNeeded &&
|
|
1645
|
-
!existingRuby
|
|
1814
|
+
!isRubyVersionActive(existingRuby, rubyVersionNeeded)
|
|
1646
1815
|
) {
|
|
1647
1816
|
recordLedgerEvent(LEDGER_EVENT_KINDS.TOOL_MISMATCH, {
|
|
1648
1817
|
ecosystem: "ruby",
|
|
1649
1818
|
tool: "ruby",
|
|
1650
1819
|
wanted: rubyVersionNeeded,
|
|
1651
|
-
found: existingRuby.version,
|
|
1820
|
+
found: existingRuby.description || existingRuby.version,
|
|
1652
1821
|
source: "env",
|
|
1653
|
-
detail: `Ruby ${rubyVersionNeeded} is required and the active interpreter reports ${existingRuby.version}.`,
|
|
1822
|
+
detail: `Ruby ${rubyVersionNeeded} is required and the active interpreter reports ${existingRuby.description || existingRuby.version}.`,
|
|
1654
1823
|
});
|
|
1655
1824
|
}
|
|
1656
1825
|
if (rubyVersionNeeded && !rbenvPresent) {
|