@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,4 +1,5 @@
|
|
|
1
1
|
import { getDistroInfo } from "./osinfo.js";
|
|
2
|
+
import { buildOsPackagePurl, osPackageVersion } from "./osPackagePurl.js";
|
|
2
3
|
import { tryBuildPurl } from "./purl.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -62,6 +63,8 @@ export function deriveOsQueryPublisher(res) {
|
|
|
62
63
|
res.creator ||
|
|
63
64
|
res.manufacturer ||
|
|
64
65
|
res.provider ||
|
|
66
|
+
res.packager ||
|
|
67
|
+
res.developer_name ||
|
|
65
68
|
"";
|
|
66
69
|
return publisher === "null" ? "" : publisher;
|
|
67
70
|
}
|
|
@@ -174,7 +177,10 @@ export function shouldCreateOsQueryPurl(componentType) {
|
|
|
174
177
|
|
|
175
178
|
// Purl types whose type rules require a namespace. For OS package types the
|
|
176
179
|
// namespace is the distro vendor (`pkg:deb/debian/...`, `pkg:rpm/redhat/...`),
|
|
177
|
-
// which osquery does not report but /etc/os-release does.
|
|
180
|
+
// which osquery does not report but /etc/os-release does. deb, rpm and apk rows
|
|
181
|
+
// are named by createOsQueryPackageIdentity; this covers the other types that
|
|
182
|
+
// need a vendor (portage's ebuild) and a caller that asks for one of the three
|
|
183
|
+
// directly.
|
|
178
184
|
const NAMESPACE_REQUIRED_TYPES = new Set([
|
|
179
185
|
"deb",
|
|
180
186
|
"rpm",
|
|
@@ -216,10 +222,9 @@ export function createOsQueryPurl(
|
|
|
216
222
|
if (type === "swid" && !finalQualifiers.tag_id) {
|
|
217
223
|
finalQualifiers.tag_id = name;
|
|
218
224
|
}
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
// purl instead of losing one.
|
|
225
|
+
// Rows with no group whose purl type requires a namespace get the vendor from
|
|
226
|
+
// /etc/os-release, the way osPackageResolver does, so they keep a valid purl
|
|
227
|
+
// instead of losing one.
|
|
223
228
|
let namespace = group || null;
|
|
224
229
|
if (!namespace && NAMESPACE_REQUIRED_TYPES.has(type)) {
|
|
225
230
|
namespace = getDistroInfo().namespace || null;
|
|
@@ -237,6 +242,110 @@ export function createOsQueryPurl(
|
|
|
237
242
|
});
|
|
238
243
|
}
|
|
239
244
|
|
|
245
|
+
/**
|
|
246
|
+
* The version and purl of a row from an OS package table: `deb_packages`,
|
|
247
|
+
* `rpm_packages`, `pacman_packages`, `homebrew_packages`, `macports_packages`,
|
|
248
|
+
* `snap_packages`, `flatpak_packages` or the rows of the apk database.
|
|
249
|
+
*
|
|
250
|
+
* Such a row is named the way the package manager's own scan names it: deb,
|
|
251
|
+
* rpm and apk with the vendor and the `arch`, `distro`, `distro_name` and
|
|
252
|
+
* `epoch` qualifiers of the container scan, an rpm version joined to its
|
|
253
|
+
* release, a MacPorts version joined to its revision, a flatpak by its
|
|
254
|
+
* application ID with the `arch` and `branch` of its ref. A row's `path`,
|
|
255
|
+
* `admindir` or source file is not a path inside the package, so none of them
|
|
256
|
+
* becomes a subpath.
|
|
257
|
+
*
|
|
258
|
+
* @param {string} purlType One of OS_PACKAGE_PURL_TYPES
|
|
259
|
+
* @param {Object} res osquery row
|
|
260
|
+
* @param {string} name Component name derived from the row
|
|
261
|
+
* @param {Object} [distro] What getDistroInfo() reports; the host's by default
|
|
262
|
+
* @returns {{version: string, purl: string|null}} The component version and the purl, which is null when the row cannot be expressed as one
|
|
263
|
+
*/
|
|
264
|
+
export function createOsQueryPackageIdentity(purlType, res, name, distro) {
|
|
265
|
+
const pkg = {
|
|
266
|
+
type: purlType,
|
|
267
|
+
name,
|
|
268
|
+
version: res.version,
|
|
269
|
+
arch: res.arch,
|
|
270
|
+
};
|
|
271
|
+
if (purlType === "rpm") {
|
|
272
|
+
pkg.release = res.release;
|
|
273
|
+
pkg.epoch = res.epoch;
|
|
274
|
+
} else if (purlType === "macports") {
|
|
275
|
+
pkg.revision = res.revision;
|
|
276
|
+
} else if (purlType === "flatpak") {
|
|
277
|
+
pkg.branch = res.branch;
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
version: osPackageVersion(pkg),
|
|
281
|
+
purl: buildOsPackagePurl(pkg, distro),
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Columns of an OS package table that are named or dropped on their way to a
|
|
286
|
+
// property. A bare `source` is the source package of a deb, the source rpm file
|
|
287
|
+
// of an rpm and the pkgbase of a pacman package, and `type` says whether a
|
|
288
|
+
// Homebrew entry is a formula or a cask, a snap an app or a base, a flatpak an
|
|
289
|
+
// app or a runtime, so each takes the name that says which. The dpkg
|
|
290
|
+
// administrative directory and the pacman database path are the same for every
|
|
291
|
+
// package and say nothing about one.
|
|
292
|
+
const OS_PACKAGE_PROPERTY_NAMES = {
|
|
293
|
+
deb: { source: "cdx:deb:source", admindir: undefined },
|
|
294
|
+
rpm: { source: "cdx:rpm:sourceRpm" },
|
|
295
|
+
apk: { origin: "cdx:apk:origin" },
|
|
296
|
+
alpm: { source: "cdx:alpm:pkgbase", dbpath: undefined },
|
|
297
|
+
brew: { type: "cdx:homebrew:type" },
|
|
298
|
+
macports: {
|
|
299
|
+
variants: "cdx:macports:variants",
|
|
300
|
+
requested: "cdx:macports:requested",
|
|
301
|
+
},
|
|
302
|
+
snap: {
|
|
303
|
+
type: "cdx:snap:type",
|
|
304
|
+
revision: "cdx:snap:revision",
|
|
305
|
+
channel: "cdx:snap:channel",
|
|
306
|
+
confinement: "cdx:snap:confinement",
|
|
307
|
+
},
|
|
308
|
+
flatpak: {
|
|
309
|
+
display_name: "cdx:flatpak:displayName",
|
|
310
|
+
commit_hash: "cdx:flatpak:commit",
|
|
311
|
+
origin: "cdx:flatpak:origin",
|
|
312
|
+
installation: "cdx:flatpak:installation",
|
|
313
|
+
type: "cdx:flatpak:type",
|
|
314
|
+
runtime: "cdx:flatpak:runtime",
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The columns of an OS package row that give its licence and its home page.
|
|
320
|
+
* They become the component's `licenses` and a `website` reference rather than
|
|
321
|
+
* properties. `separator` splits a licence column that lists several: pacman
|
|
322
|
+
* keeps one licence per entry, which osquery joins with commas.
|
|
323
|
+
*/
|
|
324
|
+
export const OS_PACKAGE_LICENSE_COLUMNS = {
|
|
325
|
+
alpm: { license: "licenses", separator: ",", website: "url" },
|
|
326
|
+
snap: { license: "license" },
|
|
327
|
+
flatpak: { license: "license", website: "homepage" },
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* The property name an OS package table column is emitted under.
|
|
332
|
+
*
|
|
333
|
+
* @param {string|undefined} purlType Purl type of the table
|
|
334
|
+
* @param {string} column Column name
|
|
335
|
+
* @returns {string|undefined} The property name, or undefined when the column is not emitted
|
|
336
|
+
*/
|
|
337
|
+
export function osPackagePropertyName(purlType, column) {
|
|
338
|
+
const fields = OS_PACKAGE_LICENSE_COLUMNS[purlType];
|
|
339
|
+
if (fields && (column === fields.license || column === fields.website)) {
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
const names = OS_PACKAGE_PROPERTY_NAMES[purlType];
|
|
343
|
+
if (names && Object.hasOwn(names, column)) {
|
|
344
|
+
return names[column];
|
|
345
|
+
}
|
|
346
|
+
return column;
|
|
347
|
+
}
|
|
348
|
+
|
|
240
349
|
function _deriveCertificateFormat(certificateFileExtension) {
|
|
241
350
|
switch ((certificateFileExtension || "").toLowerCase()) {
|
|
242
351
|
case "pem":
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
// Bounded PHP build-version reader for unpackaged PHP binaries and
|
|
2
|
+
// libraries. Recovers the version a source-built PHP carries, so a BOM can
|
|
3
|
+
// answer "does this image ship a PHP affected by CVE-2026-109618/109619".
|
|
4
|
+
//
|
|
5
|
+
// The primary marker comes from php-src: SAPI_PHP_VERSION_HEADER is
|
|
6
|
+
// "X-Powered-By: PHP/" PHP_VERSION and is compiled into every SAPI binary and
|
|
7
|
+
// libphp, e.g. the literal `X-Powered-By: PHP/8.4.26`:
|
|
8
|
+
// https://github.com/php/php-src/blob/php-8.4.27/main/SAPI.h#L309
|
|
9
|
+
// The same definition is present in 7.4 through 8.5 (main/SAPI.h at each tag).
|
|
10
|
+
// If a stripped or unusual build lacks it, the installed php_version.h is read
|
|
11
|
+
// from the same prefix. configure.ac generates its PHP_VERSION line:
|
|
12
|
+
// https://github.com/php/php-src/blob/php-8.4.27/configure.ac#L83-L90
|
|
13
|
+
// and install-headers copies it under $(phpincludedir)/main:
|
|
14
|
+
// https://github.com/php/php-src/blob/php-8.4.27/build/Makefile.global#L52
|
|
15
|
+
//
|
|
16
|
+
// Every file in an image is hostile: only regular files inside the extracted
|
|
17
|
+
// root are opened, reads are capped, and a missing/odd file yields no result
|
|
18
|
+
// rather than throwing.
|
|
19
|
+
//
|
|
20
|
+
// The runtime component helpers at the end are the one place a PHP version
|
|
21
|
+
// becomes a component, for the container scan, the OS scan and formulation.
|
|
22
|
+
|
|
23
|
+
import { closeSync, readSync } from "node:fs";
|
|
24
|
+
import { basename, join, resolve, sep } from "node:path";
|
|
25
|
+
|
|
26
|
+
import { openRegularFile } from "../core/boundedFile.js";
|
|
27
|
+
import { applyPurl, tryBuildPurl } from "./purl.js";
|
|
28
|
+
|
|
29
|
+
// The version grammar the contract accepts: M.m.p, optionally with -dev or an
|
|
30
|
+
// alpha/beta/RC number.
|
|
31
|
+
const VERSION_BODY = "\\d+\\.\\d+\\.\\d+(?:-dev|(?:alpha|beta|RC)\\d+)?";
|
|
32
|
+
const MARKER = Buffer.from("X-Powered-By: PHP/");
|
|
33
|
+
const MAX_VERSION_BYTES = 32;
|
|
34
|
+
// Overlap is at least the marker length plus 32 bytes, per the contract, so a
|
|
35
|
+
// marker split across two reads is seen whole by the second one.
|
|
36
|
+
const OVERLAP_BYTES = MARKER.length + 32;
|
|
37
|
+
const READ_CHUNK_BYTES = 1024 * 1024;
|
|
38
|
+
const MAX_BINARY_BYTES = 256 * 1024 * 1024;
|
|
39
|
+
const MAX_HEADER_BYTES = 64 * 1024;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The most PHP-named files one scan opens in search of a build version.
|
|
43
|
+
*
|
|
44
|
+
* @type {number}
|
|
45
|
+
*/
|
|
46
|
+
export const MAX_PHP_PROBES = 64;
|
|
47
|
+
|
|
48
|
+
// The trailing lookahead rejects 8.4.26.1 and 8.4.26foo while accepting
|
|
49
|
+
// 8.4.26, 8.4.26-dev and 8.6.0RC3 followed by a non-version byte.
|
|
50
|
+
const VERSION_PREFIX_RE = new RegExp(`^(${VERSION_BODY})(?![0-9A-Za-z.-])`);
|
|
51
|
+
const HEADER_VERSION_RE = new RegExp(
|
|
52
|
+
`^#define\\s+PHP_VERSION\\s+"(${VERSION_BODY})"\\s*$`,
|
|
53
|
+
"m",
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Read the version a text starts with, in the grammar the build-version reader
|
|
58
|
+
* accepts: `8.4.27`, `8.5.0-dev`, `8.6.0RC3`. What follows the version must not
|
|
59
|
+
* continue it, so `8.4.26.1` and `8.4.26foo` parse to nothing. A distro
|
|
60
|
+
* revision such as Ubuntu's `8.1.2-1ubuntu2.14` is not an upstream version
|
|
61
|
+
* either, and parses to nothing rather than to a guess.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} text text that begins with the version
|
|
64
|
+
* @returns {string|undefined} the version, or undefined when the text does not
|
|
65
|
+
* begin with one
|
|
66
|
+
*/
|
|
67
|
+
export function parsePhpVersion(text) {
|
|
68
|
+
if (typeof text !== "string") {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
// One character past the maximum lets the lookahead see a too-long version
|
|
72
|
+
// instead of treating the end of the slice as a boundary.
|
|
73
|
+
const match = VERSION_PREFIX_RE.exec(text.slice(0, MAX_VERSION_BYTES + 1));
|
|
74
|
+
if (!match || match[1].length > MAX_VERSION_BYTES) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
return match[1];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// A distro package can append its revision to PHP_VERSION, as Ubuntu 22.04
|
|
81
|
+
// does. The digit after the hyphen tells it from PHP's own `-dev`.
|
|
82
|
+
const DISTRO_VERSION_RE =
|
|
83
|
+
/^\d+\.\d+\.\d+-\d[0-9A-Za-z.+~-]*(?![0-9A-Za-z.+~-])/;
|
|
84
|
+
const MAX_REPORTED_VERSION_LENGTH = 64;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Read the version a PHP reports about itself, as `php -v` prints it. An
|
|
88
|
+
* upstream version parses as parsePhpVersion does. A version with a distro
|
|
89
|
+
* revision, such as Ubuntu's `8.1.2-1ubuntu2.14`, is kept whole and marked: it
|
|
90
|
+
* is what that PHP reports, but the distro backports fixes without changing the
|
|
91
|
+
* upstream part, so it must not be matched against upstream advisories.
|
|
92
|
+
*
|
|
93
|
+
* @param {string} text text that begins with the version
|
|
94
|
+
* @returns {{version: string, distroRevision: boolean}|undefined} the version,
|
|
95
|
+
* or undefined when the text does not begin with one
|
|
96
|
+
*/
|
|
97
|
+
export function parsePhpReportedVersion(text) {
|
|
98
|
+
const version = parsePhpVersion(text);
|
|
99
|
+
if (version) {
|
|
100
|
+
return { version, distroRevision: false };
|
|
101
|
+
}
|
|
102
|
+
if (typeof text !== "string") {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const match = DISTRO_VERSION_RE.exec(
|
|
106
|
+
text.slice(0, MAX_REPORTED_VERSION_LENGTH + 1),
|
|
107
|
+
);
|
|
108
|
+
if (!match || match[0].length > MAX_REPORTED_VERSION_LENGTH) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
return { version: match[0], distroRevision: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The package manager whose store a path lies in. Homebrew (`Cellar/<formula>/
|
|
116
|
+
* <version>/`), Nix (`nix/store/`) and Guix (`gnu/store/`) own everything under
|
|
117
|
+
* their stores, whatever links lead there, and a PHP they own is identified by
|
|
118
|
+
* their package, never as an unpackaged build.
|
|
119
|
+
*
|
|
120
|
+
* @param {string} filePath absolute or root-relative path, either separator
|
|
121
|
+
* @returns {string|undefined} `homebrew`, `nix` or `guix`, or undefined
|
|
122
|
+
*/
|
|
123
|
+
export function packageStoreOf(filePath) {
|
|
124
|
+
if (typeof filePath !== "string" || !filePath) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const parts = filePath.split(/[\\/]+/);
|
|
128
|
+
const cellar = parts.indexOf("Cellar");
|
|
129
|
+
if (cellar !== -1 && parts.length - cellar >= 4) {
|
|
130
|
+
return "homebrew";
|
|
131
|
+
}
|
|
132
|
+
for (let index = 0; index + 2 < parts.length; index++) {
|
|
133
|
+
if (parts[index + 1] !== "store") {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (parts[index] === "nix") {
|
|
137
|
+
return "nix";
|
|
138
|
+
}
|
|
139
|
+
if (parts[index] === "gnu") {
|
|
140
|
+
return "guix";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Installed names come from the per-SAPI Makefile.frag files at the 8.4.27
|
|
147
|
+
// tag: bin/php, bin/php-cgi, sbin/php-fpm, bin/phpdbg and libs/libphp.so,
|
|
148
|
+
// each with the optional $(program_prefix)/$(program_suffix):
|
|
149
|
+
// https://github.com/php/php-src/blob/php-8.4.27/sapi/cli/Makefile.frag#L9
|
|
150
|
+
// https://github.com/php/php-src/blob/php-8.4.27/sapi/cgi/Makefile.frag#L9
|
|
151
|
+
// https://github.com/php/php-src/blob/php-8.4.27/sapi/fpm/Makefile.frag#L11
|
|
152
|
+
// https://github.com/php/php-src/blob/php-8.4.27/sapi/phpdbg/Makefile.frag#L28
|
|
153
|
+
// The Apache module is libphp.so (libs/libphp.so in build/Makefile.global).
|
|
154
|
+
// FrankenPHP embeds PHP in its own binary.
|
|
155
|
+
const PHP_SAPI_BY_NAME = [
|
|
156
|
+
[/^php(\d+(\.\d+)?)?$/, "cli"],
|
|
157
|
+
[/^php-fpm(\d+(\.\d+)?)?$/, "fpm"],
|
|
158
|
+
[/^php-cgi(\d+(\.\d+)?)?$/, "cgi"],
|
|
159
|
+
[/^phpdbg(\d+(\.\d+)?)?$/, "phpdbg"],
|
|
160
|
+
[/^libphp(\d+(\.\d+)?)?\.so$/, "libphp"],
|
|
161
|
+
[/^frankenphp$/, "frankenphp"],
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Classify a file by its basename into a PHP SAPI, or undefined when the name
|
|
166
|
+
* is not one php-src installs.
|
|
167
|
+
*
|
|
168
|
+
* @param {string} fileName basename or path of the candidate
|
|
169
|
+
* @returns {string|undefined} cli, fpm, cgi, phpdbg, libphp or frankenphp
|
|
170
|
+
*/
|
|
171
|
+
export function classifyPhpSapi(fileName) {
|
|
172
|
+
if (typeof fileName !== "string" || !fileName) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
const name = basename(fileName);
|
|
176
|
+
for (const [pattern, sapi] of PHP_SAPI_BY_NAME) {
|
|
177
|
+
if (pattern.test(name)) {
|
|
178
|
+
return sapi;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function matchVersionAt(buffer, start, readLength) {
|
|
185
|
+
if (start >= readLength) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
// One byte past the maximum lets the lookahead see a too-long version
|
|
189
|
+
// instead of treating the end of the slice as a boundary.
|
|
190
|
+
const end = Math.min(readLength, start + MAX_VERSION_BYTES + 1);
|
|
191
|
+
return parsePhpVersion(buffer.toString("latin1", start, end));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Scan a bounded prefix of a binary for the compiled-in marker. Files larger
|
|
196
|
+
* than the cap are skipped entirely: the contract treats them as hostile and
|
|
197
|
+
* emits no property.
|
|
198
|
+
*
|
|
199
|
+
* @param {number} fd open file descriptor
|
|
200
|
+
* @param {number} size file size from fstat
|
|
201
|
+
* @returns {string|undefined}
|
|
202
|
+
*/
|
|
203
|
+
function scanBinaryVersion(fd, size) {
|
|
204
|
+
if (size <= 0 || size > MAX_BINARY_BYTES) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
const bufferSize = Math.min(size, READ_CHUNK_BYTES) + OVERLAP_BYTES;
|
|
208
|
+
const buffer = Buffer.allocUnsafe(bufferSize);
|
|
209
|
+
for (let position = 0; position < size; position += READ_CHUNK_BYTES) {
|
|
210
|
+
const want = Math.min(bufferSize, size - position);
|
|
211
|
+
const read = readSync(fd, buffer, 0, want, position);
|
|
212
|
+
if (read <= 0) {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
// Only matches starting inside this window are new; the overlap exists so
|
|
216
|
+
// a version split at the window edge is readable. The last window has no
|
|
217
|
+
// next one, so everything it read is searched.
|
|
218
|
+
const isLast = position + read >= size;
|
|
219
|
+
const searchLimit = isLast ? read : Math.min(READ_CHUNK_BYTES, read);
|
|
220
|
+
// Bytes a previous, longer read left past `read` are never searched.
|
|
221
|
+
const window = buffer.subarray(0, read);
|
|
222
|
+
let offset = window.indexOf(MARKER);
|
|
223
|
+
while (offset !== -1 && offset < searchLimit) {
|
|
224
|
+
const version = matchVersionAt(buffer, offset + MARKER.length, read);
|
|
225
|
+
if (version) {
|
|
226
|
+
return version;
|
|
227
|
+
}
|
|
228
|
+
offset = window.indexOf(MARKER, offset + 1);
|
|
229
|
+
}
|
|
230
|
+
if (read < bufferSize) {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Derive the install prefix from a path installed under bin, sbin or lib:
|
|
239
|
+
* /usr/local/bin/php -> /usr/local, /usr/lib/apache2/modules/libphp.so -> /usr.
|
|
240
|
+
*
|
|
241
|
+
* @param {string} absPath absolute path
|
|
242
|
+
* @returns {string|undefined}
|
|
243
|
+
*/
|
|
244
|
+
function installPrefix(absPath) {
|
|
245
|
+
const parts = resolve(absPath).split(sep);
|
|
246
|
+
for (let i = parts.length - 2; i >= 0; i--) {
|
|
247
|
+
if (parts[i] === "bin" || parts[i] === "sbin" || parts[i] === "lib") {
|
|
248
|
+
return parts.slice(0, i).join(sep) || sep;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Read PHP_VERSION from <prefix>/include/php/main/php_version.h. Only called
|
|
256
|
+
* for files installed under bin, sbin or lib, and only when the binary marker
|
|
257
|
+
* was absent.
|
|
258
|
+
*
|
|
259
|
+
* @param {string} absPath path of the PHP binary/library
|
|
260
|
+
* @param {string|undefined} rootDir extracted root
|
|
261
|
+
* @returns {string|undefined}
|
|
262
|
+
*/
|
|
263
|
+
function readHeaderVersion(absPath, rootDir) {
|
|
264
|
+
const prefix = installPrefix(absPath);
|
|
265
|
+
if (!prefix) {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
const headerPath = join(prefix, "include", "php", "main", "php_version.h");
|
|
269
|
+
const opened = openRegularFile(headerPath, rootDir);
|
|
270
|
+
if (!opened) {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
const length = Math.min(opened.size, MAX_HEADER_BYTES);
|
|
275
|
+
if (length <= 0) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
279
|
+
const read = readSync(opened.fd, buffer, 0, length, 0);
|
|
280
|
+
if (read <= 0) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
const match = HEADER_VERSION_RE.exec(buffer.toString("latin1", 0, read));
|
|
284
|
+
return match ? match[1] : undefined;
|
|
285
|
+
} catch {
|
|
286
|
+
return undefined;
|
|
287
|
+
} finally {
|
|
288
|
+
try {
|
|
289
|
+
closeSync(opened.fd);
|
|
290
|
+
} catch {
|
|
291
|
+
// already closed
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Read the PHP build version of a binary or shared library.
|
|
298
|
+
*
|
|
299
|
+
* @param {string} absPath absolute path
|
|
300
|
+
* @param {string|undefined} rootDir extracted root, when the file came from one
|
|
301
|
+
* @returns {{version: string, source: string}|undefined} source is `binary`
|
|
302
|
+
* or `php_version.h`
|
|
303
|
+
*/
|
|
304
|
+
export function readPhpBuildVersion(absPath, rootDir) {
|
|
305
|
+
const opened = openRegularFile(absPath, rootDir);
|
|
306
|
+
if (!opened) {
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
const fromBinary = scanBinaryVersion(opened.fd, opened.size);
|
|
311
|
+
if (fromBinary) {
|
|
312
|
+
return { version: fromBinary, source: "binary" };
|
|
313
|
+
}
|
|
314
|
+
} finally {
|
|
315
|
+
try {
|
|
316
|
+
closeSync(opened.fd);
|
|
317
|
+
} catch {
|
|
318
|
+
// already closed
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const fromHeader = readHeaderVersion(absPath, rootDir);
|
|
322
|
+
return fromHeader
|
|
323
|
+
? { version: fromHeader, source: "php_version.h" }
|
|
324
|
+
: undefined;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* The CPE that lets a downstream scanner match a PHP version. NVD puts a
|
|
329
|
+
* pre-release suffix in the update field, e.g.
|
|
330
|
+
* cpe:2.3:a:php:php:8.5.0:rc1:*:*:*:*:*:* (NVD CPE dictionary).
|
|
331
|
+
*
|
|
332
|
+
* @param {string} version PHP version in the build-version grammar
|
|
333
|
+
* @returns {string} CPE 2.3 string
|
|
334
|
+
*/
|
|
335
|
+
export function phpCpe(version) {
|
|
336
|
+
const match = /^(\d+\.\d+\.\d+)(?:-(dev)|(alpha|beta|RC)(\d+))?$/.exec(
|
|
337
|
+
version,
|
|
338
|
+
);
|
|
339
|
+
if (!match) {
|
|
340
|
+
return `cpe:2.3:a:php:php:${version}:*:*:*:*:*:*:*`;
|
|
341
|
+
}
|
|
342
|
+
let update = "*";
|
|
343
|
+
if (match[2]) {
|
|
344
|
+
update = "dev";
|
|
345
|
+
} else if (match[3]) {
|
|
346
|
+
update = `${match[3].toLowerCase()}${match[4]}`;
|
|
347
|
+
}
|
|
348
|
+
return `cpe:2.3:a:php:php:${match[1]}:${update}:*:*:*:*:*:*`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* A PHP runtime is one `platform` component per distinct version, like the Go
|
|
353
|
+
* stdlib component. Its identity is `pkg:generic/php`: it is only used where no
|
|
354
|
+
* package manager owns PHP, or where a PATH probe cannot tell which one does.
|
|
355
|
+
*
|
|
356
|
+
* @param {string} version PHP version in the build-version grammar, or as a
|
|
357
|
+
* distro build reports it
|
|
358
|
+
* @param {Object} [options] options
|
|
359
|
+
* @param {boolean} [options.withCpe=true] false for a version with a distro
|
|
360
|
+
* revision, which no upstream CPE describes
|
|
361
|
+
* @returns {Object} component with `purl`, `bom-ref` and, unless left out, `cpe`
|
|
362
|
+
*/
|
|
363
|
+
export function createPhpRuntimeComponent(version, { withCpe = true } = {}) {
|
|
364
|
+
const component = { type: "platform", name: "php", version };
|
|
365
|
+
if (withCpe) {
|
|
366
|
+
component.cpe = phpCpe(version);
|
|
367
|
+
}
|
|
368
|
+
return applyPurl(
|
|
369
|
+
component,
|
|
370
|
+
tryBuildPurl({ type: "generic", name: "php", version }),
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The bom-ref of the runtime component for a version.
|
|
376
|
+
*
|
|
377
|
+
* @param {string} version PHP version in the build-version grammar
|
|
378
|
+
* @returns {string} bom-ref, which is the runtime's purl
|
|
379
|
+
*/
|
|
380
|
+
export function phpRuntimeRef(version) {
|
|
381
|
+
return createPhpRuntimeComponent(version)["bom-ref"];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The properties a PHP file component carries once its build version is known.
|
|
386
|
+
* They belong on unpackaged `file` components only: a package-owned PHP is
|
|
387
|
+
* identified by its package, and distro builds carry backported fixes under
|
|
388
|
+
* old version numbers, so the PHP-RCE audit rules that match these properties
|
|
389
|
+
* would raise false positives there.
|
|
390
|
+
*
|
|
391
|
+
* @param {{version: string, source: string}} build result of readPhpBuildVersion
|
|
392
|
+
* @param {string} sapi result of classifyPhpSapi
|
|
393
|
+
* @returns {{name: string, value: string}[]} properties to append
|
|
394
|
+
*/
|
|
395
|
+
export function phpBuildProperties(build, sapi) {
|
|
396
|
+
return [
|
|
397
|
+
{ name: "cdx:php:buildVersion", value: build.version },
|
|
398
|
+
{ name: "cdx:php:buildVersionSource", value: build.source },
|
|
399
|
+
{ name: "cdx:php:sapi", value: sapi },
|
|
400
|
+
];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Start collecting the runtime components and edges of one scan.
|
|
405
|
+
*
|
|
406
|
+
* @returns {{runtimeComponents: Object[], dependencies: Object[], runtimeRefs: Set<string>}}
|
|
407
|
+
*/
|
|
408
|
+
export function createPhpRuntimeInventory() {
|
|
409
|
+
return { runtimeComponents: [], dependencies: [], runtimeRefs: new Set() };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Tag a PHP file component with its build facts and link it to the runtime
|
|
414
|
+
* component of its version, creating that component on first use. Mutates the
|
|
415
|
+
* component in place, as the Go collector does.
|
|
416
|
+
*
|
|
417
|
+
* @param {{runtimeComponents: Object[], dependencies: Object[], runtimeRefs: Set<string>}} inventory
|
|
418
|
+
* accumulator from createPhpRuntimeInventory
|
|
419
|
+
* @param {Object} component file component, with a `properties` array
|
|
420
|
+
* @param {{version: string, source: string}} build result of readPhpBuildVersion
|
|
421
|
+
* @param {string} sapi result of classifyPhpSapi
|
|
422
|
+
*/
|
|
423
|
+
export function attachPhpBuild(inventory, component, build, sapi) {
|
|
424
|
+
component.properties.push(...phpBuildProperties(build, sapi));
|
|
425
|
+
const runtime = createPhpRuntimeComponent(build.version);
|
|
426
|
+
const runtimeRef = runtime["bom-ref"];
|
|
427
|
+
if (!inventory.runtimeRefs.has(runtimeRef)) {
|
|
428
|
+
inventory.runtimeRefs.add(runtimeRef);
|
|
429
|
+
inventory.runtimeComponents.push(runtime);
|
|
430
|
+
inventory.dependencies.push({ ref: runtimeRef, dependsOn: [] });
|
|
431
|
+
}
|
|
432
|
+
if (component["bom-ref"]) {
|
|
433
|
+
inventory.dependencies.push({
|
|
434
|
+
ref: component["bom-ref"],
|
|
435
|
+
dependsOn: [runtimeRef],
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|