@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,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the frontmatter of a SKILL.md satisfies the open Agent Skills
|
|
3
|
+
* constraints (agentskills.io): a name of 1-64 lowercase letters, digits and
|
|
4
|
+
* hyphens, and a description of 1-1024 characters.
|
|
5
|
+
*
|
|
6
|
+
* @param {{name?: unknown, description?: unknown}|undefined} metadata Frontmatter values
|
|
7
|
+
* @returns {boolean} true when both constraints hold
|
|
8
|
+
*/
|
|
9
|
+
export function isAgentSkillSpecValid(metadata) {
|
|
10
|
+
return (
|
|
11
|
+
typeof metadata?.name === "string" &&
|
|
12
|
+
/^[a-z0-9-]{1,64}$/u.test(metadata.name) &&
|
|
13
|
+
typeof metadata?.description === "string" &&
|
|
14
|
+
metadata.description.length >= 1 &&
|
|
15
|
+
metadata.description.length <= 1024
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// Bounded, read-only reader of the apk installed database.
|
|
2
|
+
//
|
|
3
|
+
// osquery has no apk table, so an Alpine, Wolfi or Chainguard host would have no
|
|
4
|
+
// OS package inventory in an OBOM, and the PHP host probe needs the same
|
|
5
|
+
// database to tell which PHP files a package owns. Both read it through here, so
|
|
6
|
+
// what the OBOM lists and what the probe treats as owned follow one parser: the
|
|
7
|
+
// same file, location order, caps and record rules.
|
|
8
|
+
//
|
|
9
|
+
// The database is `/lib/apk/db/installed`. A usr-merged system keeps it at
|
|
10
|
+
// `/usr/lib/apk/db/installed` (and `/lib` is a link to `/usr/lib`, so the two
|
|
11
|
+
// names are one file), and Alpaquita keeps it under `/var/lib/apk/db`. The first
|
|
12
|
+
// of those that exists is the database. One that exists but cannot be read
|
|
13
|
+
// leaves the packages unknown: it is reported as unreadable, never read in part
|
|
14
|
+
// and never skipped for the next name.
|
|
15
|
+
//
|
|
16
|
+
// A record is a run of `<letter>:<value>` lines ended by a blank line. P: names
|
|
17
|
+
// the package, V: its version, A: its architecture, o: its origin (the source
|
|
18
|
+
// package), m: its maintainer, L: its licence, U: its home page and T: its
|
|
19
|
+
// description. F: names a directory and each R: after it a file in that
|
|
20
|
+
// directory. A record may start with C: (Alpine) or P: (Wolfi).
|
|
21
|
+
|
|
22
|
+
import { statSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import process from "node:process";
|
|
25
|
+
|
|
26
|
+
import { isDryRun, recordActivity } from "../core/activity.js";
|
|
27
|
+
import { safeExistsSync, safeReadFileSync } from "../core/fs.js";
|
|
28
|
+
import { thoughtLog } from "../core/logger.js";
|
|
29
|
+
|
|
30
|
+
/** The database, or the packages it lists, past these is unreadable rather than read in part. */
|
|
31
|
+
export const MAX_APK_DATABASE_BYTES = 64 * 1024 * 1024;
|
|
32
|
+
export const MAX_APK_PACKAGES = 100000;
|
|
33
|
+
|
|
34
|
+
/** Where apk keeps the installed database, relative to the root, in the order they are tried. */
|
|
35
|
+
export const APK_INSTALLED_DB_PATHS = [
|
|
36
|
+
"lib/apk/db/installed",
|
|
37
|
+
"usr/lib/apk/db/installed",
|
|
38
|
+
"var/lib/apk/db/installed",
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
// No field of a package record is longer than this once read.
|
|
42
|
+
const MAX_FIELD_LENGTH = 4096;
|
|
43
|
+
|
|
44
|
+
const FIELD_NAMES = {
|
|
45
|
+
P: "name",
|
|
46
|
+
V: "version",
|
|
47
|
+
A: "arch",
|
|
48
|
+
o: "origin",
|
|
49
|
+
m: "maintainer",
|
|
50
|
+
L: "license",
|
|
51
|
+
U: "url",
|
|
52
|
+
T: "description",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function readBounded(file, maxBytes) {
|
|
56
|
+
try {
|
|
57
|
+
const stats = statSync(file);
|
|
58
|
+
if (!stats.isFile() || stats.size > maxBytes) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const text = safeReadFileSync(file, "utf-8");
|
|
65
|
+
// A file that grew between the stat and the read is over the cap too.
|
|
66
|
+
return typeof text === "string" && text.length <= maxBytes ? text : undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function newRecord() {
|
|
70
|
+
return { fields: {}, fileCount: 0, files: [], touched: false };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Parse the text of an installed database.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} text Database text
|
|
77
|
+
* @param {Set<string>|undefined} wantedFiles Absolute paths whose owner is wanted
|
|
78
|
+
* @param {number} maxPackages Most packages to accept
|
|
79
|
+
* @returns {{packages: Object[], malformed: number, overflow: boolean}}
|
|
80
|
+
*/
|
|
81
|
+
function parseDatabase(text, wantedFiles, maxPackages) {
|
|
82
|
+
const packages = [];
|
|
83
|
+
let malformed = 0;
|
|
84
|
+
let overflow = false;
|
|
85
|
+
let record = newRecord();
|
|
86
|
+
let directory = "";
|
|
87
|
+
const finishRecord = () => {
|
|
88
|
+
const { fields, fileCount, files, touched } = record;
|
|
89
|
+
record = newRecord();
|
|
90
|
+
directory = "";
|
|
91
|
+
if (!touched) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// A package with no name or no version cannot be named by a purl.
|
|
95
|
+
if (!fields.name || !fields.version) {
|
|
96
|
+
malformed++;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
packages.push({ ...fields, fileCount, files });
|
|
100
|
+
if (packages.length > maxPackages) {
|
|
101
|
+
overflow = true;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
let position = 0;
|
|
105
|
+
while (position <= text.length && !overflow) {
|
|
106
|
+
let end = text.indexOf("\n", position);
|
|
107
|
+
if (end === -1) {
|
|
108
|
+
end = text.length;
|
|
109
|
+
}
|
|
110
|
+
let line = text.slice(position, end);
|
|
111
|
+
position = end + 1;
|
|
112
|
+
if (line.endsWith("\r")) {
|
|
113
|
+
line = line.slice(0, -1);
|
|
114
|
+
}
|
|
115
|
+
if (line === "") {
|
|
116
|
+
finishRecord();
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (line.length < 2 || line[1] !== ":") {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const letter = line[0];
|
|
123
|
+
const value = line.slice(2);
|
|
124
|
+
if (Object.hasOwn(FIELD_NAMES, letter)) {
|
|
125
|
+
record.touched = true;
|
|
126
|
+
if (value) {
|
|
127
|
+
record.fields[FIELD_NAMES[letter]] =
|
|
128
|
+
value.length > MAX_FIELD_LENGTH
|
|
129
|
+
? value.slice(0, MAX_FIELD_LENGTH)
|
|
130
|
+
: value;
|
|
131
|
+
}
|
|
132
|
+
} else if (letter === "F") {
|
|
133
|
+
record.touched = true;
|
|
134
|
+
directory = value;
|
|
135
|
+
} else if (letter === "R") {
|
|
136
|
+
record.touched = true;
|
|
137
|
+
record.fileCount++;
|
|
138
|
+
if (wantedFiles) {
|
|
139
|
+
const file = `/${directory ? `${directory}/` : ""}${value}`;
|
|
140
|
+
if (wantedFiles.has(file)) {
|
|
141
|
+
record.files.push(file);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!overflow) {
|
|
147
|
+
finishRecord();
|
|
148
|
+
}
|
|
149
|
+
return { packages, malformed, overflow };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Read the packages of the apk installed database.
|
|
154
|
+
*
|
|
155
|
+
* @param {Object} [options] Read options
|
|
156
|
+
* @param {string} [options.rootPath="/"] Directory the database paths hang off;
|
|
157
|
+
* tests point it at a built tree
|
|
158
|
+
* @param {string} [options.databaseFile] Read this file instead of looking
|
|
159
|
+
* under `rootPath`
|
|
160
|
+
* @param {Set<string>} [options.wantedFiles] Absolute paths whose owner is
|
|
161
|
+
* wanted. A package comes back with the ones it owns in `files`; the other
|
|
162
|
+
* files of a package are only counted, so a large database stays small.
|
|
163
|
+
* @param {number} [options.maxBytes] Largest database to read, 64 MiB by default
|
|
164
|
+
* @param {number} [options.maxPackages] Most packages to accept, 100000 by default
|
|
165
|
+
* @param {string} [options.platform] `linux` by default for the process; an apk
|
|
166
|
+
* database exists on Linux only
|
|
167
|
+
* @returns {{state: string, path?: string, packages: Object[], malformed: number}}
|
|
168
|
+
* `state` is `ok`, `absent` (no database, or not Linux), `unreadable` (a
|
|
169
|
+
* database that could not be read in full) or `skipped` (a dry run).
|
|
170
|
+
* `malformed` counts the records left out for having no name or version.
|
|
171
|
+
* Each package has `name`, `version` and `fileCount`, `files`, and, when the
|
|
172
|
+
* record has them, `arch`, `origin`, `maintainer`, `license`, `url` and
|
|
173
|
+
* `description`.
|
|
174
|
+
*/
|
|
175
|
+
export function readApkInstalledDb(options = {}) {
|
|
176
|
+
const result = (state, extra = {}) => ({
|
|
177
|
+
state,
|
|
178
|
+
packages: [],
|
|
179
|
+
malformed: 0,
|
|
180
|
+
...extra,
|
|
181
|
+
});
|
|
182
|
+
const platform = options.platform || process.platform;
|
|
183
|
+
if (platform !== "linux") {
|
|
184
|
+
return result("absent");
|
|
185
|
+
}
|
|
186
|
+
const rootPath = options.rootPath || "/";
|
|
187
|
+
if (isDryRun) {
|
|
188
|
+
recordActivity({
|
|
189
|
+
capability: "apk-installed-db",
|
|
190
|
+
kind: "read",
|
|
191
|
+
reason:
|
|
192
|
+
"Dry run mode blocks reading the apk installed database, which lives outside the project.",
|
|
193
|
+
status: "blocked",
|
|
194
|
+
target: options.databaseFile || rootPath,
|
|
195
|
+
});
|
|
196
|
+
return result("skipped");
|
|
197
|
+
}
|
|
198
|
+
const candidates = options.databaseFile
|
|
199
|
+
? [options.databaseFile]
|
|
200
|
+
: APK_INSTALLED_DB_PATHS.map((location) => join(rootPath, location));
|
|
201
|
+
const file = candidates.find((candidate) => safeExistsSync(candidate));
|
|
202
|
+
if (!file) {
|
|
203
|
+
return result("absent");
|
|
204
|
+
}
|
|
205
|
+
const unreadable = (reason) => {
|
|
206
|
+
thoughtLog(`The apk installed database ${file} was not read: ${reason}.`);
|
|
207
|
+
recordActivity({
|
|
208
|
+
capability: "apk-installed-db",
|
|
209
|
+
kind: "read",
|
|
210
|
+
reason: `The apk installed database ${file} could not be read: ${reason}.`,
|
|
211
|
+
status: "failed",
|
|
212
|
+
target: file,
|
|
213
|
+
});
|
|
214
|
+
return result("unreadable", { path: file });
|
|
215
|
+
};
|
|
216
|
+
const maxBytes = options.maxBytes ?? MAX_APK_DATABASE_BYTES;
|
|
217
|
+
const text = readBounded(file, maxBytes);
|
|
218
|
+
if (text === undefined) {
|
|
219
|
+
return unreadable(`it is not a file of at most ${maxBytes} bytes`);
|
|
220
|
+
}
|
|
221
|
+
const maxPackages = options.maxPackages ?? MAX_APK_PACKAGES;
|
|
222
|
+
const { packages, malformed, overflow } = parseDatabase(
|
|
223
|
+
text,
|
|
224
|
+
options.wantedFiles,
|
|
225
|
+
maxPackages,
|
|
226
|
+
);
|
|
227
|
+
if (overflow) {
|
|
228
|
+
return unreadable(`it lists more than ${maxPackages} packages`);
|
|
229
|
+
}
|
|
230
|
+
if (malformed) {
|
|
231
|
+
thoughtLog(
|
|
232
|
+
`${malformed} record(s) of the apk installed database ${file} have no name or no version and were left out.`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
recordActivity({
|
|
236
|
+
capability: "apk-installed-db",
|
|
237
|
+
kind: "read",
|
|
238
|
+
reason: `Read ${packages.length} package(s) from the apk installed database ${file}.`,
|
|
239
|
+
status: "completed",
|
|
240
|
+
target: file,
|
|
241
|
+
});
|
|
242
|
+
return result("ok", { path: file, packages, malformed });
|
|
243
|
+
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { TRACE_MODE } from "../core/logger.js";
|
|
31
31
|
import { dirNameStr, isWin } from "../core/paths.js";
|
|
32
32
|
import { formatDuration, reapProcessTree } from "../core/processTree.js";
|
|
33
|
+
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
33
34
|
import { readJsonFile } from "../parsers/largeJson.js";
|
|
34
35
|
import { cmakeBuildDirCandidates } from "./cmakeBuildDirs.js";
|
|
35
36
|
|
|
@@ -182,6 +183,7 @@ export function findCompileCommands(src, options = {}) {
|
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
const atomFrontendArgKeysCache = new Map();
|
|
186
|
+
const atomOptionsCache = new Map();
|
|
185
187
|
|
|
186
188
|
/**
|
|
187
189
|
* Split an atom command that may carry a leading argument (`node
|
|
@@ -270,6 +272,43 @@ export function atomFrontendArgKeys(language) {
|
|
|
270
272
|
return keys;
|
|
271
273
|
}
|
|
272
274
|
|
|
275
|
+
/**
|
|
276
|
+
* The options the installed atom accepts, as `atom --help` lists them. Asked
|
|
277
|
+
* once per atom command. An atom that fails to start accepts none.
|
|
278
|
+
*
|
|
279
|
+
* @returns {Set<string>} Supported options, such as `--bom-file`
|
|
280
|
+
*/
|
|
281
|
+
export function atomOptions() {
|
|
282
|
+
const command = getAtomCommand();
|
|
283
|
+
if (atomOptionsCache.has(command)) {
|
|
284
|
+
return atomOptionsCache.get(command);
|
|
285
|
+
}
|
|
286
|
+
const [bin, leadingArg] = splitAtomCommand(command);
|
|
287
|
+
const args = ["--help"];
|
|
288
|
+
if (leadingArg !== undefined) {
|
|
289
|
+
args.unshift(leadingArg);
|
|
290
|
+
}
|
|
291
|
+
const env = { ...process.env };
|
|
292
|
+
if (readEnvironmentVariable("ATOM_JAVA_HOME")) {
|
|
293
|
+
env.JAVA_HOME = readEnvironmentVariable("ATOM_JAVA_HOME");
|
|
294
|
+
}
|
|
295
|
+
const options = new Set();
|
|
296
|
+
const result = safeSpawnSync(bin, args, {
|
|
297
|
+
shell: isWin,
|
|
298
|
+
env,
|
|
299
|
+
timeout: 120000,
|
|
300
|
+
});
|
|
301
|
+
if (result?.status === 0 && result.stdout) {
|
|
302
|
+
for (const match of `${result.stdout}`.matchAll(
|
|
303
|
+
/(?:^|\s)(--[a-z][a-z0-9-]*)/g,
|
|
304
|
+
)) {
|
|
305
|
+
options.add(match[1]);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
atomOptionsCache.set(command, options);
|
|
309
|
+
return options;
|
|
310
|
+
}
|
|
311
|
+
|
|
273
312
|
/**
|
|
274
313
|
* The `--frontend-args` atom takes for a C/C++ language: the project's
|
|
275
314
|
* compilation database, when there is one and the installed atom reads it
|
|
@@ -314,6 +353,77 @@ export function atomCompileCommandsArgs(src, language, options = {}) {
|
|
|
314
353
|
return ["--frontend-args", `compile-commands=${database}`];
|
|
315
354
|
}
|
|
316
355
|
|
|
356
|
+
/**
|
|
357
|
+
* The `--bom-file` a reachables slice takes package purls from: the BOM evinse
|
|
358
|
+
* annotates. atom would otherwise only find a BOM saved as `bom.json` inside
|
|
359
|
+
* the scanned directory, so a BOM written anywhere else gave every reachable
|
|
360
|
+
* flow no purl. An atom without `--bom-file` is given the arguments it has
|
|
361
|
+
* always been given, and still reads a `bom.json` in the scanned directory.
|
|
362
|
+
*
|
|
363
|
+
* @param {string} sliceType atom slice command
|
|
364
|
+
* @param {Object} options CLI options, for `input`
|
|
365
|
+
* @returns {string[]} Arguments to add to the atom command
|
|
366
|
+
*/
|
|
367
|
+
export function atomBomFileArgs(sliceType, options = {}) {
|
|
368
|
+
if (
|
|
369
|
+
sliceType !== "reachables" ||
|
|
370
|
+
!options?.input ||
|
|
371
|
+
!safeExistsSync(options.input)
|
|
372
|
+
) {
|
|
373
|
+
return [];
|
|
374
|
+
}
|
|
375
|
+
const bomFile = resolve(options.input);
|
|
376
|
+
// The argument reaches a Windows shell command line through `shell: isWin`
|
|
377
|
+
// in executeAtom, so a path a shell would interpret is never passed on.
|
|
378
|
+
if (UNSAFE_SHELL_CHARS_RE.test(bomFile)) {
|
|
379
|
+
console.warn(
|
|
380
|
+
`The BOM path ${bomFile} contains characters a shell would interpret. Reachable flows will carry purls only if a bom.json is in the scanned directory.`,
|
|
381
|
+
);
|
|
382
|
+
return [];
|
|
383
|
+
}
|
|
384
|
+
if (!atomOptions().has("--bom-file")) {
|
|
385
|
+
if (DEBUG_MODE) {
|
|
386
|
+
console.log(
|
|
387
|
+
"The installed atom does not take --bom-file. Reachable flows will carry purls only if a bom.json is in the scanned directory.",
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
return [];
|
|
391
|
+
}
|
|
392
|
+
return ["--bom-file", bomFile];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* The arguments that stop atom's Java frontend from running the scanned
|
|
397
|
+
* project's build. In its default delombok mode, atom runs
|
|
398
|
+
* `mvn dependency:build-classpath` inside a Maven project, which loads the
|
|
399
|
+
* project's `.mvn` configuration and build extensions, so delombok and
|
|
400
|
+
* dependency fetching are turned off whenever the project's own tooling may
|
|
401
|
+
* not run.
|
|
402
|
+
*
|
|
403
|
+
* @param {string} language atom `-l` value
|
|
404
|
+
* @param {Object} options CLI options
|
|
405
|
+
* @returns {string[]|undefined} Arguments to add to the atom command, or
|
|
406
|
+
* undefined when the installed atom cannot be told to leave the build alone
|
|
407
|
+
*/
|
|
408
|
+
export function atomBuildToolArgs(language, options = {}) {
|
|
409
|
+
if (
|
|
410
|
+
`${language}`.toLowerCase() !== "java" ||
|
|
411
|
+
isProjectToolingAllowed(options)
|
|
412
|
+
) {
|
|
413
|
+
return [];
|
|
414
|
+
}
|
|
415
|
+
const keys = atomFrontendArgKeys(language);
|
|
416
|
+
if (!keys.has("delombok-mode")) {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
return [
|
|
420
|
+
"--frontend-args",
|
|
421
|
+
keys.has("fetch-dependencies")
|
|
422
|
+
? "delombok-mode=no-delombok,fetch-dependencies=false"
|
|
423
|
+
: "delombok-mode=no-delombok",
|
|
424
|
+
];
|
|
425
|
+
}
|
|
426
|
+
|
|
317
427
|
// Absolute ceiling on atom's heap. Past this point a larger heap buys a
|
|
318
428
|
// collector that runs less often rather than an analysis that succeeds where it
|
|
319
429
|
// otherwise would not, while the reservation itself is what pushes a build
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shouldRunPredictiveBomAudit } from "../core/env.js";
|
|
1
|
+
import { getCommandName, shouldRunPredictiveBomAudit } from "../core/env.js";
|
|
2
2
|
|
|
3
3
|
/** Frozen list of hardware-bill-of-materials (HBOM) audit categories. */
|
|
4
4
|
export const HBOM_AUDIT_CATEGORIES = Object.freeze([
|
|
@@ -186,11 +186,11 @@ export function validateBomAuditCategories(categories, rules) {
|
|
|
186
186
|
* @returns {string | undefined} Default category string, if any
|
|
187
187
|
*/
|
|
188
188
|
export function getDefaultBomAuditCategories(options, commandPath) {
|
|
189
|
-
const
|
|
189
|
+
const commandName = getCommandName(commandPath);
|
|
190
190
|
const defaultHbomCategories = options?.includeRuntime
|
|
191
191
|
? `${DEFAULT_HBOM_AUDIT_CATEGORIES},host-topology`
|
|
192
192
|
: DEFAULT_HBOM_AUDIT_CATEGORIES;
|
|
193
|
-
if (
|
|
193
|
+
if (commandName.startsWith("hbom")) {
|
|
194
194
|
return defaultHbomCategories;
|
|
195
195
|
}
|
|
196
196
|
const projectTypes = Array.isArray(options?.projectType)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// BOM components for the Brotli decoders compiled into native files. The
|
|
2
|
+
// detection itself, and why its markers can be trusted, is in
|
|
3
|
+
// lib/core/brotliProbe.js.
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
MAX_BROTLI_PROBE_BYTES,
|
|
7
|
+
MAX_BROTLI_PROBES,
|
|
8
|
+
readBrotliDecoder,
|
|
9
|
+
} from "../core/brotliProbe.js";
|
|
10
|
+
import { applyPurl, tryBuildPurl } from "./purl.js";
|
|
11
|
+
|
|
12
|
+
export { MAX_BROTLI_PROBE_BYTES, MAX_BROTLI_PROBES, readBrotliDecoder };
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The properties a file component carries once it has been probed: the fix
|
|
16
|
+
* state of each decoder found in it, or why it could not be read.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} decoder result of readBrotliDecoder
|
|
19
|
+
* @returns {{name: string, value: string}[]} properties to append
|
|
20
|
+
*/
|
|
21
|
+
export function brotliDecoderProperties(decoder) {
|
|
22
|
+
const properties = [];
|
|
23
|
+
if (decoder?.skipped) {
|
|
24
|
+
properties.push({ name: "cdx:brotli:probe", value: decoder.skipped });
|
|
25
|
+
}
|
|
26
|
+
if (decoder?.c) {
|
|
27
|
+
properties.push(
|
|
28
|
+
{ name: "cdx:brotli:c:blockSwitchFix", value: decoder.c.blockSwitchFix },
|
|
29
|
+
{ name: "cdx:brotli:c:evidence", value: decoder.c.evidence },
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (decoder?.go) {
|
|
33
|
+
properties.push({
|
|
34
|
+
name: "cdx:brotli:go:blockSwitchFix",
|
|
35
|
+
value: decoder.go.blockSwitchFix,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return properties;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The library component every embedded decoder links to. It has no version:
|
|
43
|
+
* no build records one, and only the per-file cdx:brotli:*:blockSwitchFix
|
|
44
|
+
* properties say whether a copy is vulnerable. The `vcs_url` qualifier names
|
|
45
|
+
* the project and keeps the identity apart from an unpackaged `brotli`
|
|
46
|
+
* executable, which is `pkg:generic/brotli`. The purl has no namespace: a
|
|
47
|
+
* `pkg:github` purl is typed as an application and `pkg:generic/google` as a
|
|
48
|
+
* framework when the BOM is assembled, and this is neither.
|
|
49
|
+
*
|
|
50
|
+
* @returns {Object} component with `purl` and `bom-ref`
|
|
51
|
+
*/
|
|
52
|
+
export function createBrotliComponent() {
|
|
53
|
+
return applyPurl(
|
|
54
|
+
{
|
|
55
|
+
type: "library",
|
|
56
|
+
name: "brotli",
|
|
57
|
+
description:
|
|
58
|
+
"Brotli decoder compiled into native files; see the cdx:brotli properties on each file",
|
|
59
|
+
properties: [],
|
|
60
|
+
},
|
|
61
|
+
tryBuildPurl({
|
|
62
|
+
type: "generic",
|
|
63
|
+
name: "brotli",
|
|
64
|
+
qualifiers: { vcs_url: "git+https://github.com/google/brotli" },
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Start collecting the decoder copies of one scan.
|
|
71
|
+
*
|
|
72
|
+
* @returns {{component: Object|undefined, dependencies: Object[], copies: number, unpatched: number, unknown: number}}
|
|
73
|
+
*/
|
|
74
|
+
export function createBrotliInventory() {
|
|
75
|
+
return {
|
|
76
|
+
component: undefined,
|
|
77
|
+
dependencies: [],
|
|
78
|
+
copies: 0,
|
|
79
|
+
unpatched: 0,
|
|
80
|
+
unknown: 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Tag a probed file component and, when it holds a decoder, link it to the
|
|
86
|
+
* shared brotli component, creating that component on first use. Mutates the
|
|
87
|
+
* file component in place.
|
|
88
|
+
*
|
|
89
|
+
* @param {Object} inventory accumulator from createBrotliInventory
|
|
90
|
+
* @param {Object} fileComponent file component with a `properties` array
|
|
91
|
+
* @param {Object} decoder result of readBrotliDecoder
|
|
92
|
+
*/
|
|
93
|
+
export function attachBrotliDecoder(inventory, fileComponent, decoder) {
|
|
94
|
+
const properties = brotliDecoderProperties(decoder);
|
|
95
|
+
if (!properties.length) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
fileComponent.properties.push(...properties);
|
|
99
|
+
if (!decoder.c && !decoder.go) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!inventory.component) {
|
|
103
|
+
inventory.component = createBrotliComponent();
|
|
104
|
+
}
|
|
105
|
+
const states = [decoder.c?.blockSwitchFix, decoder.go?.blockSwitchFix];
|
|
106
|
+
inventory.copies += 1;
|
|
107
|
+
if (states.includes("false")) {
|
|
108
|
+
inventory.unpatched += 1;
|
|
109
|
+
} else if (states.includes("unknown")) {
|
|
110
|
+
inventory.unknown += 1;
|
|
111
|
+
}
|
|
112
|
+
if (fileComponent["bom-ref"] && inventory.component["bom-ref"]) {
|
|
113
|
+
inventory.dependencies.push({
|
|
114
|
+
ref: fileComponent["bom-ref"],
|
|
115
|
+
dependsOn: [inventory.component["bom-ref"]],
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Finish an inventory: record the copy counts on the brotli component.
|
|
122
|
+
*
|
|
123
|
+
* @param {Object} inventory accumulator from createBrotliInventory
|
|
124
|
+
* @returns {{components: Object[], dependencies: Object[]}} what to add to the BOM
|
|
125
|
+
*/
|
|
126
|
+
export function finishBrotliInventory(inventory) {
|
|
127
|
+
if (!inventory?.component) {
|
|
128
|
+
return { components: [], dependencies: [] };
|
|
129
|
+
}
|
|
130
|
+
inventory.component.properties = [
|
|
131
|
+
{ name: "cdx:brotli:embeddedCopies", value: String(inventory.copies) },
|
|
132
|
+
{ name: "cdx:brotli:unpatchedCopies", value: String(inventory.unpatched) },
|
|
133
|
+
{ name: "cdx:brotli:unknownCopies", value: String(inventory.unknown) },
|
|
134
|
+
];
|
|
135
|
+
return {
|
|
136
|
+
components: [inventory.component],
|
|
137
|
+
dependencies: [
|
|
138
|
+
{ ref: inventory.component["bom-ref"], dependsOn: [] },
|
|
139
|
+
...inventory.dependencies,
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -129,7 +129,7 @@ export function noteBuildToolRateLimit(tool, output, context = {}) {
|
|
|
129
129
|
? `, so cdxgen runs no further ${tool} resolution in this scan and`
|
|
130
130
|
: ", so";
|
|
131
131
|
console.warn(
|
|
132
|
-
`The repository ${tool} downloads from is rate limiting this machine (HTTP 429)${stopped} its dependency tree is incomplete. ${MIRROR_SETTINGS[tool] || MIRROR_SETTINGS[TOOL_MATCHER[tool]]}
|
|
132
|
+
`The repository ${tool} downloads from is rate limiting this machine (HTTP 429)${stopped} its dependency tree is incomplete. ${MIRROR_SETTINGS[tool] || MIRROR_SETTINGS[TOOL_MATCHER[tool]]} Set MAVEN_CENTRAL_MIRROR=true to point every build tool at Google's Maven Central mirror, or MAVEN_CENTRAL_URL for cdxgen's own lookups.`,
|
|
133
133
|
);
|
|
134
134
|
recordDegradation("build.rate-limited", {
|
|
135
135
|
ecosystem: "java",
|
|
@@ -28,13 +28,21 @@ import { isMac, isWin } from "../core/paths.js";
|
|
|
28
28
|
* rather than reading `isWin`/`isMac` here, or the Windows and Linux branches
|
|
29
29
|
* become untestable on a macOS developer machine.
|
|
30
30
|
*
|
|
31
|
+
* Every environment-derived root must be absolute by the *target* platform's
|
|
32
|
+
* rules, never the host's (`path.isAbsolute` would accept `\cache` on a
|
|
33
|
+
* Windows host): a relative value is ignored, so it cannot point inside the
|
|
34
|
+
* working directory. XDG requires this of `XDG_CACHE_HOME`; the home and
|
|
35
|
+
* `LOCALAPPDATA` roots get the same treatment for the same reason.
|
|
36
|
+
*
|
|
31
37
|
* @param {Object} inputs Resolution inputs.
|
|
32
38
|
* @param {"linux"|"macos"|"windows"} inputs.platform Which convention to apply.
|
|
33
|
-
* @param {string} [inputs.cdxgenCacheDir] `CDXGEN_CACHE_DIR
|
|
39
|
+
* @param {string} [inputs.cdxgenCacheDir] `CDXGEN_CACHE_DIR`, an explicit
|
|
40
|
+
* override taken as given.
|
|
34
41
|
* @param {string} [inputs.xdgCacheHome] `XDG_CACHE_HOME`. Ignored unless
|
|
35
|
-
*
|
|
36
|
-
* @param {string} [inputs.home] Home directory.
|
|
37
|
-
* @param {string} [inputs.localAppData] `LOCALAPPDATA`.
|
|
42
|
+
* `/`-rooted.
|
|
43
|
+
* @param {string} [inputs.home] Home directory. Ignored unless absolute.
|
|
44
|
+
* @param {string} [inputs.localAppData] `LOCALAPPDATA`. Ignored unless drive-
|
|
45
|
+
* or UNC-rooted; the home profile is the fallback.
|
|
38
46
|
* @returns {string|null} Absolute path, or null when nothing resolves.
|
|
39
47
|
*/
|
|
40
48
|
export function resolveCacheDirFor({
|
|
@@ -48,20 +56,55 @@ export function resolveCacheDirFor({
|
|
|
48
56
|
return path.resolve(cdxgenCacheDir);
|
|
49
57
|
}
|
|
50
58
|
if (platform === "windows") {
|
|
51
|
-
if (localAppData
|
|
59
|
+
if (isWindowsAbsolute(localAppData)) {
|
|
52
60
|
return path.join(localAppData, "cdxgen", "cache");
|
|
53
61
|
}
|
|
54
|
-
return home
|
|
62
|
+
return isWindowsAbsolute(home)
|
|
55
63
|
? path.join(home, "AppData", "Local", "cdxgen", "cache")
|
|
56
64
|
: null;
|
|
57
65
|
}
|
|
58
66
|
if (platform === "macos") {
|
|
59
|
-
return
|
|
67
|
+
return isPosixAbsolute(home)
|
|
68
|
+
? path.join(home, "Library", "Caches", "cdxgen")
|
|
69
|
+
: null;
|
|
60
70
|
}
|
|
61
|
-
if (
|
|
71
|
+
if (isPosixAbsolute(xdgCacheHome)) {
|
|
62
72
|
return path.join(xdgCacheHome, "cdxgen");
|
|
63
73
|
}
|
|
64
|
-
return home
|
|
74
|
+
return isPosixAbsolute(home) ? path.join(home, ".cache", "cdxgen") : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Absolute by POSIX rules: `/`-rooted.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} [value] Candidate path
|
|
81
|
+
* @returns {boolean} Whether the path is absolute
|
|
82
|
+
*/
|
|
83
|
+
function isPosixAbsolute(value) {
|
|
84
|
+
return typeof value === "string" && value.startsWith("/");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Absolute by Windows rules: a drive letter followed by a separator (`C:\` or
|
|
89
|
+
* `C:/`), or a UNC path (`\\server\share`). `C:cache` (relative to the drive's
|
|
90
|
+
* current directory) and `\cache` (relative to the current drive) are not.
|
|
91
|
+
* Mirrors `is_windows_absolute` in cdxrs.
|
|
92
|
+
*
|
|
93
|
+
* @param {string} [value] Candidate path
|
|
94
|
+
* @returns {boolean} Whether the path is absolute
|
|
95
|
+
*/
|
|
96
|
+
function isWindowsAbsolute(value) {
|
|
97
|
+
if (typeof value !== "string" || value.length < 3) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const isSeparator = (char) => char === "\\" || char === "/";
|
|
101
|
+
const driveRooted = /^[a-zA-Z]$/.test(value[0]) && value[1] === ":";
|
|
102
|
+
if (driveRooted) {
|
|
103
|
+
return isSeparator(value[2]);
|
|
104
|
+
}
|
|
105
|
+
return (
|
|
106
|
+
isSeparator(value[0]) && isSeparator(value[1]) && !isSeparator(value[2])
|
|
107
|
+
);
|
|
65
108
|
}
|
|
66
109
|
|
|
67
110
|
/**
|