@cdxgen/cdxgen 13.2.0 → 13.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 +33 -14
- package/bin/cdxgen.js +310 -138
- package/bin/evinse.js +22 -0
- package/bin/repl.js +86 -0
- package/bin/sign.js +119 -15
- package/bin/tracebom.js +69 -5
- package/bin/validate.js +107 -19
- package/bin/verify.js +172 -63
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +85 -0
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +38 -6
- package/lib/cli/bomAssembly.js +105 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +156 -58
- package/lib/cli/jsBom.js +99 -53
- package/lib/cli/jvmBom.js +708 -147
- package/lib/cli/managedBom.js +415 -224
- package/lib/cli/nativeBom.js +264 -44
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +22 -15
- package/lib/core/fs.js +176 -7
- package/lib/core/httpClient.js +159 -24
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +274 -108
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/caxa.js +434 -2
- package/lib/ecosystems/cmakeResolver.js +76 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +399 -51
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1983 -793
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +356 -37
- package/lib/ecosystems/jsEvidence.js +104 -44
- package/lib/ecosystems/kosi.js +31 -7
- package/lib/ecosystems/npmutils.js +174 -9
- package/lib/ecosystems/parsers-dotnet.js +616 -159
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +371 -70
- package/lib/ecosystems/parsers-js.js +263 -76
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +929 -216
- package/lib/ecosystems/parsers-python.js +167 -24
- package/lib/ecosystems/parsers-rust.js +28 -22
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +1492 -182
- package/lib/ecosystems/rusi.js +54 -10
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +342 -0
- package/lib/ecosystems/scalasem.js +2618 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +1008 -191
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +1101 -222
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +703 -43
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cdxrs.js +103 -12
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +798 -57
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +90 -6
- package/lib/inventory/evidenceUtils.js +166 -113
- package/lib/inventory/fetchBatch.js +690 -55
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/protobom.js +8 -1
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +45 -27
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +295 -25
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +467 -46
- package/lib/managers/oci.js +63 -3
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +466 -0
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +23 -0
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/postgen.js +83 -16
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +64 -11
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +2 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +23 -23
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- 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.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +24 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +9 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +55 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- package/types/lib/core/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- package/types/lib/ecosystems/bunutils.d.ts +12 -5
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- package/types/lib/ecosystems/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +28 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +17 -1
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +70 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +13 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +57 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +54 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +16 -5
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +70 -4
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +53 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +27 -2
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +164 -3
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +132 -15
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +182 -13
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +219 -5
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
- package/types/lib/inventory/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +59 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +121 -9
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +10 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +19 -4
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +9 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +59 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +42 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +17 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +77 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- package/types/lib/parsers/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +18 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
package/lib/managers/oci.js
CHANGED
|
@@ -229,6 +229,35 @@ function getDockerCreds(registry) {
|
|
|
229
229
|
return readEnvironmentVariable("DOCKER_AUTH");
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Check whether a token realm URL belongs to the registry itself.
|
|
234
|
+
*
|
|
235
|
+
* A registry's 401 response names the realm, so a hostile registry picks the
|
|
236
|
+
* URL cdxgen will call. Credentials picked up for the registry must therefore
|
|
237
|
+
* only be attached when the realm resolves to the same host and port; public
|
|
238
|
+
* delegating realms (docker.io to auth.docker.io) are still usable, just
|
|
239
|
+
* anonymously.
|
|
240
|
+
*
|
|
241
|
+
* @param {string} registry Registry host, optionally with a port
|
|
242
|
+
* @param {URL} realmUrl Parsed realm URL from the WWW-Authenticate header
|
|
243
|
+
* @returns {boolean} true when host and port match the registry
|
|
244
|
+
*/
|
|
245
|
+
export function isSameRegistryHost(registry, realmUrl) {
|
|
246
|
+
try {
|
|
247
|
+
const registryUrl = new URL(
|
|
248
|
+
registry.startsWith("localhost")
|
|
249
|
+
? `http://${registry}`
|
|
250
|
+
: `https://${registry}`,
|
|
251
|
+
);
|
|
252
|
+
return (
|
|
253
|
+
registryUrl.hostname === realmUrl.hostname &&
|
|
254
|
+
registryUrl.port === realmUrl.port
|
|
255
|
+
);
|
|
256
|
+
} catch (_e) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
232
261
|
async function getOciToken(registry, repository, scope) {
|
|
233
262
|
const creds = getDockerCreds(registry);
|
|
234
263
|
const scheme = registry.startsWith("localhost") ? "http" : "https";
|
|
@@ -254,12 +283,24 @@ async function getOciToken(registry, repository, scope) {
|
|
|
254
283
|
if (params.service)
|
|
255
284
|
reqUrl.searchParams.set("service", params.service);
|
|
256
285
|
reqUrl.searchParams.set("scope", `repository:${repository}:${scope}`);
|
|
286
|
+
// Tokens expire within minutes, so a cached one would be replayed
|
|
287
|
+
// after it stopped working.
|
|
257
288
|
const options = {
|
|
289
|
+
cache: false,
|
|
258
290
|
responseType: "json",
|
|
259
291
|
timeout: getOciTimeoutMs(),
|
|
260
292
|
};
|
|
261
|
-
|
|
293
|
+
// The realm comes from the registry's own 401 response, so a
|
|
294
|
+
// hostile registry chooses this URL. Never forward the stored
|
|
295
|
+
// registry credentials to a different host; an anonymous cross-host
|
|
296
|
+
// token request (how public registries such as docker.io delegate
|
|
297
|
+
// to auth.docker.io) is still allowed.
|
|
298
|
+
if (creds && isSameRegistryHost(registry, reqUrl)) {
|
|
262
299
|
options.headers = { Authorization: `Basic ${creds}` };
|
|
300
|
+
} else if (creds) {
|
|
301
|
+
console.warn(
|
|
302
|
+
`Skipping credential forwarding to the token realm ${reqUrl.host} for registry ${registry}: the host differs.`,
|
|
303
|
+
);
|
|
263
304
|
}
|
|
264
305
|
const tokenRes = await cdxgenAgent.get(reqUrl.toString(), options);
|
|
265
306
|
if (
|
|
@@ -372,11 +413,18 @@ async function pullBlob(registry, repository, digest, token) {
|
|
|
372
413
|
*
|
|
373
414
|
* @param {string} image OCI image reference (e.g. `"registry.example.com/org/app:tag"`)
|
|
374
415
|
* @param {string} [platform] OCI platform string (e.g. `"linux/amd64"`); no-op for JS implementation
|
|
416
|
+
* @param {Object} [options]
|
|
417
|
+
* @param {function(Buffer): void} [options.checkBytes] Called with the raw bytes of the BOM layer that is about to be returned. An error it throws is rethrown, and no other referrer is tried.
|
|
375
418
|
* @returns {Promise<Object|undefined>} Parsed CycloneDX BOM JSON object, or `undefined` if not found
|
|
376
419
|
*/
|
|
377
|
-
export async function getBomWithOras(
|
|
420
|
+
export async function getBomWithOras(
|
|
421
|
+
image,
|
|
422
|
+
_platform = undefined,
|
|
423
|
+
options = {},
|
|
424
|
+
) {
|
|
378
425
|
const { registry, repository, reference } = parseImageRef(image);
|
|
379
426
|
const token = await getOciToken(registry, repository, "pull");
|
|
427
|
+
let rejection;
|
|
380
428
|
|
|
381
429
|
try {
|
|
382
430
|
const targetManifest = await fetchManifest(
|
|
@@ -450,9 +498,10 @@ export async function getBomWithOras(image, _platform = undefined) {
|
|
|
450
498
|
bomLayer.digest,
|
|
451
499
|
token,
|
|
452
500
|
);
|
|
501
|
+
const text = blob.toString("utf8");
|
|
453
502
|
let bomJson;
|
|
454
503
|
try {
|
|
455
|
-
bomJson = JSON.parse(
|
|
504
|
+
bomJson = JSON.parse(text);
|
|
456
505
|
} catch {
|
|
457
506
|
// Defensive: the mediaType claimed JSON but the layer did not parse
|
|
458
507
|
// (truncated/corrupt blob). Skip it and try the next candidate.
|
|
@@ -471,6 +520,14 @@ export async function getBomWithOras(image, _platform = undefined) {
|
|
|
471
520
|
}
|
|
472
521
|
|
|
473
522
|
if (isCycloneDxBom(bomJson)) {
|
|
523
|
+
if (options.checkBytes) {
|
|
524
|
+
try {
|
|
525
|
+
options.checkBytes(blob);
|
|
526
|
+
} catch (err) {
|
|
527
|
+
rejection = err;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
474
531
|
return bomJson;
|
|
475
532
|
}
|
|
476
533
|
}
|
|
@@ -479,6 +536,9 @@ export async function getBomWithOras(image, _platform = undefined) {
|
|
|
479
536
|
`Unable to pull the SBOM attachment for ${image} natively! ${e.message}`,
|
|
480
537
|
);
|
|
481
538
|
}
|
|
539
|
+
if (rejection) {
|
|
540
|
+
throw rejection;
|
|
541
|
+
}
|
|
482
542
|
return undefined;
|
|
483
543
|
}
|
|
484
544
|
|
|
@@ -197,3 +197,72 @@ export function resolveCmakeCacheFacts(map) {
|
|
|
197
197
|
fetchContentBase,
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
|
+
|
|
201
|
+
/** CMake's compiler ids, mapped to the family names the C/C++ toolchain uses. */
|
|
202
|
+
const CMAKE_COMPILER_FAMILIES = {
|
|
203
|
+
GNU: "gcc",
|
|
204
|
+
Clang: "clang",
|
|
205
|
+
AppleClang: "apple-clang",
|
|
206
|
+
MSVC: "msvc",
|
|
207
|
+
IntelLLVM: "icx",
|
|
208
|
+
Intel: "icc",
|
|
209
|
+
NVIDIA: "nvcc",
|
|
210
|
+
NVHPC: "nvhpc",
|
|
211
|
+
PGI: "nvhpc",
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Parse a compiler description CMake writes when it configures a build
|
|
216
|
+
* (`CMakeFiles/<cmake version>/CMake<LANG>Compiler.cmake`): the compiler's
|
|
217
|
+
* path, CMake's id for it and its version, all identified by CMake itself.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} text File contents
|
|
220
|
+
* @returns {{language: string, compiler: string, family: string, id: string, version: string}|null}
|
|
221
|
+
* `null` when the text names no compiler
|
|
222
|
+
*/
|
|
223
|
+
export function parseCmakeCompilerFile(text) {
|
|
224
|
+
if (!text || typeof text !== "string") {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const values = new Map();
|
|
228
|
+
for (const rawLine of text.split(/\r\n|\r|\n/)) {
|
|
229
|
+
const line = rawLine.trim();
|
|
230
|
+
if (!line.startsWith("set(CMAKE_") || !line.endsWith(")")) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const body = line.slice(4, -1).trim();
|
|
234
|
+
const space = body.search(/\s/);
|
|
235
|
+
if (space < 0) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const key = body.slice(0, space);
|
|
239
|
+
let value = body.slice(space).trim();
|
|
240
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
241
|
+
value = value.slice(1, -1);
|
|
242
|
+
}
|
|
243
|
+
if (!values.has(key)) {
|
|
244
|
+
values.set(key, value);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
for (const [key, compiler] of values) {
|
|
248
|
+
const match = key.match(/^CMAKE_([A-Z]+)_COMPILER$/);
|
|
249
|
+
if (!match || !compiler) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const language = match[1];
|
|
253
|
+
const id = values.get(`CMAKE_${language}_COMPILER_ID`) || "";
|
|
254
|
+
const variant = values.get(`CMAKE_${language}_COMPILER_FRONTEND_VARIANT`);
|
|
255
|
+
let family = CMAKE_COMPILER_FAMILIES[id] || id.toLowerCase() || "unknown";
|
|
256
|
+
if (id === "Clang" && variant === "MSVC") {
|
|
257
|
+
family = "clang-cl";
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
language,
|
|
261
|
+
compiler,
|
|
262
|
+
family,
|
|
263
|
+
id,
|
|
264
|
+
version: values.get(`CMAKE_${language}_COMPILER_VERSION`) || "",
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure parser for CMake language files (`CMakeLists.txt`, `*.cmake`).
|
|
3
|
+
*
|
|
4
|
+
* `parseCmakeCommands` splits a file into command invocations the way CMake
|
|
5
|
+
* reads it: command names are case-insensitive, an invocation may span several
|
|
6
|
+
* lines, and arguments may be quoted, bracketed (`[[...]]`) or interleaved with
|
|
7
|
+
* comments. `cmakeFetchDependencies` reads the commands that download a
|
|
8
|
+
* dependency at configure time: `FetchContent_Declare`, `ExternalProject_Add`
|
|
9
|
+
* and the CPM.cmake `CPMAddPackage` family.
|
|
10
|
+
*
|
|
11
|
+
* This module is layer 1 (text in, data out): no filesystem or subprocess
|
|
12
|
+
* access. `lib/ecosystems/parsers-misc.js` turns the facts into components.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Length of the bracket opener (`[[`, `[=[`, `[==[`, ...) at `text[start]`, or 0.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} text CMake source
|
|
19
|
+
* @param {number} start Index of a `[`
|
|
20
|
+
* @returns {number} Opener length including both brackets, or 0
|
|
21
|
+
*/
|
|
22
|
+
function bracketOpenLength(text, start) {
|
|
23
|
+
if (text[start] !== "[") {
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
let i = start + 1;
|
|
27
|
+
while (text[i] === "=") {
|
|
28
|
+
i++;
|
|
29
|
+
}
|
|
30
|
+
return text[i] === "[" ? i - start + 1 : 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Index just past the bracket closer matching an opener of `openLength`.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} text CMake source
|
|
37
|
+
* @param {number} contentStart First index after the opener
|
|
38
|
+
* @param {number} openLength Opener length (`[==[` is 4)
|
|
39
|
+
* @returns {number} Index after the closer, or the text length when unclosed
|
|
40
|
+
*/
|
|
41
|
+
function bracketCloseEnd(text, contentStart, openLength) {
|
|
42
|
+
const closer = `]${"=".repeat(openLength - 2)}]`;
|
|
43
|
+
const end = text.indexOf(closer, contentStart);
|
|
44
|
+
return end === -1 ? text.length : end + closer.length;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Index of the end of the comment starting at `text[start]` (a `#`).
|
|
49
|
+
*
|
|
50
|
+
* @param {string} text CMake source
|
|
51
|
+
* @param {number} start Index of the `#`
|
|
52
|
+
* @returns {number} Index of the first character after the comment
|
|
53
|
+
*/
|
|
54
|
+
function skipComment(text, start) {
|
|
55
|
+
const openLength = bracketOpenLength(text, start + 1);
|
|
56
|
+
if (openLength) {
|
|
57
|
+
return bracketCloseEnd(text, start + 1 + openLength, openLength);
|
|
58
|
+
}
|
|
59
|
+
const eol = text.indexOf("\n", start);
|
|
60
|
+
return eol === -1 ? text.length : eol;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isIdentifierStart(c) {
|
|
64
|
+
return (c >= "A" && c <= "Z") || (c >= "a" && c <= "z") || c === "_";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isIdentifierPart(c) {
|
|
68
|
+
return isIdentifierStart(c) || (c >= "0" && c <= "9");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isSpace(c) {
|
|
72
|
+
return c === " " || c === "\t" || c === "\r" || c === "\n";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Split CMake source into command invocations.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} text Contents of a CMake file
|
|
79
|
+
* @returns {{name: string, args: string[], line: number}[]} Invocations in
|
|
80
|
+
* source order. `name` is lower-cased; `args` have their quotes and bracket
|
|
81
|
+
* delimiters removed; `line` is the 1-based line of the command name.
|
|
82
|
+
*/
|
|
83
|
+
export function parseCmakeCommands(text) {
|
|
84
|
+
const commands = [];
|
|
85
|
+
if (!text || typeof text !== "string") {
|
|
86
|
+
return commands;
|
|
87
|
+
}
|
|
88
|
+
let line = 1;
|
|
89
|
+
let i = 0;
|
|
90
|
+
const advanceTo = (end) => {
|
|
91
|
+
for (let k = i; k < end; k++) {
|
|
92
|
+
if (text[k] === "\n") {
|
|
93
|
+
line++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
i = end;
|
|
97
|
+
};
|
|
98
|
+
while (i < text.length) {
|
|
99
|
+
const c = text[i];
|
|
100
|
+
if (c === "#") {
|
|
101
|
+
advanceTo(skipComment(text, i));
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (!isIdentifierStart(c) || (i > 0 && isIdentifierPart(text[i - 1]))) {
|
|
105
|
+
advanceTo(i + 1);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
let end = i;
|
|
109
|
+
while (end < text.length && isIdentifierPart(text[end])) {
|
|
110
|
+
end++;
|
|
111
|
+
}
|
|
112
|
+
const name = text.slice(i, end).toLowerCase();
|
|
113
|
+
const commandLine = line;
|
|
114
|
+
let open = end;
|
|
115
|
+
while (text[open] === " " || text[open] === "\t") {
|
|
116
|
+
open++;
|
|
117
|
+
}
|
|
118
|
+
if (text[open] !== "(") {
|
|
119
|
+
advanceTo(end);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
advanceTo(open + 1);
|
|
123
|
+
const args = [];
|
|
124
|
+
let depth = 1;
|
|
125
|
+
let current = "";
|
|
126
|
+
let hasToken = false;
|
|
127
|
+
const flush = () => {
|
|
128
|
+
if (hasToken) {
|
|
129
|
+
args.push(current);
|
|
130
|
+
}
|
|
131
|
+
current = "";
|
|
132
|
+
hasToken = false;
|
|
133
|
+
};
|
|
134
|
+
while (i < text.length && depth > 0) {
|
|
135
|
+
const ch = text[i];
|
|
136
|
+
if (ch === "#") {
|
|
137
|
+
flush();
|
|
138
|
+
advanceTo(skipComment(text, i));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (ch === '"') {
|
|
142
|
+
let k = i + 1;
|
|
143
|
+
let value = "";
|
|
144
|
+
while (k < text.length && text[k] !== '"') {
|
|
145
|
+
if (text[k] === "\\" && k + 1 < text.length) {
|
|
146
|
+
value += text[k + 1];
|
|
147
|
+
k += 2;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
value += text[k];
|
|
151
|
+
k++;
|
|
152
|
+
}
|
|
153
|
+
current += value;
|
|
154
|
+
hasToken = true;
|
|
155
|
+
advanceTo(Math.min(k + 1, text.length));
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const openLength = bracketOpenLength(text, i);
|
|
159
|
+
if (openLength && !hasToken) {
|
|
160
|
+
const contentStart = i + openLength;
|
|
161
|
+
const closeEnd = bracketCloseEnd(text, contentStart, openLength);
|
|
162
|
+
const contentEnd = Math.max(contentStart, closeEnd - openLength);
|
|
163
|
+
current += text.slice(contentStart, contentEnd);
|
|
164
|
+
hasToken = true;
|
|
165
|
+
advanceTo(closeEnd);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (ch === "(") {
|
|
169
|
+
depth++;
|
|
170
|
+
} else if (ch === ")") {
|
|
171
|
+
depth--;
|
|
172
|
+
if (depth === 0) {
|
|
173
|
+
flush();
|
|
174
|
+
advanceTo(i + 1);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (isSpace(ch)) {
|
|
179
|
+
flush();
|
|
180
|
+
} else {
|
|
181
|
+
current += ch;
|
|
182
|
+
hasToken = true;
|
|
183
|
+
}
|
|
184
|
+
advanceTo(i + 1);
|
|
185
|
+
}
|
|
186
|
+
commands.push({ name, args, line: commandLine });
|
|
187
|
+
}
|
|
188
|
+
return commands;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Upper bound on an expanded value; a set() chain that doubles a variable can
|
|
192
|
+
// otherwise grow it beyond the maximum string length.
|
|
193
|
+
const MAX_EXPANDED_LENGTH = 65536;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Replace `${VAR}` references with the values of simple `set()` calls. A
|
|
197
|
+
* reference to an unknown variable is left in place, so callers can tell a
|
|
198
|
+
* resolved value from an unresolved one. Expansion stops at the previous
|
|
199
|
+
* round once the text would exceed 64 KiB.
|
|
200
|
+
*
|
|
201
|
+
* @param {string} value Argument text
|
|
202
|
+
* @param {Map<string,string>} variables Known variable values
|
|
203
|
+
* @returns {string} The expanded text
|
|
204
|
+
*/
|
|
205
|
+
export function expandCmakeVariables(value, variables) {
|
|
206
|
+
let result = value;
|
|
207
|
+
for (let round = 0; round < 5 && result.includes("${"); round++) {
|
|
208
|
+
let next = "";
|
|
209
|
+
let i = 0;
|
|
210
|
+
while (i < result.length) {
|
|
211
|
+
if (next.length > MAX_EXPANDED_LENGTH) {
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
const start = result.indexOf("${", i);
|
|
215
|
+
if (start === -1) {
|
|
216
|
+
next += result.slice(i);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
const end = result.indexOf("}", start + 2);
|
|
220
|
+
if (end === -1) {
|
|
221
|
+
next += result.slice(i);
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
const key = result.slice(start + 2, end);
|
|
225
|
+
next += result.slice(i, start);
|
|
226
|
+
next += variables.has(key)
|
|
227
|
+
? variables.get(key)
|
|
228
|
+
: result.slice(start, end + 1);
|
|
229
|
+
i = end + 1;
|
|
230
|
+
}
|
|
231
|
+
if (next === result) {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
result = next;
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The variables a file sets to a single value: `set(FOO_VERSION 1.2.3)` and
|
|
241
|
+
* `set(FOO_VERSION "1.2.3" CACHE STRING "...")`. Later definitions win.
|
|
242
|
+
*
|
|
243
|
+
* @param {{name: string, args: string[]}[]} commands Parsed commands
|
|
244
|
+
* @returns {Map<string,string>} Variable values
|
|
245
|
+
*/
|
|
246
|
+
export function cmakeSetVariables(commands) {
|
|
247
|
+
const variables = new Map();
|
|
248
|
+
for (const command of commands) {
|
|
249
|
+
if (command.name !== "set" || command.args.length < 2) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const [key, value, ...rest] = command.args;
|
|
253
|
+
if (
|
|
254
|
+
rest.length === 0 ||
|
|
255
|
+
rest[0] === "CACHE" ||
|
|
256
|
+
rest[0] === "PARENT_SCOPE"
|
|
257
|
+
) {
|
|
258
|
+
// An appending set(FOO "${FOO} ...") of an undefined FOO starts from an
|
|
259
|
+
// empty value in CMake; dropping the self-reference also keeps it from
|
|
260
|
+
// being substituted into itself on every later expansion round.
|
|
261
|
+
variables.set(
|
|
262
|
+
key,
|
|
263
|
+
expandCmakeVariables(value, variables).replaceAll(`\${${key}}`, ""),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return variables;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The value following `keyword` in a keyword-style argument list, skipping the
|
|
272
|
+
* leading positional arguments.
|
|
273
|
+
*
|
|
274
|
+
* @param {string[]} args Command arguments
|
|
275
|
+
* @param {string} keyword Keyword to look up, e.g. `GIT_TAG`
|
|
276
|
+
* @param {number} from First index that may hold a keyword
|
|
277
|
+
* @returns {string|undefined} The value, or undefined when absent
|
|
278
|
+
*/
|
|
279
|
+
function keywordValue(args, keyword, from) {
|
|
280
|
+
for (let i = from; i < args.length - 1; i++) {
|
|
281
|
+
if (args[i] === keyword) {
|
|
282
|
+
return args[i + 1];
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const HOST_SHORTHANDS = {
|
|
289
|
+
gh: "https://github.com/",
|
|
290
|
+
gl: "https://gitlab.com/",
|
|
291
|
+
bb: "https://bitbucket.org/",
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Read a CPM.cmake shorthand such as `gh:fmtlib/fmt#7.1.3`,
|
|
296
|
+
* `gh:nlohmann/json@3.11.2` or `https://example.com/x.git@1.0#v1.0`.
|
|
297
|
+
*
|
|
298
|
+
* @param {string} spec The single CPMAddPackage argument
|
|
299
|
+
* @returns {{name: string, gitRepository?: string, url?: string, gitTag?: string, version?: string}|undefined}
|
|
300
|
+
*/
|
|
301
|
+
function parseCpmShorthand(spec) {
|
|
302
|
+
let rest = spec;
|
|
303
|
+
let gitTag;
|
|
304
|
+
let version;
|
|
305
|
+
const hash = rest.lastIndexOf("#");
|
|
306
|
+
if (hash !== -1) {
|
|
307
|
+
gitTag = rest.slice(hash + 1);
|
|
308
|
+
rest = rest.slice(0, hash);
|
|
309
|
+
}
|
|
310
|
+
const colon = rest.indexOf(":");
|
|
311
|
+
const scheme = colon === -1 ? "" : rest.slice(0, colon);
|
|
312
|
+
let repository;
|
|
313
|
+
if (HOST_SHORTHANDS[scheme]) {
|
|
314
|
+
let path = rest.slice(colon + 1);
|
|
315
|
+
const at = path.lastIndexOf("@");
|
|
316
|
+
if (at !== -1) {
|
|
317
|
+
version = path.slice(at + 1);
|
|
318
|
+
path = path.slice(0, at);
|
|
319
|
+
}
|
|
320
|
+
repository = `${HOST_SHORTHANDS[scheme]}${path}`;
|
|
321
|
+
} else if (scheme === "https" || scheme === "http") {
|
|
322
|
+
const lastSlash = rest.lastIndexOf("/");
|
|
323
|
+
const at = rest.lastIndexOf("@");
|
|
324
|
+
if (at > lastSlash) {
|
|
325
|
+
version = rest.slice(at + 1);
|
|
326
|
+
rest = rest.slice(0, at);
|
|
327
|
+
}
|
|
328
|
+
repository = rest;
|
|
329
|
+
} else {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
const name = repository
|
|
333
|
+
.split("/")
|
|
334
|
+
.filter(Boolean)
|
|
335
|
+
.pop()
|
|
336
|
+
?.replace(/\.git$/, "");
|
|
337
|
+
if (!name) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
name,
|
|
342
|
+
gitRepository: repository,
|
|
343
|
+
gitTag: gitTag || (version ? `v${version}` : undefined),
|
|
344
|
+
version,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* The dependencies a CMake file downloads at configure time.
|
|
350
|
+
*
|
|
351
|
+
* `FetchContent_Declare(<name> ...)` and `ExternalProject_Add(<name> ...)` are
|
|
352
|
+
* read for `GIT_REPOSITORY`/`GIT_TAG` and `URL`/`URL_HASH`/`URL_MD5`.
|
|
353
|
+
* `CPMAddPackage`, `CPMFindPackage` and `CPMDeclarePackage` are read in both
|
|
354
|
+
* the shorthand form and the keyword form (`NAME`, `VERSION`,
|
|
355
|
+
* `GITHUB_REPOSITORY`, `GITLAB_REPOSITORY`, `BITBUCKET_REPOSITORY`,
|
|
356
|
+
* `GIT_REPOSITORY`, `GIT_TAG`, `URL`, `URL_HASH`). `${VAR}` references are
|
|
357
|
+
* expanded with the file's simple `set()` values; anything still unresolved
|
|
358
|
+
* stays as written.
|
|
359
|
+
*
|
|
360
|
+
* @param {string} text Contents of a CMake file
|
|
361
|
+
* @returns {{kind: string, name: string, line: number, gitRepository?: string, gitTag?: string, url?: string, urlHash?: string, version?: string, madeAvailable: boolean}[]}
|
|
362
|
+
* `kind` is `fetch`, `external-project` or `cpm`; `madeAvailable` is true
|
|
363
|
+
* when a `FetchContent_MakeAvailable` in the same file names the dependency.
|
|
364
|
+
*/
|
|
365
|
+
export function cmakeFetchDependencies(text) {
|
|
366
|
+
const commands = parseCmakeCommands(text);
|
|
367
|
+
const variables = cmakeSetVariables(commands);
|
|
368
|
+
const expand = (value) =>
|
|
369
|
+
value === undefined ? undefined : expandCmakeVariables(value, variables);
|
|
370
|
+
const madeAvailable = new Set();
|
|
371
|
+
for (const command of commands) {
|
|
372
|
+
if (command.name === "fetchcontent_makeavailable") {
|
|
373
|
+
for (const arg of command.args) {
|
|
374
|
+
madeAvailable.add(expand(arg).toLowerCase());
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const deps = [];
|
|
379
|
+
for (const command of commands) {
|
|
380
|
+
const args = command.args.map(expand);
|
|
381
|
+
let dep;
|
|
382
|
+
if (
|
|
383
|
+
(command.name === "fetchcontent_declare" ||
|
|
384
|
+
command.name === "externalproject_add") &&
|
|
385
|
+
args.length
|
|
386
|
+
) {
|
|
387
|
+
const url = keywordValue(args, "URL", 1);
|
|
388
|
+
const md5 = keywordValue(args, "URL_MD5", 1);
|
|
389
|
+
dep = {
|
|
390
|
+
kind:
|
|
391
|
+
command.name === "fetchcontent_declare"
|
|
392
|
+
? "fetch"
|
|
393
|
+
: "external-project",
|
|
394
|
+
name: args[0],
|
|
395
|
+
gitRepository: keywordValue(args, "GIT_REPOSITORY", 1),
|
|
396
|
+
gitTag: keywordValue(args, "GIT_TAG", 1),
|
|
397
|
+
url: url?.split(";")[0],
|
|
398
|
+
urlHash: keywordValue(args, "URL_HASH", 1) || (md5 && `MD5=${md5}`),
|
|
399
|
+
};
|
|
400
|
+
} else if (
|
|
401
|
+
command.name === "cpmaddpackage" ||
|
|
402
|
+
command.name === "cpmfindpackage" ||
|
|
403
|
+
command.name === "cpmdeclarepackage"
|
|
404
|
+
) {
|
|
405
|
+
const positional = command.name === "cpmdeclarepackage" ? 1 : 0;
|
|
406
|
+
if (args.length === positional + 1) {
|
|
407
|
+
const shorthand = parseCpmShorthand(args[positional]);
|
|
408
|
+
if (shorthand) {
|
|
409
|
+
dep = {
|
|
410
|
+
kind: "cpm",
|
|
411
|
+
...shorthand,
|
|
412
|
+
name: positional ? args[0] : shorthand.name,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
} else if (args.length > positional) {
|
|
416
|
+
const name = positional ? args[0] : keywordValue(args, "NAME", 0);
|
|
417
|
+
const version = keywordValue(args, "VERSION", positional);
|
|
418
|
+
const github = keywordValue(args, "GITHUB_REPOSITORY", positional);
|
|
419
|
+
const gitlab = keywordValue(args, "GITLAB_REPOSITORY", positional);
|
|
420
|
+
const bitbucket = keywordValue(
|
|
421
|
+
args,
|
|
422
|
+
"BITBUCKET_REPOSITORY",
|
|
423
|
+
positional,
|
|
424
|
+
);
|
|
425
|
+
const hosted =
|
|
426
|
+
(github && `${HOST_SHORTHANDS.gh}${github}`) ||
|
|
427
|
+
(gitlab && `${HOST_SHORTHANDS.gl}${gitlab}`) ||
|
|
428
|
+
(bitbucket && `${HOST_SHORTHANDS.bb}${bitbucket}`);
|
|
429
|
+
const gitRepository =
|
|
430
|
+
hosted || keywordValue(args, "GIT_REPOSITORY", positional);
|
|
431
|
+
const url = keywordValue(args, "URL", positional);
|
|
432
|
+
const derivedName =
|
|
433
|
+
name ||
|
|
434
|
+
(gitRepository || url)
|
|
435
|
+
?.split("/")
|
|
436
|
+
.filter(Boolean)
|
|
437
|
+
.pop()
|
|
438
|
+
?.replace(/\.git$/, "");
|
|
439
|
+
if (derivedName) {
|
|
440
|
+
dep = {
|
|
441
|
+
kind: "cpm",
|
|
442
|
+
name: derivedName,
|
|
443
|
+
gitRepository,
|
|
444
|
+
gitTag:
|
|
445
|
+
keywordValue(args, "GIT_TAG", positional) ||
|
|
446
|
+
(gitRepository && version ? `v${version}` : undefined),
|
|
447
|
+
url,
|
|
448
|
+
urlHash: keywordValue(args, "URL_HASH", positional),
|
|
449
|
+
version,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (dep?.name) {
|
|
455
|
+
for (const key of Object.keys(dep)) {
|
|
456
|
+
if (dep[key] === undefined || dep[key] === "") {
|
|
457
|
+
delete dep[key];
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
dep.line = command.line;
|
|
461
|
+
dep.madeAvailable = madeAvailable.has(dep.name.toLowerCase());
|
|
462
|
+
deps.push(dep);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return deps;
|
|
466
|
+
}
|