@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
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Bounded, read-only probe for the PHP no package manager owns on a live host,
|
|
2
|
+
// for `-t os` / obom. osquery's package tables list what dpkg, rpm and the like
|
|
3
|
+
// installed, so a PHP built from source (a Docker Official image, FrankenPHP, a
|
|
4
|
+
// /usr/local or /opt build) is invisible to them, and with it the version a PHP
|
|
5
|
+
// advisory is read against.
|
|
6
|
+
//
|
|
7
|
+
// Candidates come from a few fixed directories, one level deep, never from a
|
|
8
|
+
// walk of the file system. A candidate is opened only after the package
|
|
9
|
+
// databases have said they do not own it: a package manager's file is
|
|
10
|
+
// identified by its package, and distros backport fixes under old PHP version
|
|
11
|
+
// numbers, which the cdx:php properties would misstate. Only files nothing owns
|
|
12
|
+
// are read, for their bytes; nothing is executed.
|
|
13
|
+
//
|
|
14
|
+
// A package database that exists but cannot be read leaves ownership unknown.
|
|
15
|
+
// Unknown is not unpackaged: it emits nothing and records a degradation.
|
|
16
|
+
|
|
17
|
+
import { dirname, join } from "node:path";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
isDryRun,
|
|
21
|
+
readEnvironmentVariable,
|
|
22
|
+
recordActivity,
|
|
23
|
+
} from "../core/activity.js";
|
|
24
|
+
import { recordDegradation } from "../core/buildLedger.js";
|
|
25
|
+
import {
|
|
26
|
+
isDirectory,
|
|
27
|
+
listDirectory,
|
|
28
|
+
probeHostRuntime,
|
|
29
|
+
} from "./hostRuntimeProbe.js";
|
|
30
|
+
import {
|
|
31
|
+
attachPhpBuild,
|
|
32
|
+
classifyPhpSapi,
|
|
33
|
+
createPhpRuntimeInventory,
|
|
34
|
+
MAX_PHP_PROBES,
|
|
35
|
+
readPhpBuildVersion,
|
|
36
|
+
} from "./phpBuildInfo.js";
|
|
37
|
+
|
|
38
|
+
// Where the PHP of a source build is installed, relative to the root. `only`
|
|
39
|
+
// narrows a directory to one SAPI. `opt/*/bin` and `opt/*/sbin` read /opt one
|
|
40
|
+
// level down and nothing below that.
|
|
41
|
+
const POSIX_DIRECTORIES = {
|
|
42
|
+
linux: [
|
|
43
|
+
{ dir: "usr/local/bin" },
|
|
44
|
+
{ dir: "usr/local/sbin" },
|
|
45
|
+
{ dir: "usr/local/lib", only: "libphp" },
|
|
46
|
+
{ dir: "usr/lib/apache2/modules", only: "libphp" },
|
|
47
|
+
{ dir: "usr/lib/httpd/modules", only: "libphp" },
|
|
48
|
+
{ dir: "usr/lib64/httpd/modules", only: "libphp" },
|
|
49
|
+
{ dir: "opt/*/bin" },
|
|
50
|
+
{ dir: "opt/*/sbin" },
|
|
51
|
+
],
|
|
52
|
+
darwin: [
|
|
53
|
+
{ dir: "usr/local/bin" },
|
|
54
|
+
{ dir: "usr/local/sbin" },
|
|
55
|
+
{ dir: "opt/homebrew/bin" },
|
|
56
|
+
{ dir: "opt/homebrew/sbin" },
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// The programs of a php.net Windows build. The CGI and debugger executables
|
|
61
|
+
// hold no version of their own; the core DLL beside them does (php8.dll, or
|
|
62
|
+
// php8ts.dll for a thread-safe build).
|
|
63
|
+
const WINDOWS_SAPIS = {
|
|
64
|
+
"php.exe": "cli",
|
|
65
|
+
"php-win.exe": "cli",
|
|
66
|
+
"php-cgi.exe": "cgi",
|
|
67
|
+
"phpdbg.exe": "phpdbg",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function isWindowsCoreLibrary(name) {
|
|
71
|
+
const lower = name.toLowerCase();
|
|
72
|
+
if (!lower.startsWith("php") || !lower.endsWith(".dll")) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const stem = lower.slice("php".length, -".dll".length);
|
|
76
|
+
return /^\d+$/.test(stem.endsWith("ts") ? stem.slice(0, -2) : stem);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The directories to look in, as absolute paths.
|
|
80
|
+
function candidateDirectories(context) {
|
|
81
|
+
const { rootPath, platform } = context;
|
|
82
|
+
const directories = [];
|
|
83
|
+
if (platform === "win32") {
|
|
84
|
+
const programFiles =
|
|
85
|
+
context.readEnv("ProgramFiles") || join(rootPath, "Program Files");
|
|
86
|
+
for (const parent of [rootPath, join(rootPath, "tools"), programFiles]) {
|
|
87
|
+
for (const name of listDirectory(parent)) {
|
|
88
|
+
const directory = join(parent, name);
|
|
89
|
+
if (name.toLowerCase().startsWith("php") && isDirectory(directory)) {
|
|
90
|
+
directories.push({ directory });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return directories;
|
|
95
|
+
}
|
|
96
|
+
for (const { dir, only } of POSIX_DIRECTORIES[platform] || []) {
|
|
97
|
+
if (!dir.startsWith("opt/*/")) {
|
|
98
|
+
directories.push({ directory: join(rootPath, dir), only });
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const leaf = dir.slice("opt/*/".length);
|
|
102
|
+
for (const name of listDirectory(join(rootPath, "opt"))) {
|
|
103
|
+
const directory = join(rootPath, "opt", name, leaf);
|
|
104
|
+
if (isDirectory(directory)) {
|
|
105
|
+
directories.push({ directory, only });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return directories;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// On Windows the CGI and debugger executables hold no version; the core DLL in
|
|
113
|
+
// the same directory does.
|
|
114
|
+
function readBuild(context, candidate) {
|
|
115
|
+
const build = readPhpBuildVersion(candidate.real, context.realRoot);
|
|
116
|
+
if (build || context.platform !== "win32") {
|
|
117
|
+
return build;
|
|
118
|
+
}
|
|
119
|
+
const directory = dirname(candidate.real);
|
|
120
|
+
for (const name of listDirectory(directory)) {
|
|
121
|
+
if (isWindowsCoreLibrary(name)) {
|
|
122
|
+
const fromLibrary = readPhpBuildVersion(
|
|
123
|
+
join(directory, name),
|
|
124
|
+
context.realRoot,
|
|
125
|
+
);
|
|
126
|
+
if (fromLibrary) {
|
|
127
|
+
return fromLibrary;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Look for PHP no package manager owns, in the fixed directories of the host.
|
|
136
|
+
* Components come back only for the files nothing owns: a `file` component for
|
|
137
|
+
* each PHP binary or library with the cdx:php properties, one `platform`
|
|
138
|
+
* component per version with its CPE, and the edges between them.
|
|
139
|
+
*
|
|
140
|
+
* @param {Object} [options] probe options
|
|
141
|
+
* @param {string} [options.rootPath] directory the fixed directories hang off:
|
|
142
|
+
* `/`, or the system drive on Windows. Tests point it at a built tree.
|
|
143
|
+
* @param {string} [options.platform] `linux`, `darwin` or `win32`
|
|
144
|
+
* @param {Object} [options.env] environment read for ProgramFiles, SystemDrive
|
|
145
|
+
* and ChocolateyInstall, instead of the process's
|
|
146
|
+
* @param {{dpkgInfoDirectory?: string, apkInstalledFile?: string, rpmDatabaseDirectories?: string[]}} [options.packageDatabases]
|
|
147
|
+
* where the package databases are, instead of under `rootPath`
|
|
148
|
+
* @param {function(string): (Object[]|undefined)} [options.executeOsQuery] runs
|
|
149
|
+
* an osquery query; the rpm database is only asked through it
|
|
150
|
+
* @param {Object[]} [options.packageComponents] the packages the OS scan has
|
|
151
|
+
* inventoried, from which Chocolatey's are known
|
|
152
|
+
* @param {number} [options.maxProbes] most candidate files to look at
|
|
153
|
+
* @returns {{components: Object[], dependencies: Object[]}}
|
|
154
|
+
*/
|
|
155
|
+
export function probeHostPhp(options = {}) {
|
|
156
|
+
return probeHostRuntime(options, {
|
|
157
|
+
label: "PHP",
|
|
158
|
+
ecosystem: "php",
|
|
159
|
+
capability: "php-host-probe",
|
|
160
|
+
remediationId: "php.host-probe.package-db-unreadable",
|
|
161
|
+
maxProbes: MAX_PHP_PROBES,
|
|
162
|
+
chocolateyPackages: ["php"],
|
|
163
|
+
// Chocolatey's php package unpacks under C:\tools by default.
|
|
164
|
+
chocolateyDirectories: (context) => [join(context.rootPath, "tools")],
|
|
165
|
+
directories: candidateDirectories,
|
|
166
|
+
classify: (name, platform) =>
|
|
167
|
+
platform === "win32"
|
|
168
|
+
? WINDOWS_SAPIS[name.toLowerCase()]
|
|
169
|
+
: classifyPhpSapi(name),
|
|
170
|
+
readBuild,
|
|
171
|
+
createInventory: createPhpRuntimeInventory,
|
|
172
|
+
attach: (inventory, component, build, candidate) =>
|
|
173
|
+
attachPhpBuild(inventory, component, build, candidate.kind),
|
|
174
|
+
finish: (inventory) => ({
|
|
175
|
+
components: inventory.runtimeComponents,
|
|
176
|
+
dependencies: inventory.dependencies,
|
|
177
|
+
}),
|
|
178
|
+
hooks: {
|
|
179
|
+
isDryRun,
|
|
180
|
+
readEnvironmentVariable,
|
|
181
|
+
recordActivity,
|
|
182
|
+
recordDegradation,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import {
|
|
2
|
+
closeSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
openSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
readSync,
|
|
7
|
+
realpathSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { basename, join, relative, sep } from "node:path";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
androidNetworkSecurityConfigPins,
|
|
13
|
+
CertificateCollector,
|
|
14
|
+
certRecordFromDer,
|
|
15
|
+
classifyCryptoFile,
|
|
16
|
+
kubernetesTlsSecretRecords,
|
|
17
|
+
linkFallbackIssuers,
|
|
18
|
+
} from "./certificates.js";
|
|
19
|
+
|
|
20
|
+
// Bounds mirror trustinspector's rootfs mode (PLAN section 4.1): 16 MiB per
|
|
21
|
+
// file, 20,000 files read and 500,000 entries visited per tree, depth 12.
|
|
22
|
+
export const ROOTFS_CERT_LIMITS = Object.freeze({
|
|
23
|
+
maxFileBytes: 16 * 1024 * 1024,
|
|
24
|
+
maxFiles: 20000,
|
|
25
|
+
maxEntries: 500000,
|
|
26
|
+
maxDepth: 12,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Coverage table: the same locations and sourceTypes as trustinspector's
|
|
30
|
+
// rootfs mode (thirdparty/trustinspector/rootfs.go candidateDirs).
|
|
31
|
+
const CA_STORE_DIRS = [
|
|
32
|
+
"etc/ssl/certs",
|
|
33
|
+
"usr/local/share/ca-certificates",
|
|
34
|
+
"usr/share/ca-certificates",
|
|
35
|
+
"etc/pki/tls/certs",
|
|
36
|
+
"etc/pki/ca-trust/source/anchors",
|
|
37
|
+
"etc/pki/ca-trust/extracted",
|
|
38
|
+
"var/lib/ca-certificates",
|
|
39
|
+
"usr/share/pki/ca-trust-source/anchors",
|
|
40
|
+
];
|
|
41
|
+
const CA_STORE_FILES = ["etc/ssl/cert.pem"];
|
|
42
|
+
const CA_BLOCKLIST_DIRS = [
|
|
43
|
+
"etc/pki/ca-trust/source/blocklist",
|
|
44
|
+
"etc/pki/ca-trust/source/blacklist",
|
|
45
|
+
"usr/share/pki/ca-trust-source/blocklist",
|
|
46
|
+
"usr/share/pki/ca-trust-source/blacklist",
|
|
47
|
+
];
|
|
48
|
+
const SERVER_CONFIG_DIRS = [
|
|
49
|
+
"etc/nginx",
|
|
50
|
+
"etc/apache2",
|
|
51
|
+
"etc/httpd",
|
|
52
|
+
"etc/haproxy",
|
|
53
|
+
"etc/letsencrypt/live",
|
|
54
|
+
"etc/letsencrypt/archive",
|
|
55
|
+
"etc/kubernetes/pki",
|
|
56
|
+
"var/lib/kubelet/pki",
|
|
57
|
+
"etc/docker/certs.d",
|
|
58
|
+
"etc/containerd/certs.d",
|
|
59
|
+
"etc/envoy",
|
|
60
|
+
"etc/traefik",
|
|
61
|
+
];
|
|
62
|
+
const JAVA_DIRS = [
|
|
63
|
+
"usr/lib/jvm",
|
|
64
|
+
"opt",
|
|
65
|
+
"etc/ssl/certs/java",
|
|
66
|
+
"etc/pki/java",
|
|
67
|
+
"usr/share/elasticsearch",
|
|
68
|
+
];
|
|
69
|
+
const LANGUAGE_BUNDLE_DIRS = ["usr/lib", "usr/local/lib", "opt"];
|
|
70
|
+
// Kubernetes secrets and Android configs are cdxgen-side producers: the
|
|
71
|
+
// plugin has no YAML or Android parser.
|
|
72
|
+
const SECRET_YAML_DIRS = [
|
|
73
|
+
"etc/kubernetes",
|
|
74
|
+
"var/run/secrets",
|
|
75
|
+
"run/secrets",
|
|
76
|
+
"var/lib/kubelet",
|
|
77
|
+
"srv",
|
|
78
|
+
"app",
|
|
79
|
+
"opt",
|
|
80
|
+
"home",
|
|
81
|
+
"root",
|
|
82
|
+
];
|
|
83
|
+
const ANDROID_DIRS = ["app", "opt", "srv", "data", "home", "root"];
|
|
84
|
+
|
|
85
|
+
const CERT_SUFFIXES = [
|
|
86
|
+
".pem",
|
|
87
|
+
".crt",
|
|
88
|
+
".cer",
|
|
89
|
+
".der",
|
|
90
|
+
".p7b",
|
|
91
|
+
".p7c",
|
|
92
|
+
".p12",
|
|
93
|
+
".pfx",
|
|
94
|
+
".jks",
|
|
95
|
+
".jceks",
|
|
96
|
+
".truststore",
|
|
97
|
+
".key",
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
function isJavaKeystoreName(base) {
|
|
101
|
+
if (base === "cacerts" || base === "jssecacerts") {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return [".jks", ".jceks", ".truststore", ".p12", ".pfx"].some((suffix) =>
|
|
105
|
+
base.endsWith(suffix),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Mirrors looksLikeTrustMaterialPath in trustinspector: private-key
|
|
110
|
+
// directories are never read.
|
|
111
|
+
function looksLikeTrustMaterialPath(rel, sourceType) {
|
|
112
|
+
const lower = rel.toLowerCase();
|
|
113
|
+
if (
|
|
114
|
+
lower.startsWith("etc/ssl/private/") ||
|
|
115
|
+
lower.startsWith("etc/pki/tls/private/")
|
|
116
|
+
) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
const base = basename(lower);
|
|
120
|
+
if (sourceType === "language-bundle") {
|
|
121
|
+
return base === "cacert.pem";
|
|
122
|
+
}
|
|
123
|
+
if (sourceType === "java-truststore") {
|
|
124
|
+
return isJavaKeystoreName(base);
|
|
125
|
+
}
|
|
126
|
+
if (isJavaKeystoreName(base)) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return (
|
|
130
|
+
CERT_SUFFIXES.some((suffix) => lower.endsWith(suffix)) ||
|
|
131
|
+
lower.includes("/certs/")
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Read at most `maxBytes` of a regular file. The cap applies to the read
|
|
137
|
+
* itself, so a file that grows after the size check cannot exceed it. Files
|
|
138
|
+
* that are not regular (FIFOs, devices, sockets) are never opened.
|
|
139
|
+
*/
|
|
140
|
+
function readCapped(absolute, maxBytes) {
|
|
141
|
+
const before = lstatSync(absolute);
|
|
142
|
+
if (!before.isFile() || before.size > maxBytes) {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
const fd = openSync(absolute, "r");
|
|
146
|
+
try {
|
|
147
|
+
const buffer = Buffer.alloc(Math.min(before.size, maxBytes) + 1);
|
|
148
|
+
let total = 0;
|
|
149
|
+
while (total < buffer.length) {
|
|
150
|
+
const n = readSync(fd, buffer, total, buffer.length - total, null);
|
|
151
|
+
if (n === 0) {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
total += n;
|
|
155
|
+
}
|
|
156
|
+
if (total > maxBytes) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
return buffer.subarray(0, total);
|
|
160
|
+
} finally {
|
|
161
|
+
closeSync(fd);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Pure-JS inventory of the certificate locations trustinspector's rootfs
|
|
167
|
+
* mode covers, plus Kubernetes TLS secrets and Android pins. Used when the
|
|
168
|
+
* plugin is not available. Produces components through the shared
|
|
169
|
+
* CertificateCollector, so bom-refs, names and deduplication match the other
|
|
170
|
+
* producers; locations are root-relative ("/etc/ssl/certs/x.crt").
|
|
171
|
+
*
|
|
172
|
+
* Every access stays inside the root: symlinks (files or directories) are
|
|
173
|
+
* skipped, only regular files are opened, and the walk is bounded by entry,
|
|
174
|
+
* file, size and depth limits.
|
|
175
|
+
*
|
|
176
|
+
* @param {string} rootDir extracted root filesystem
|
|
177
|
+
* @param {object} [limits] overrides for ROOTFS_CERT_LIMITS (tests)
|
|
178
|
+
* @returns {{components: object[], dependencies: object[], warnings: string[]}}
|
|
179
|
+
*/
|
|
180
|
+
export function collectRootfsCertificates(rootDir, limits = {}) {
|
|
181
|
+
const bounds = { ...ROOTFS_CERT_LIMITS, ...limits };
|
|
182
|
+
const warnings = [];
|
|
183
|
+
let realRoot;
|
|
184
|
+
try {
|
|
185
|
+
realRoot = realpathSync(rootDir);
|
|
186
|
+
} catch {
|
|
187
|
+
return { components: [], dependencies: [], warnings };
|
|
188
|
+
}
|
|
189
|
+
const collector = new CertificateCollector();
|
|
190
|
+
const records = [];
|
|
191
|
+
const components = [];
|
|
192
|
+
const seenFiles = new Set();
|
|
193
|
+
let filesRead = 0;
|
|
194
|
+
|
|
195
|
+
// Resolve a root-relative directory to an absolute path that is proven to
|
|
196
|
+
// stay inside the root; undefined when it does not exist, is not a
|
|
197
|
+
// directory, or any symlink on the way leaves the root.
|
|
198
|
+
const resolveInside = (rel) => {
|
|
199
|
+
const absolute = join(realRoot, rel);
|
|
200
|
+
let real;
|
|
201
|
+
try {
|
|
202
|
+
real = realpathSync(absolute);
|
|
203
|
+
} catch {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const inside = relative(realRoot, real);
|
|
207
|
+
if (inside.startsWith("..") || inside.split(sep).includes("..")) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return real;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const walk = (relBase, visit) => {
|
|
214
|
+
const start = resolveInside(relBase);
|
|
215
|
+
if (!start) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
let startStat;
|
|
219
|
+
try {
|
|
220
|
+
startStat = lstatSync(start);
|
|
221
|
+
} catch {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (!startStat.isDirectory()) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
let entries = 0;
|
|
228
|
+
const stack = [{ abs: start, rel: relBase, depth: 0 }];
|
|
229
|
+
while (stack.length) {
|
|
230
|
+
const { abs, rel, depth } = stack.pop();
|
|
231
|
+
let dirents;
|
|
232
|
+
try {
|
|
233
|
+
dirents = readdirSync(abs, { withFileTypes: true });
|
|
234
|
+
} catch {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
dirents.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
238
|
+
for (const dirent of dirents) {
|
|
239
|
+
entries += 1;
|
|
240
|
+
if (entries > bounds.maxEntries) {
|
|
241
|
+
warnings.push(
|
|
242
|
+
`entry limit (${bounds.maxEntries}) reached in /${relBase}`,
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
// Symlinks and special files are skipped outright.
|
|
247
|
+
if (dirent.isSymbolicLink()) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const childRel = `${rel}/${dirent.name}`;
|
|
251
|
+
if (dirent.isDirectory()) {
|
|
252
|
+
if (depth + 1 >= bounds.maxDepth) {
|
|
253
|
+
warnings.push(
|
|
254
|
+
`depth limit (${bounds.maxDepth}) exceeded in /${relBase}`,
|
|
255
|
+
);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
stack.push({
|
|
259
|
+
abs: join(abs, dirent.name),
|
|
260
|
+
rel: childRel,
|
|
261
|
+
depth: depth + 1,
|
|
262
|
+
});
|
|
263
|
+
} else if (dirent.isFile()) {
|
|
264
|
+
if (visit(childRel, join(abs, dirent.name)) === false) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const readRel = (rel, abs) => {
|
|
273
|
+
if (filesRead >= bounds.maxFiles) {
|
|
274
|
+
warnings.push(`file count limit (${bounds.maxFiles}) reached`);
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
filesRead += 1;
|
|
278
|
+
try {
|
|
279
|
+
const bytes = readCapped(abs, bounds.maxFileBytes);
|
|
280
|
+
if (!bytes) {
|
|
281
|
+
warnings.push(
|
|
282
|
+
`file over ${bounds.maxFileBytes} bytes skipped: /${rel}`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
return bytes;
|
|
286
|
+
} catch {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const consumeMaterial = (rel, abs, sourceType) => {
|
|
292
|
+
if (seenFiles.has(rel) || !looksLikeTrustMaterialPath(rel, sourceType)) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const bytes = readRel(rel, abs);
|
|
296
|
+
if (!bytes?.length) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
seenFiles.add(rel);
|
|
300
|
+
const location = `/${rel}`;
|
|
301
|
+
const classification = classifyCryptoFile(bytes);
|
|
302
|
+
if (classification.kind === "certificates") {
|
|
303
|
+
const dot = basename(rel).lastIndexOf(".");
|
|
304
|
+
for (const der of classification.ders) {
|
|
305
|
+
const record = certRecordFromDer(der, { location, sourceType });
|
|
306
|
+
if (record) {
|
|
307
|
+
record.fileExtension =
|
|
308
|
+
dot > 0 ? basename(rel).slice(dot + 1) : undefined;
|
|
309
|
+
records.push(record);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} else if (classification.kind === "private-key") {
|
|
313
|
+
components.push(
|
|
314
|
+
rootfsMaterialComponent("private-key", location, {
|
|
315
|
+
format: classification.format,
|
|
316
|
+
sourceType,
|
|
317
|
+
}),
|
|
318
|
+
);
|
|
319
|
+
} else if (classification.kind === "keystore") {
|
|
320
|
+
components.push(
|
|
321
|
+
rootfsMaterialComponent("keystore", location, {
|
|
322
|
+
format: classification.format,
|
|
323
|
+
sourceType,
|
|
324
|
+
}),
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Most specific location first, so a file under two candidates takes the
|
|
330
|
+
// sourceType of the narrower one (same rule as the plugin).
|
|
331
|
+
const candidates = [
|
|
332
|
+
...CA_STORE_DIRS.map((rel) => ({ rel, sourceType: "ca-store" })),
|
|
333
|
+
...CA_BLOCKLIST_DIRS.map((rel) => ({ rel, sourceType: "ca-blocklist" })),
|
|
334
|
+
...SERVER_CONFIG_DIRS.map((rel) => ({ rel, sourceType: "server-config" })),
|
|
335
|
+
...JAVA_DIRS.map((rel) => ({ rel, sourceType: "java-truststore" })),
|
|
336
|
+
...LANGUAGE_BUNDLE_DIRS.map((rel) => ({
|
|
337
|
+
rel,
|
|
338
|
+
sourceType: "language-bundle",
|
|
339
|
+
})),
|
|
340
|
+
].sort((a, b) => b.rel.split("/").length - a.rel.split("/").length);
|
|
341
|
+
for (const file of CA_STORE_FILES) {
|
|
342
|
+
const abs = resolveInside(file);
|
|
343
|
+
if (abs && lstatSync(abs).isFile()) {
|
|
344
|
+
consumeMaterial(file, abs, "ca-store");
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
for (const { rel, sourceType } of candidates) {
|
|
348
|
+
walk(rel, (childRel, abs) => {
|
|
349
|
+
consumeMaterial(childRel, abs, sourceType);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const secretFiles = new Set();
|
|
354
|
+
for (const dir of SECRET_YAML_DIRS) {
|
|
355
|
+
walk(dir, (childRel, abs) => {
|
|
356
|
+
if (/\.ya?ml$/i.test(childRel) && !secretFiles.has(childRel)) {
|
|
357
|
+
secretFiles.add(childRel);
|
|
358
|
+
const bytes = readRel(childRel, abs);
|
|
359
|
+
const text = bytes?.toString("utf-8");
|
|
360
|
+
// Cheap pre-filter, as in the source scan.
|
|
361
|
+
if (text?.includes("kubernetes.io/tls")) {
|
|
362
|
+
records.push(
|
|
363
|
+
...kubernetesTlsSecretRecords(text, { file: `/${childRel}` }),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
const androidFiles = new Set();
|
|
370
|
+
for (const dir of ANDROID_DIRS) {
|
|
371
|
+
walk(dir, (childRel, abs) => {
|
|
372
|
+
if (
|
|
373
|
+
/\/res\/xml\/[^/]+\.xml$/i.test(childRel) &&
|
|
374
|
+
!androidFiles.has(childRel)
|
|
375
|
+
) {
|
|
376
|
+
androidFiles.add(childRel);
|
|
377
|
+
const text = readRel(childRel, abs)?.toString("utf-8");
|
|
378
|
+
if (text?.includes("<network-security-config")) {
|
|
379
|
+
for (const pin of androidNetworkSecurityConfigPins(text, {
|
|
380
|
+
file: `/${childRel}`,
|
|
381
|
+
})) {
|
|
382
|
+
collector.addPin(pin);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (records.length) {
|
|
390
|
+
linkFallbackIssuers(records);
|
|
391
|
+
collector.addRecords(records);
|
|
392
|
+
}
|
|
393
|
+
const built = collector.build();
|
|
394
|
+
return {
|
|
395
|
+
components: [...built.components, ...components],
|
|
396
|
+
dependencies: built.dependencies,
|
|
397
|
+
warnings,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Same shape as the keystore and private-key components the other producers
|
|
402
|
+
// emit (managedBom sourceKeystoreComponent / sourcePrivateKeyComponent).
|
|
403
|
+
function rootfsMaterialComponent(kind, location, { format, sourceType }) {
|
|
404
|
+
return {
|
|
405
|
+
name: basename(location),
|
|
406
|
+
type: "cryptographic-asset",
|
|
407
|
+
"bom-ref": `crypto/related-crypto-material/${kind}/${encodeURIComponent(location)}`,
|
|
408
|
+
cryptoProperties: {
|
|
409
|
+
assetType: "related-crypto-material",
|
|
410
|
+
relatedCryptoMaterialProperties: {
|
|
411
|
+
type: kind === "keystore" ? "other" : "private-key",
|
|
412
|
+
...(format ? { format } : {}),
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
evidence: { occurrences: [{ location }] },
|
|
416
|
+
properties: [
|
|
417
|
+
{ name: "internal:SrcFile", value: location },
|
|
418
|
+
...(sourceType
|
|
419
|
+
? [{ name: "cdx:crypto:sourceType", value: sourceType }]
|
|
420
|
+
: []),
|
|
421
|
+
...(kind === "keystore"
|
|
422
|
+
? [{ name: "cdx:crypto:keystoreReadable", value: "false" }]
|
|
423
|
+
: []),
|
|
424
|
+
],
|
|
425
|
+
};
|
|
426
|
+
}
|