@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import {
|
|
4
4
|
sep as _sep,
|
|
5
5
|
basename,
|
|
@@ -13,11 +13,17 @@ import {
|
|
|
13
13
|
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
14
14
|
import { parse as _load } from "yaml";
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
DEBUG_MODE,
|
|
18
|
+
hasDangerousUnicode,
|
|
19
|
+
readEnvironmentVariable,
|
|
20
|
+
} from "../core/activity.js";
|
|
21
|
+
import { readRegularTextFile } from "../core/boundedFile.js";
|
|
17
22
|
import { recordDegradation } from "../core/buildLedger.js";
|
|
18
23
|
import { deferFailOnError } from "../core/deferredExit.js";
|
|
19
24
|
import { shouldFetchLicense } from "../core/env.js";
|
|
20
25
|
import {
|
|
26
|
+
getTmpDir,
|
|
21
27
|
safeExistsSync,
|
|
22
28
|
safeSpawnSync,
|
|
23
29
|
safeWriteSync,
|
|
@@ -25,6 +31,10 @@ import {
|
|
|
25
31
|
} from "../core/fs.js";
|
|
26
32
|
import { thoughtLog } from "../core/logger.js";
|
|
27
33
|
import { isWin } from "../core/paths.js";
|
|
34
|
+
import {
|
|
35
|
+
isProjectToolingAllowed,
|
|
36
|
+
reportProjectToolingSkipped,
|
|
37
|
+
} from "../core/projectTooling.js";
|
|
28
38
|
import { registerRunReset } from "../core/runState.js";
|
|
29
39
|
import { mesonWrapDB } from "../core/state.js";
|
|
30
40
|
import { isGitHubUrl } from "../core/urls.js";
|
|
@@ -43,7 +53,13 @@ import {
|
|
|
43
53
|
tryBuildPurl,
|
|
44
54
|
} from "../inventory/purl.js";
|
|
45
55
|
import { getLicenses, spdxLicenses } from "../inventory/spdx.js";
|
|
46
|
-
import {
|
|
56
|
+
import {
|
|
57
|
+
cmakeFetchDependencies,
|
|
58
|
+
cmakeSetVariables,
|
|
59
|
+
expandCmakeVariables,
|
|
60
|
+
parseCmakeCommands,
|
|
61
|
+
} from "../parsers/cmakeLists.js";
|
|
62
|
+
import { isValidIdnEmail } from "../parsers/email.js";
|
|
47
63
|
import { submodulePurlCoordinates } from "../parsers/gitmodules.js";
|
|
48
64
|
import { getDartMetadata } from "./ecosystems.js";
|
|
49
65
|
import { parsePkgJson } from "./parsers-js.js";
|
|
@@ -1555,11 +1571,146 @@ export function parseComposerJson(composerJsonFile) {
|
|
|
1555
1571
|
return { rootRequires, moduleParent };
|
|
1556
1572
|
}
|
|
1557
1573
|
|
|
1574
|
+
// Composer's platform packages stand for what the runtime provides, and none
|
|
1575
|
+
// is installable, so none has a purl or an edge in the dependency graph. These
|
|
1576
|
+
// are the ones that describe PHP: PHP and its build flavours, HHVM, and the
|
|
1577
|
+
// extensions and system libraries PHP was built with. composer,
|
|
1578
|
+
// composer-plugin-api and composer-runtime-api are platform packages too, but
|
|
1579
|
+
// they describe Composer, so they are left out.
|
|
1580
|
+
// https://github.com/composer/composer/blob/2.10.3/src/Composer/Repository/PlatformRepository.php#L39
|
|
1581
|
+
const COMPOSER_PLATFORM_NAMES = new Set([
|
|
1582
|
+
"php",
|
|
1583
|
+
"php-64bit",
|
|
1584
|
+
"php-ipv6",
|
|
1585
|
+
"php-zts",
|
|
1586
|
+
"php-debug",
|
|
1587
|
+
"hhvm",
|
|
1588
|
+
]);
|
|
1589
|
+
|
|
1590
|
+
function isComposerPlatformRequirement(name) {
|
|
1591
|
+
return (
|
|
1592
|
+
COMPOSER_PLATFORM_NAMES.has(name) ||
|
|
1593
|
+
name.startsWith("ext-") ||
|
|
1594
|
+
name.startsWith("lib-")
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// What the lock records about the platform is a constraint, or the version a
|
|
1599
|
+
// project told Composer to assume. It is a fact about the project's
|
|
1600
|
+
// requirements, never the version of anything that runs, so it is kept as a
|
|
1601
|
+
// property and never becomes a component.
|
|
1602
|
+
const COMPOSER_PLATFORM_SECTIONS = [
|
|
1603
|
+
["platform", "cdx:composer:platform:"],
|
|
1604
|
+
["platform-dev", "cdx:composer:platformDev:"],
|
|
1605
|
+
["platform-overrides", "cdx:composer:platformOverride:"],
|
|
1606
|
+
];
|
|
1607
|
+
const COMPOSER_REQUIRE_PREFIX = "cdx:composer:require:";
|
|
1608
|
+
const MAX_COMPOSER_PLATFORM_VALUE_LENGTH = 256;
|
|
1609
|
+
// A real lock names a few dozen platform packages per map at most; the cap
|
|
1610
|
+
// keeps a crafted one from turning each map into thousands of properties.
|
|
1611
|
+
const MAX_COMPOSER_PLATFORM_ENTRIES = 128;
|
|
1612
|
+
|
|
1613
|
+
function isComposerPlatformKey(key) {
|
|
1614
|
+
return /^[A-Za-z0-9._-]{1,100}$/.test(key);
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* The value a platform entry carries as a property: a constraint or version
|
|
1619
|
+
* string, or `false`, which `config.platform` uses to disable a package:
|
|
1620
|
+
* https://github.com/composer/composer/blob/2.10.3/src/Composer/Repository/PlatformRepository.php#L125
|
|
1621
|
+
*
|
|
1622
|
+
* @param {unknown} value value from the lock
|
|
1623
|
+
* @returns {string|undefined} property value, or undefined when it is not one
|
|
1624
|
+
*/
|
|
1625
|
+
function composerPlatformValue(value) {
|
|
1626
|
+
if (value === false) {
|
|
1627
|
+
return "false";
|
|
1628
|
+
}
|
|
1629
|
+
if (typeof value !== "string") {
|
|
1630
|
+
return undefined;
|
|
1631
|
+
}
|
|
1632
|
+
const trimmed = value.trim();
|
|
1633
|
+
if (
|
|
1634
|
+
!trimmed ||
|
|
1635
|
+
trimmed.length > MAX_COMPOSER_PLATFORM_VALUE_LENGTH ||
|
|
1636
|
+
hasDangerousUnicode(trimmed) ||
|
|
1637
|
+
/[\t\n\r]/.test(trimmed)
|
|
1638
|
+
) {
|
|
1639
|
+
return undefined;
|
|
1640
|
+
}
|
|
1641
|
+
return trimmed;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* Turn a map of platform entries into properties named `<prefix><name>`, in
|
|
1646
|
+
* name order, at most MAX_COMPOSER_PLATFORM_ENTRIES of them. Over the cap the
|
|
1647
|
+
* PHP and HHVM entries are kept first, since `ext-*` sorts before `php`. An
|
|
1648
|
+
* array (Composer writes an empty section as `[]`) or any other shape holds no
|
|
1649
|
+
* entries.
|
|
1650
|
+
*
|
|
1651
|
+
* @param {unknown} entries map of requirement name to constraint
|
|
1652
|
+
* @param {string} prefix property name prefix
|
|
1653
|
+
* @param {function(string): boolean} [keep] which names to keep
|
|
1654
|
+
* @returns {{name: string, value: string}[]} properties
|
|
1655
|
+
*/
|
|
1656
|
+
function composerPlatformProperties(entries, prefix, keep = () => true) {
|
|
1657
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries)) {
|
|
1658
|
+
return [];
|
|
1659
|
+
}
|
|
1660
|
+
const kept = [];
|
|
1661
|
+
for (const key of Object.keys(entries).sort()) {
|
|
1662
|
+
const value = composerPlatformValue(entries[key]);
|
|
1663
|
+
if (value !== undefined && isComposerPlatformKey(key) && keep(key)) {
|
|
1664
|
+
kept.push({ key, value });
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
let selected = kept;
|
|
1668
|
+
if (kept.length > MAX_COMPOSER_PLATFORM_ENTRIES) {
|
|
1669
|
+
const runtimes = kept.filter(({ key }) => COMPOSER_PLATFORM_NAMES.has(key));
|
|
1670
|
+
const others = kept
|
|
1671
|
+
.filter(({ key }) => !COMPOSER_PLATFORM_NAMES.has(key))
|
|
1672
|
+
.slice(0, MAX_COMPOSER_PLATFORM_ENTRIES - runtimes.length);
|
|
1673
|
+
const chosen = new Set([...runtimes, ...others]);
|
|
1674
|
+
selected = kept.filter((entry) => chosen.has(entry));
|
|
1675
|
+
}
|
|
1676
|
+
return selected.map(({ key, value }) => ({ name: `${prefix}${key}`, value }));
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/**
|
|
1680
|
+
* A CycloneDX `organizationalContact` from an author record in package
|
|
1681
|
+
* metadata. The schema asserts `idn-email` on the address, so one that is not
|
|
1682
|
+
* a valid address is dropped rather than failing BOM validation; an author with
|
|
1683
|
+
* neither a name nor a valid address yields nothing.
|
|
1684
|
+
*
|
|
1685
|
+
* @param {{name?: string, email?: unknown}} author Author as the manifest has it
|
|
1686
|
+
* @returns {Array<{name?: string, email?: string}>} Zero or one contact
|
|
1687
|
+
*/
|
|
1688
|
+
function contactFromAuthor(author) {
|
|
1689
|
+
const contact = {};
|
|
1690
|
+
if (typeof author?.name === "string" && author.name.trim()) {
|
|
1691
|
+
contact.name = author.name;
|
|
1692
|
+
}
|
|
1693
|
+
const email = typeof author?.email === "string" ? author.email.trim() : "";
|
|
1694
|
+
if (isValidIdnEmail(email)) {
|
|
1695
|
+
contact.email = email;
|
|
1696
|
+
}
|
|
1697
|
+
return Object.keys(contact).length ? [contact] : [];
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1558
1700
|
/**
|
|
1559
1701
|
* Parse composer lock file
|
|
1560
1702
|
*
|
|
1561
1703
|
* @param {string} pkgLockFile composer.lock file
|
|
1562
1704
|
* @param {array} rootRequires require section from composer.json
|
|
1705
|
+
* @returns {{pkgList: Object[], dependenciesList: Object[], rootList: Object[], platformProperties: {name: string, value: string}[]}|Array}
|
|
1706
|
+
* The locked packages, the dependency edges between them, the packages the
|
|
1707
|
+
* root requires, and the lock's `platform`, `platform-dev` and
|
|
1708
|
+
* `platform-overrides` entries as `cdx:composer:*` properties. The platform
|
|
1709
|
+
* entries are constraints and assumed versions, not the PHP that runs; the
|
|
1710
|
+
* caller puts them on the module component. Every locked package carries its
|
|
1711
|
+
* own `php`, `hhvm`, `ext-*` and `lib-*` requirements as
|
|
1712
|
+
* `cdx:composer:require:<name>` properties. A lock that is not valid JSON
|
|
1713
|
+
* gives an empty array.
|
|
1563
1714
|
*/
|
|
1564
1715
|
export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
1565
1716
|
const pkgList = [];
|
|
@@ -1568,16 +1719,12 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1568
1719
|
const pkgNamePurlMap = {};
|
|
1569
1720
|
const rootList = [];
|
|
1570
1721
|
const rootRequiresMap = {};
|
|
1722
|
+
let platformProperties = [];
|
|
1571
1723
|
if (rootRequires) {
|
|
1572
1724
|
for (const rr of Object.keys(rootRequires)) {
|
|
1573
1725
|
// Skip platform requirements (php, hhvm, ext-*, lib-*) — they are never
|
|
1574
1726
|
// Composer package names and must not be used to identify root packages.
|
|
1575
|
-
if (
|
|
1576
|
-
rr === "php" ||
|
|
1577
|
-
rr === "hhvm" ||
|
|
1578
|
-
rr.startsWith("ext-") ||
|
|
1579
|
-
rr.startsWith("lib-")
|
|
1580
|
-
) {
|
|
1727
|
+
if (isComposerPlatformRequirement(rr)) {
|
|
1581
1728
|
continue;
|
|
1582
1729
|
}
|
|
1583
1730
|
rootRequiresMap[rr] = true;
|
|
@@ -1592,6 +1739,11 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1592
1739
|
return [];
|
|
1593
1740
|
}
|
|
1594
1741
|
if (lockData) {
|
|
1742
|
+
for (const [section, prefix] of COMPOSER_PLATFORM_SECTIONS) {
|
|
1743
|
+
platformProperties = platformProperties.concat(
|
|
1744
|
+
composerPlatformProperties(lockData[section], prefix),
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1595
1747
|
const packages = {};
|
|
1596
1748
|
if (lockData["packages"]) {
|
|
1597
1749
|
packages["required"] = lockData["packages"];
|
|
@@ -1664,9 +1816,10 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1664
1816
|
apkg.distribution = { url: distUrl };
|
|
1665
1817
|
}
|
|
1666
1818
|
if (pkg?.authors?.length) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1819
|
+
const authors = pkg.authors.flatMap(contactFromAuthor);
|
|
1820
|
+
if (authors.length) {
|
|
1821
|
+
apkg.authors = authors;
|
|
1822
|
+
}
|
|
1670
1823
|
}
|
|
1671
1824
|
if (pkg?.keywords?.length) {
|
|
1672
1825
|
apkg.tags = pkg.keywords;
|
|
@@ -1687,6 +1840,13 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1687
1840
|
});
|
|
1688
1841
|
}
|
|
1689
1842
|
}
|
|
1843
|
+
apkg.properties.push(
|
|
1844
|
+
...composerPlatformProperties(
|
|
1845
|
+
pkg.require,
|
|
1846
|
+
COMPOSER_REQUIRE_PREFIX,
|
|
1847
|
+
isComposerPlatformRequirement,
|
|
1848
|
+
),
|
|
1849
|
+
);
|
|
1690
1850
|
pkgList.push(apkg);
|
|
1691
1851
|
dependenciesMap[purl] = new Set();
|
|
1692
1852
|
pkgNamePurlMap[pkg.name] = purl;
|
|
@@ -1726,6 +1886,7 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1726
1886
|
pkgList,
|
|
1727
1887
|
dependenciesList,
|
|
1728
1888
|
rootList,
|
|
1889
|
+
platformProperties,
|
|
1729
1890
|
};
|
|
1730
1891
|
}
|
|
1731
1892
|
|
|
@@ -2629,6 +2790,130 @@ export function cmakeFetchDependencyComponent(dep, srcFile) {
|
|
|
2629
2790
|
return component;
|
|
2630
2791
|
}
|
|
2631
2792
|
|
|
2793
|
+
const MAX_MESON_WRAPS = 256;
|
|
2794
|
+
const MAX_MESON_WRAP_BYTES = 64 * 1024;
|
|
2795
|
+
|
|
2796
|
+
function upstreamVersionOf(text) {
|
|
2797
|
+
if (typeof text !== "string") {
|
|
2798
|
+
return undefined;
|
|
2799
|
+
}
|
|
2800
|
+
const value = text.trim().replace(/^v(?=\d)/, "");
|
|
2801
|
+
return /^\d+(\.\d+)*([-+._~][0-9A-Za-z.]+)?$/.test(value) ? value : undefined;
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
// `brotli-1.1.0`, `brotli-1.1.0.tar.gz`: the version after the last `-` that
|
|
2805
|
+
// starts a number.
|
|
2806
|
+
function versionFromArchiveName(name) {
|
|
2807
|
+
if (typeof name !== "string") {
|
|
2808
|
+
return undefined;
|
|
2809
|
+
}
|
|
2810
|
+
const stem = name.replace(/\.(tar\.gz|tar\.xz|tar\.bz2|tgz|zip|tar)$/i, "");
|
|
2811
|
+
const dash = stem.search(/-\d[^-]*$/);
|
|
2812
|
+
return dash === -1 ? undefined : upstreamVersionOf(stem.slice(dash + 1));
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
/**
|
|
2816
|
+
* Read the wrap files of a meson project's subprojects directory: which
|
|
2817
|
+
* dependency names each wrap provides, and the version it pins.
|
|
2818
|
+
* https://mesonbuild.com/Wrap-dependency-system-manual.html
|
|
2819
|
+
*
|
|
2820
|
+
* A WrapDB wrap records `wrapdb_version = <upstream>-<wrap revision>`; any wrap
|
|
2821
|
+
* names its unpacked `directory` and its archive, which carry the upstream
|
|
2822
|
+
* version; a git wrap pins a `revision` that is often a version tag.
|
|
2823
|
+
*
|
|
2824
|
+
* @param {string} subprojectsDir path of the subprojects directory
|
|
2825
|
+
* @returns {Map<string, {wrap: string, version: string, source: string, revision?: string}>}
|
|
2826
|
+
* by provided dependency name, lower-case
|
|
2827
|
+
*/
|
|
2828
|
+
export function readMesonWraps(subprojectsDir) {
|
|
2829
|
+
const provided = new Map();
|
|
2830
|
+
let wrapFiles = [];
|
|
2831
|
+
try {
|
|
2832
|
+
wrapFiles = readdirSync(subprojectsDir)
|
|
2833
|
+
.filter((name) => name.endsWith(".wrap"))
|
|
2834
|
+
.sort()
|
|
2835
|
+
.slice(0, MAX_MESON_WRAPS);
|
|
2836
|
+
} catch {
|
|
2837
|
+
return provided;
|
|
2838
|
+
}
|
|
2839
|
+
for (const wrapFile of wrapFiles) {
|
|
2840
|
+
// A regular file only: a FIFO named *.wrap would block a plain read.
|
|
2841
|
+
const text = readRegularTextFile(
|
|
2842
|
+
join(subprojectsDir, wrapFile),
|
|
2843
|
+
MAX_MESON_WRAP_BYTES,
|
|
2844
|
+
);
|
|
2845
|
+
if (text === undefined) {
|
|
2846
|
+
continue;
|
|
2847
|
+
}
|
|
2848
|
+
const sections = {};
|
|
2849
|
+
let section = "";
|
|
2850
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
2851
|
+
const line = rawLine.trim();
|
|
2852
|
+
if (!line || line.startsWith("#") || line.startsWith(";")) {
|
|
2853
|
+
continue;
|
|
2854
|
+
}
|
|
2855
|
+
if (line.startsWith("[") && line.endsWith("]")) {
|
|
2856
|
+
section = line.slice(1, -1).trim();
|
|
2857
|
+
sections[section] = sections[section] || {};
|
|
2858
|
+
continue;
|
|
2859
|
+
}
|
|
2860
|
+
const equals = line.indexOf("=");
|
|
2861
|
+
if (equals <= 0 || !section) {
|
|
2862
|
+
continue;
|
|
2863
|
+
}
|
|
2864
|
+
sections[section][line.slice(0, equals).trim()] = line
|
|
2865
|
+
.slice(equals + 1)
|
|
2866
|
+
.trim();
|
|
2867
|
+
}
|
|
2868
|
+
const body = sections["wrap-file"] || sections["wrap-git"] || {};
|
|
2869
|
+
let version;
|
|
2870
|
+
let source;
|
|
2871
|
+
let revision;
|
|
2872
|
+
if (body.wrapdb_version) {
|
|
2873
|
+
const dash = body.wrapdb_version.lastIndexOf("-");
|
|
2874
|
+
version = upstreamVersionOf(
|
|
2875
|
+
dash > 0 ? body.wrapdb_version.slice(0, dash) : body.wrapdb_version,
|
|
2876
|
+
);
|
|
2877
|
+
revision = dash > 0 ? body.wrapdb_version.slice(dash + 1) : undefined;
|
|
2878
|
+
source = "wrapdb_version";
|
|
2879
|
+
}
|
|
2880
|
+
if (!version && sections["wrap-file"]) {
|
|
2881
|
+
version =
|
|
2882
|
+
versionFromArchiveName(body.directory) ||
|
|
2883
|
+
versionFromArchiveName(body.source_filename);
|
|
2884
|
+
source = version ? "directory" : undefined;
|
|
2885
|
+
}
|
|
2886
|
+
if (!version && sections["wrap-git"]) {
|
|
2887
|
+
version = upstreamVersionOf(body.revision);
|
|
2888
|
+
source = version ? "revision" : undefined;
|
|
2889
|
+
}
|
|
2890
|
+
if (!version) {
|
|
2891
|
+
continue;
|
|
2892
|
+
}
|
|
2893
|
+
const wrap = wrapFile.slice(0, -".wrap".length);
|
|
2894
|
+
const entry = { wrap, version, source, ...(revision ? { revision } : {}) };
|
|
2895
|
+
const names = new Set([wrap.toLowerCase()]);
|
|
2896
|
+
const provide = sections.provide || {};
|
|
2897
|
+
for (const [key, value] of Object.entries(provide)) {
|
|
2898
|
+
if (key === "dependency_names" || key === "program_names") {
|
|
2899
|
+
for (const name of value.split(",")) {
|
|
2900
|
+
if (name.trim()) {
|
|
2901
|
+
names.add(name.trim().toLowerCase());
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
} else {
|
|
2905
|
+
names.add(key.toLowerCase());
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
for (const name of names) {
|
|
2909
|
+
if (!provided.has(name)) {
|
|
2910
|
+
provided.set(name, entry);
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
return provided;
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2632
2917
|
/**
|
|
2633
2918
|
* Parse a CMake-like build file (CMakeLists.txt, meson.build, etc.) and extract the
|
|
2634
2919
|
* parent component and list of dependency packages.
|
|
@@ -2646,6 +2931,11 @@ export function cmakeFetchDependencyComponent(dep, srcFile) {
|
|
|
2646
2931
|
*/
|
|
2647
2932
|
export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
|
|
2648
2933
|
const rawData = readFileSync(cmakeListFile, { encoding: "utf-8" });
|
|
2934
|
+
// The wraps a meson project ships pin the versions of the subprojects its
|
|
2935
|
+
// dependency() calls fall back to.
|
|
2936
|
+
const mesonWraps = basename(cmakeListFile).startsWith("meson")
|
|
2937
|
+
? readMesonWraps(join(dirname(cmakeListFile), "subprojects"))
|
|
2938
|
+
: new Map();
|
|
2649
2939
|
let cmakeListData = rawData;
|
|
2650
2940
|
const pkgList = [];
|
|
2651
2941
|
const pkgAddedMap = {};
|
|
@@ -2653,6 +2943,25 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
|
|
|
2653
2943
|
const versionsMap = {};
|
|
2654
2944
|
let parentComponent = {};
|
|
2655
2945
|
const templateValues = {};
|
|
2946
|
+
// The parent component is built the same way whether its name and version
|
|
2947
|
+
// came from the parsed project() command or from the line loop below.
|
|
2948
|
+
const toParentComponent = (parentName, parentVersion) => {
|
|
2949
|
+
const component = {
|
|
2950
|
+
group: options.projectGroup || "",
|
|
2951
|
+
name: parentName,
|
|
2952
|
+
version: parentVersion || options.projectVersion || "",
|
|
2953
|
+
type: "application",
|
|
2954
|
+
};
|
|
2955
|
+
component["purl"] = build({
|
|
2956
|
+
type: pkgType,
|
|
2957
|
+
namespace: component.group || null,
|
|
2958
|
+
name: component.name,
|
|
2959
|
+
version: component.version || null,
|
|
2960
|
+
subpath: null,
|
|
2961
|
+
});
|
|
2962
|
+
component["bom-ref"] = decodeURIComponent(component["purl"]);
|
|
2963
|
+
return component;
|
|
2964
|
+
};
|
|
2656
2965
|
// A dependency fetched at configure time is the same package a find_package()
|
|
2657
2966
|
// of the same name asks for: the fetched component, with its source and pin,
|
|
2658
2967
|
// stands for both.
|
|
@@ -2668,6 +2977,38 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
|
|
|
2668
2977
|
fetchedNames.add(dep.name.toLowerCase());
|
|
2669
2978
|
pkgList.push(component);
|
|
2670
2979
|
}
|
|
2980
|
+
// A project() call may span several lines, which the line loop below
|
|
2981
|
+
// cannot rejoin, so the parent name and version are read from the parsed
|
|
2982
|
+
// commands instead. The first argument is the project name and the
|
|
2983
|
+
// VERSION keyword is followed by the version.
|
|
2984
|
+
const commands = parseCmakeCommands(rawData);
|
|
2985
|
+
const variables = cmakeSetVariables(commands);
|
|
2986
|
+
const projectCommand = commands.find(
|
|
2987
|
+
(command) => command.name === "project",
|
|
2988
|
+
);
|
|
2989
|
+
if (projectCommand?.args.length) {
|
|
2990
|
+
const args = projectCommand.args.map((arg) =>
|
|
2991
|
+
expandCmakeVariables(arg, variables),
|
|
2992
|
+
);
|
|
2993
|
+
const parentName = args[0].replace(":", "").trim();
|
|
2994
|
+
let parentVersion;
|
|
2995
|
+
const versionIndex = args.findIndex(
|
|
2996
|
+
(arg) => arg.toLowerCase() === "version",
|
|
2997
|
+
);
|
|
2998
|
+
if (versionIndex > -1 && args.length > versionIndex + 1) {
|
|
2999
|
+
parentVersion = args[versionIndex + 1];
|
|
3000
|
+
}
|
|
3001
|
+
// A `${VAR}` that no `set()` in this file defines stays literal. It is
|
|
3002
|
+
// not a version, and percent-encoding it into a purl publishes the
|
|
3003
|
+
// variable name as the version, so the field is left empty instead.
|
|
3004
|
+
// CMakeCache.txt carries the evaluated value when a build tree exists.
|
|
3005
|
+
if (parentVersion?.includes("$")) {
|
|
3006
|
+
parentVersion = undefined;
|
|
3007
|
+
}
|
|
3008
|
+
if (parentName.length && !parentName.includes("$")) {
|
|
3009
|
+
parentComponent = toParentComponent(parentName, parentVersion);
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
2671
3012
|
}
|
|
2672
3013
|
cmakeListData = cmakeListData
|
|
2673
3014
|
.replace(/^ {2}/g, "")
|
|
@@ -2728,23 +3069,7 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
|
|
|
2728
3069
|
parentVersion = undefined;
|
|
2729
3070
|
}
|
|
2730
3071
|
if (parentName?.length && !parentName.includes("$")) {
|
|
2731
|
-
parentComponent =
|
|
2732
|
-
group: options.projectGroup || "",
|
|
2733
|
-
name: parentName,
|
|
2734
|
-
version: parentVersion || options.projectVersion || "",
|
|
2735
|
-
type: "application",
|
|
2736
|
-
};
|
|
2737
|
-
const safePath = path ? path.replace(/^\/+/, "") : null;
|
|
2738
|
-
parentComponent["purl"] = build({
|
|
2739
|
-
type: pkgType,
|
|
2740
|
-
namespace: parentComponent.group || null,
|
|
2741
|
-
name: parentComponent.name,
|
|
2742
|
-
version: parentComponent.version || null,
|
|
2743
|
-
subpath: safePath,
|
|
2744
|
-
});
|
|
2745
|
-
parentComponent["bom-ref"] = decodeURIComponent(
|
|
2746
|
-
parentComponent["purl"],
|
|
2747
|
-
);
|
|
3072
|
+
parentComponent = toParentComponent(parentName, parentVersion);
|
|
2748
3073
|
}
|
|
2749
3074
|
} else if (CMAKE_FIND_COMMANDS.has(command)) {
|
|
2750
3075
|
let tmpB = commandArgs.split(")")[0].split(" ");
|
|
@@ -2886,7 +3211,26 @@ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
|
|
|
2886
3211
|
// wrap match has since replaced, so the scraped name is the fallback.
|
|
2887
3212
|
const versionSpecifier =
|
|
2888
3213
|
versionSpecifiersMap[n] || versionSpecifiersMap[scrapedName];
|
|
2889
|
-
|
|
3214
|
+
let version = versionsMap[n] || versionsMap[scrapedName] || "";
|
|
3215
|
+
const localWrap =
|
|
3216
|
+
mesonWraps.get(scrapedName.toLowerCase()) ||
|
|
3217
|
+
mesonWraps.get(n.toLowerCase());
|
|
3218
|
+
if (localWrap && !version) {
|
|
3219
|
+
version = localWrap.version;
|
|
3220
|
+
props.push({ name: "cdx:meson:wrap", value: localWrap.wrap });
|
|
3221
|
+
props.push({
|
|
3222
|
+
name: "cdx:meson:wrap:versionSource",
|
|
3223
|
+
value: localWrap.source,
|
|
3224
|
+
});
|
|
3225
|
+
if (localWrap.revision) {
|
|
3226
|
+
props.push({
|
|
3227
|
+
name: "cdx:meson:wrap:revision",
|
|
3228
|
+
value: localWrap.revision,
|
|
3229
|
+
});
|
|
3230
|
+
}
|
|
3231
|
+
// A pinned wrap is a stronger identity than a name match alone.
|
|
3232
|
+
confidence = Math.max(confidence, 0.7);
|
|
3233
|
+
}
|
|
2890
3234
|
if (versionSpecifier) {
|
|
2891
3235
|
props.push({
|
|
2892
3236
|
name: "cdx:build:versionSpecifiers",
|
|
@@ -3608,6 +3952,12 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3608
3952
|
value: podspecLocation,
|
|
3609
3953
|
});
|
|
3610
3954
|
}
|
|
3955
|
+
// A Ruby podspec is a program that only `pod` can read, and it comes from
|
|
3956
|
+
// the scanned project or a repository its lockfile names. Without
|
|
3957
|
+
// permission to run the project's tooling, only JSON podspecs are read,
|
|
3958
|
+
// directly and without `pod`.
|
|
3959
|
+
const toolingAllowed = isProjectToolingAllowed(options);
|
|
3960
|
+
const isRemote = podspecLocation.startsWith("http");
|
|
3611
3961
|
let replacements = [];
|
|
3612
3962
|
if (
|
|
3613
3963
|
podspecLocation.endsWith(".podspec") &&
|
|
@@ -3624,34 +3974,50 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3624
3974
|
"COCOA_PODSPEC_JSON_REPLACEMENTS",
|
|
3625
3975
|
).split(";");
|
|
3626
3976
|
}
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
httpResult =
|
|
3634
|
-
prefetchedResponse(cocoaPodspecPrefetch, candidate) ||
|
|
3635
|
-
(await gatedGet(candidate, { responseType: "text" }));
|
|
3636
|
-
podspecLocation = candidate;
|
|
3637
|
-
} catch (_err) {
|
|
3638
|
-
continue;
|
|
3639
|
-
}
|
|
3640
|
-
component.properties.find(
|
|
3641
|
-
({ name }) => name === "cdx:pods:podspecLocation",
|
|
3642
|
-
).value = podspecLocation;
|
|
3643
|
-
podspecLocation = `${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`;
|
|
3644
|
-
podspecContent = httpResult.body;
|
|
3645
|
-
break;
|
|
3977
|
+
let podspecContent;
|
|
3978
|
+
if (isRemote) {
|
|
3979
|
+
let httpResult;
|
|
3980
|
+
for (const candidate of podspecCandidates(podspecLocation)) {
|
|
3981
|
+
if (!toolingAllowed && !candidate.endsWith(".json")) {
|
|
3982
|
+
continue;
|
|
3646
3983
|
}
|
|
3647
|
-
|
|
3648
|
-
|
|
3984
|
+
try {
|
|
3985
|
+
httpResult =
|
|
3986
|
+
prefetchedResponse(cocoaPodspecPrefetch, candidate) ||
|
|
3987
|
+
(await gatedGet(candidate, { responseType: "text" }));
|
|
3988
|
+
podspecLocation = candidate;
|
|
3989
|
+
} catch (_err) {
|
|
3990
|
+
continue;
|
|
3991
|
+
}
|
|
3992
|
+
component.properties.find(
|
|
3993
|
+
({ name }) => name === "cdx:pods:podspecLocation",
|
|
3994
|
+
).value = podspecLocation;
|
|
3995
|
+
podspecContent = httpResult.body;
|
|
3996
|
+
break;
|
|
3649
3997
|
}
|
|
3650
3998
|
// A location none of the candidates served contributes nothing: the
|
|
3651
3999
|
// component keeps what the lockfile already said about the pod.
|
|
3652
4000
|
if (podspecContent === undefined) {
|
|
3653
4001
|
return;
|
|
3654
4002
|
}
|
|
4003
|
+
} else if (!toolingAllowed && !podspecLocation.endsWith(".json")) {
|
|
4004
|
+
reportProjectToolingSkipped(
|
|
4005
|
+
options,
|
|
4006
|
+
"cocoa.podspec.tooling-disabled",
|
|
4007
|
+
"Skipping 'pod ipc spec' for Ruby podspecs since dependency installation is disabled. Their pods keep the details from Podfile.lock.",
|
|
4008
|
+
{
|
|
4009
|
+
ecosystem: "cocoa",
|
|
4010
|
+
tool: "pod",
|
|
4011
|
+
impact: "licenses",
|
|
4012
|
+
detail:
|
|
4013
|
+
"Dependency installation is disabled, so Ruby podspecs were not evaluated and their pods lack license and author details.",
|
|
4014
|
+
},
|
|
4015
|
+
);
|
|
4016
|
+
return;
|
|
4017
|
+
} else if (replacements.length || !toolingAllowed) {
|
|
4018
|
+
podspecContent = readFileSync(podspecLocation, "utf-8");
|
|
4019
|
+
}
|
|
4020
|
+
if (podspecContent !== undefined) {
|
|
3655
4021
|
for (const replacement of replacements) {
|
|
3656
4022
|
const replacementPair = replacement.split("=");
|
|
3657
4023
|
let match = replacementPair[0].replaceAll("<NEWLINE>", "\n");
|
|
@@ -3661,18 +4027,28 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3661
4027
|
const repl = replacementPair[1].replaceAll("<NEWLINE>", "\n");
|
|
3662
4028
|
podspecContent = podspecContent.replaceAll(match, repl);
|
|
3663
4029
|
}
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
4030
|
+
}
|
|
4031
|
+
if (!toolingAllowed) {
|
|
4032
|
+
// Only JSON podspecs reach this point, and they need no `pod`
|
|
4033
|
+
result = { stdout: podspecContent };
|
|
4034
|
+
} else {
|
|
4035
|
+
if (podspecContent !== undefined) {
|
|
4036
|
+
// A downloaded podspec goes to the temporary directory; a local one
|
|
4037
|
+
// with replacements stays beside the original, whose relative paths
|
|
4038
|
+
// its Ruby may read.
|
|
4039
|
+
podspecLocation = join(
|
|
4040
|
+
isRemote ? getTmpDir() : dirname(podspecLocation),
|
|
4041
|
+
`${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`,
|
|
4042
|
+
);
|
|
4043
|
+
safeWriteSync(podspecLocation, podspecContent);
|
|
4044
|
+
temporaryFiles.add(podspecLocation);
|
|
4045
|
+
}
|
|
4046
|
+
result = executePodCommand(
|
|
4047
|
+
["ipc", "spec", "--silent", podspecLocation],
|
|
4048
|
+
undefined,
|
|
4049
|
+
options,
|
|
3667
4050
|
);
|
|
3668
|
-
safeWriteSync(podspecLocation, podspecContent);
|
|
3669
|
-
temporaryFiles.add(podspecLocation);
|
|
3670
4051
|
}
|
|
3671
|
-
result = executePodCommand(
|
|
3672
|
-
["ipc", "spec", "--silent", podspecLocation],
|
|
3673
|
-
undefined,
|
|
3674
|
-
options,
|
|
3675
|
-
);
|
|
3676
4052
|
} else {
|
|
3677
4053
|
let dependencyName = dependency.name;
|
|
3678
4054
|
if (dependencyName.includes("/")) {
|
|
@@ -3737,9 +4113,7 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3737
4113
|
component.authors = [];
|
|
3738
4114
|
if (podspec.authors.constructor === Object) {
|
|
3739
4115
|
Object.entries(podspec.authors).forEach(([name, email]) => {
|
|
3740
|
-
|
|
3741
|
-
? component.authors.push({ name, email })
|
|
3742
|
-
: component.authors.push({ name });
|
|
4116
|
+
component.authors.push(...contactFromAuthor({ name, email }));
|
|
3743
4117
|
});
|
|
3744
4118
|
} else if (podspec.authors.constructor === Array) {
|
|
3745
4119
|
podspec.authors.forEach((name) => {
|