@cdxgen/cdxgen 13.5.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 +6 -6
- package/bin/cdxgen.js +4 -0
- package/bin/evinse.js +3 -0
- package/bin/validate.js +6 -0
- package/data/license-aliases.json +24 -2
- package/data/remediations.json +124 -0
- package/data/rules/ai-bom.yaml +1 -1
- package/data/rules/brotli-decoder.yaml +107 -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/cli/bomAssembly.js +92 -3
- package/lib/cli/index.js +108 -1
- package/lib/cli/jsBom.js +153 -64
- package/lib/cli/jvmBom.js +237 -46
- package/lib/cli/managedBom.js +142 -16
- package/lib/cli/nativeBom.js +42 -5
- package/lib/core/boundedFile.js +344 -0
- package/lib/core/brotliProbe.js +182 -0
- package/lib/core/env.js +8 -0
- package/lib/core/fs.js +12 -0
- package/lib/core/httpClient.js +98 -1
- package/lib/core/lockPaths.js +38 -0
- package/lib/core/paths.js +24 -1
- package/lib/core/processTree.js +5 -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/gradleutils.js +828 -224
- package/lib/ecosystems/parsers-bazel.js +107 -6
- package/lib/ecosystems/parsers-js.js +505 -22
- package/lib/ecosystems/parsers-misc.js +209 -20
- package/lib/ecosystems/parsers-python.js +89 -2
- package/lib/ecosystems/pythonutils.js +128 -2
- package/lib/ecosystems/rubyutils.js +538 -31
- package/lib/ecosystems/scalacliutils.js +2 -0
- package/lib/ecosystems/scalasem.js +796 -151
- package/lib/ecosystems/vendoredCode.js +237 -3
- package/lib/ecosystems/venvProbe.js +360 -0
- package/lib/evinser/evinser.js +346 -43
- package/lib/helpers/agenticToolutils.js +413 -93
- package/lib/helpers/vsixutils.js +1574 -315
- package/lib/inventory/atomUtils.js +77 -0
- package/lib/inventory/brotliDecoder.js +142 -0
- package/lib/inventory/buildToolRateLimit.js +1 -1
- package/lib/inventory/cbomutils.js +56 -7
- package/lib/inventory/deps.js +35 -0
- package/lib/inventory/dosai.js +139 -5
- package/lib/inventory/dotnetRuntime.js +217 -0
- package/lib/inventory/envcontext.js +307 -13
- package/lib/inventory/evidenceUtils.js +11 -0
- package/lib/inventory/fetchBatch.js +1 -1
- package/lib/inventory/formulationParsers.js +31 -3
- package/lib/inventory/gtfobins.js +13 -0
- package/lib/inventory/hostRuntimeProbe.js +686 -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/osPackageResolver.js +182 -0
- package/lib/inventory/phpBuildInfo.js +10 -59
- package/lib/inventory/phpHostProbe.js +32 -488
- 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 +49 -2
- package/lib/managers/binary.js +337 -3
- package/lib/managers/docker.js +79 -0
- package/lib/parsers/largeJson.js +28 -11
- package/lib/parsers/vscodeExtensionId.js +183 -0
- package/lib/server/server.js +12 -1
- package/lib/stages/postgen/ruleEngine.js +13 -5
- package/lib/stages/pregen/envAudit.js +1 -0
- package/lib/stages/pregen/pregen.js +114 -57
- package/lib/validator/bomValidator.js +6 -1
- package/lib/validator/generated/validate-1.6.mjs +1 -1
- package/lib/validator/generated/validate-1.7.mjs +1 -1
- package/lib/validator/generated/validate-2.0.mjs +1 -1
- package/lib/validator/index.js +1 -1
- package/package.json +3 -2
- package/types/lib/cli/bomAssembly.d.ts +13 -1
- package/types/lib/cli/bomAssembly.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.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 +4 -0
- 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/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/gradleutils.d.ts +47 -13
- 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-js.d.ts +17 -20
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +10 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +20 -2
- 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/scalacliutils.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 +63 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +19 -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/cbomutils.d.ts +17 -0
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +17 -1
- 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/envcontext.d.ts +52 -6
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/gtfobins.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/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/osPackageResolver.d.ts +38 -0
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
- 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 +10 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/largeJson.d.ts +4 -3
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +12 -8
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// Bounded, read-only inventory of the gems installed on a live host, for
|
|
2
|
+
// `-t os` / obom. osquery has no table for RubyGems, so without this an OBOM
|
|
3
|
+
// lists no gems at all: neither the default gems that ship with each Ruby
|
|
4
|
+
// (resolv, net-http, uri, ...), nor what `gem install` and the distro's
|
|
5
|
+
// ruby-* packages put in the system gem homes, nor the gems of the version
|
|
6
|
+
// manager installs.
|
|
7
|
+
//
|
|
8
|
+
// Gem homes are found in fixed places, one wildcard level at a time: a
|
|
9
|
+
// directory holding a `specifications` directory is a gem home. Only the
|
|
10
|
+
// `*.gemspec` files under specifications/ and specifications/default/ are
|
|
11
|
+
// read, statically; nothing is executed. A gemspec a package database lists is
|
|
12
|
+
// marked with `cdx:os:installedBy`, so a consumer judges it by that package,
|
|
13
|
+
// which may carry backported fixes under the same gem version.
|
|
14
|
+
|
|
15
|
+
import { readdirSync, statSync } from "node:fs";
|
|
16
|
+
import { join, posix, relative, sep, win32 } from "node:path";
|
|
17
|
+
import process from "node:process";
|
|
18
|
+
|
|
19
|
+
import { isDryRun, recordActivity } from "../core/activity.js";
|
|
20
|
+
import { readRegularTextFile } from "../core/boundedFile.js";
|
|
21
|
+
import { safeExistsSync } from "../core/fs.js";
|
|
22
|
+
import { thoughtLog } from "../core/logger.js";
|
|
23
|
+
import { findHostPathOwners } from "../inventory/hostRuntimeProbe.js";
|
|
24
|
+
import {
|
|
25
|
+
expandDirectories,
|
|
26
|
+
homeDirectories,
|
|
27
|
+
windowsRubyRoots,
|
|
28
|
+
} from "../inventory/rubyHostProbe.js";
|
|
29
|
+
import { parseGemspecData } from "./rubyutils.js";
|
|
30
|
+
|
|
31
|
+
const MAX_GEM_HOMES = 128;
|
|
32
|
+
const MAX_GEMSPECS = 8192;
|
|
33
|
+
const MAX_GEMSPEC_BYTES = 1024 * 1024;
|
|
34
|
+
|
|
35
|
+
// Gem homes relative to the root.
|
|
36
|
+
const ROOT_GEM_HOMES = {
|
|
37
|
+
linux: [
|
|
38
|
+
"usr/lib/ruby/gems/*",
|
|
39
|
+
"usr/lib64/ruby/gems/*",
|
|
40
|
+
"usr/share/gems",
|
|
41
|
+
// Debian and Ubuntu ruby-* packages register their gems here
|
|
42
|
+
"usr/share/rubygems-integration/*",
|
|
43
|
+
"usr/lib/*/rubygems-integration/*",
|
|
44
|
+
"usr/local/share/gems",
|
|
45
|
+
"var/lib/gems/*",
|
|
46
|
+
"usr/local/lib/ruby/gems/*",
|
|
47
|
+
"usr/local/bundle",
|
|
48
|
+
"opt/*/lib/ruby/gems/*",
|
|
49
|
+
"opt/rubies/*/lib/ruby/gems/*",
|
|
50
|
+
"opt/hostedtoolcache/Ruby/*/*/lib/ruby/gems/*",
|
|
51
|
+
"usr/local/rbenv/versions/*/lib/ruby/gems/*",
|
|
52
|
+
"usr/local/rvm/rubies/*/lib/ruby/gems/*",
|
|
53
|
+
"usr/local/rvm/gems/*",
|
|
54
|
+
],
|
|
55
|
+
darwin: [
|
|
56
|
+
"Library/Ruby/Gems/*",
|
|
57
|
+
"usr/local/lib/ruby/gems/*",
|
|
58
|
+
"opt/homebrew/lib/ruby/gems/*",
|
|
59
|
+
"opt/*/lib/ruby/gems/*",
|
|
60
|
+
"opt/rubies/*/lib/ruby/gems/*",
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Gem homes relative to a home directory.
|
|
65
|
+
const USER_GEM_HOMES = [
|
|
66
|
+
".gem/ruby/*",
|
|
67
|
+
".local/share/gem/ruby/*",
|
|
68
|
+
".rbenv/versions/*/lib/ruby/gems/*",
|
|
69
|
+
".rvm/rubies/*/lib/ruby/gems/*",
|
|
70
|
+
".rvm/gems/*",
|
|
71
|
+
".asdf/installs/ruby/*/lib/ruby/gems/*",
|
|
72
|
+
".local/share/mise/installs/ruby/*/lib/ruby/gems/*",
|
|
73
|
+
".rubies/*/lib/ruby/gems/*",
|
|
74
|
+
".frum/versions/*/lib/ruby/gems/*",
|
|
75
|
+
".local/share/rv/rubies/*/lib/ruby/gems/*",
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
function listGemspecs(directory) {
|
|
79
|
+
try {
|
|
80
|
+
return readdirSync(directory)
|
|
81
|
+
.filter((name) => name.endsWith(".gemspec"))
|
|
82
|
+
.sort();
|
|
83
|
+
} catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isDirectory(path) {
|
|
89
|
+
try {
|
|
90
|
+
return statSync(path).isDirectory();
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The gem homes of a host, each a directory with a `specifications` directory.
|
|
98
|
+
*
|
|
99
|
+
* @param {string} rootPath root directory
|
|
100
|
+
* @param {string} platform `linux`, `darwin` or `win32`
|
|
101
|
+
* @returns {string[]} absolute gem home directories
|
|
102
|
+
*/
|
|
103
|
+
export function findGemHomes(rootPath, platform) {
|
|
104
|
+
const context = { rootPath, platform };
|
|
105
|
+
const candidates = [];
|
|
106
|
+
if (platform === "win32") {
|
|
107
|
+
for (const root of windowsRubyRoots(context)) {
|
|
108
|
+
candidates.push(...expandDirectories(root, "lib/ruby/gems/*"));
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
for (const pattern of ROOT_GEM_HOMES[platform] || []) {
|
|
112
|
+
candidates.push(...expandDirectories(rootPath, pattern));
|
|
113
|
+
}
|
|
114
|
+
for (const home of homeDirectories(context)) {
|
|
115
|
+
for (const pattern of USER_GEM_HOMES) {
|
|
116
|
+
candidates.push(...expandDirectories(home, pattern));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const homes = [];
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
for (const candidate of candidates) {
|
|
123
|
+
if (
|
|
124
|
+
seen.has(candidate) ||
|
|
125
|
+
!isDirectory(join(candidate, "specifications"))
|
|
126
|
+
) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
seen.add(candidate);
|
|
130
|
+
homes.push(candidate);
|
|
131
|
+
if (homes.length >= MAX_GEM_HOMES) {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return homes;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// The path as the host names it, for the package databases and evidence.
|
|
139
|
+
function logicalPath(rootPath, platform, absolutePath) {
|
|
140
|
+
const parts = relative(rootPath, absolutePath).split(sep);
|
|
141
|
+
if (platform === "win32") {
|
|
142
|
+
return win32.join(rootPath, ...parts);
|
|
143
|
+
}
|
|
144
|
+
return posix.join("/", ...parts);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Inventory the gems of a host's gem homes. One component per distinct gem
|
|
149
|
+
* and version, with an occurrence for each gemspec it was read from, so no
|
|
150
|
+
* host path reaches a purl.
|
|
151
|
+
*
|
|
152
|
+
* @param {Object} [options] options
|
|
153
|
+
* @param {string} [options.rootPath="/"] root directory; tests point it at a built tree
|
|
154
|
+
* @param {string} [options.platform] `linux`, `darwin` or `win32`
|
|
155
|
+
* @param {function(string): (Object[]|undefined)} [options.executeOsQuery] runs
|
|
156
|
+
* an osquery query, for rpm ownership
|
|
157
|
+
* @param {Object[]} [options.packageComponents] the OS packages the scan has
|
|
158
|
+
* inventoried, which name the owners
|
|
159
|
+
* @param {Object} [options.packageDatabases] where the package databases are
|
|
160
|
+
* @returns {Promise<Object[]>} gem components
|
|
161
|
+
*/
|
|
162
|
+
export async function probeHostRubyGems(options = {}) {
|
|
163
|
+
const platform = options.platform || process.platform;
|
|
164
|
+
const rootPath =
|
|
165
|
+
options.rootPath ||
|
|
166
|
+
(platform === "win32" ? `${process.env.SystemDrive || "C:"}\\` : "/");
|
|
167
|
+
if (isDryRun) {
|
|
168
|
+
recordActivity({
|
|
169
|
+
capability: "ruby-gem-probe",
|
|
170
|
+
kind: "read",
|
|
171
|
+
reason:
|
|
172
|
+
"Dry run mode blocks the RubyGems probe, which reads gem specifications outside the project.",
|
|
173
|
+
status: "blocked",
|
|
174
|
+
target: rootPath,
|
|
175
|
+
});
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
if (!safeExistsSync(rootPath)) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
const homes = findGemHomes(rootPath, platform);
|
|
182
|
+
const gemspecs = [];
|
|
183
|
+
for (const home of homes) {
|
|
184
|
+
for (const subdir of [
|
|
185
|
+
"specifications",
|
|
186
|
+
join("specifications", "default"),
|
|
187
|
+
]) {
|
|
188
|
+
const directory = join(home, subdir);
|
|
189
|
+
for (const name of listGemspecs(directory)) {
|
|
190
|
+
if (gemspecs.length >= MAX_GEMSPECS) {
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
const absolutePath = join(directory, name);
|
|
194
|
+
gemspecs.push({
|
|
195
|
+
absolutePath,
|
|
196
|
+
logical: logicalPath(rootPath, platform, absolutePath),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!gemspecs.length) {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
const { owners, unreadable } = findHostPathOwners(
|
|
205
|
+
{ ...options, platform, rootPath },
|
|
206
|
+
gemspecs.map((gemspec) => gemspec.logical),
|
|
207
|
+
);
|
|
208
|
+
// An OBOM names a package once; its purl says how the distro named it.
|
|
209
|
+
const packagePurls = new Map();
|
|
210
|
+
for (const component of options.packageComponents || []) {
|
|
211
|
+
if (
|
|
212
|
+
component?.name &&
|
|
213
|
+
typeof component.purl === "string" &&
|
|
214
|
+
/^pkg:(deb|rpm|apk)\//.test(component.purl) &&
|
|
215
|
+
!packagePurls.has(component.name)
|
|
216
|
+
) {
|
|
217
|
+
packagePurls.set(component.name, decodeURIComponent(component.purl));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const byRef = new Map();
|
|
221
|
+
for (const gemspec of gemspecs) {
|
|
222
|
+
// Only a regular file under the root, within the cap: any user can leave
|
|
223
|
+
// `~/.gem/ruby/*/specifications/x.gemspec -> /dev/zero` for a root scan.
|
|
224
|
+
const text = readRegularTextFile(
|
|
225
|
+
gemspec.absolutePath,
|
|
226
|
+
MAX_GEMSPEC_BYTES,
|
|
227
|
+
rootPath,
|
|
228
|
+
);
|
|
229
|
+
if (typeof text !== "string") {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
// The gemspec files RubyGems writes on install are Ruby source, read here
|
|
233
|
+
// without evaluating them. Thousands of them are no reason to ask the
|
|
234
|
+
// registry about each.
|
|
235
|
+
const [pkg] = await parseGemspecData(text, gemspec.logical, {
|
|
236
|
+
noFetch: true,
|
|
237
|
+
});
|
|
238
|
+
if (!pkg?.name || !pkg.version || !pkg["bom-ref"]) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const owner = owners.get(gemspec.logical);
|
|
242
|
+
// cdx:os:installedBy holds a purl, as everywhere else; an owner the
|
|
243
|
+
// OBOM did not list cannot be named by one.
|
|
244
|
+
const ownerRef = owner ? packagePurls.get(owner.packageName) : undefined;
|
|
245
|
+
let component = byRef.get(pkg["bom-ref"]);
|
|
246
|
+
if (!component) {
|
|
247
|
+
component = pkg;
|
|
248
|
+
component.evidence = {
|
|
249
|
+
...(component.evidence || {}),
|
|
250
|
+
occurrences: [],
|
|
251
|
+
};
|
|
252
|
+
byRef.set(pkg["bom-ref"], component);
|
|
253
|
+
}
|
|
254
|
+
component.properties = component.properties || [];
|
|
255
|
+
if (
|
|
256
|
+
ownerRef &&
|
|
257
|
+
!component.properties.some(
|
|
258
|
+
(prop) => prop.name === "cdx:os:installedBy" && prop.value === ownerRef,
|
|
259
|
+
)
|
|
260
|
+
) {
|
|
261
|
+
component.properties.push({
|
|
262
|
+
name: "cdx:os:installedBy",
|
|
263
|
+
value: ownerRef,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (
|
|
267
|
+
!component.evidence.occurrences.some(
|
|
268
|
+
(occurrence) => occurrence.location === gemspec.logical,
|
|
269
|
+
)
|
|
270
|
+
) {
|
|
271
|
+
component.evidence.occurrences.push({ location: gemspec.logical });
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// A gem a package owns in one gem home and `gem install` put in another is
|
|
275
|
+
// not wholly that package's, so it keeps no owner.
|
|
276
|
+
for (const component of byRef.values()) {
|
|
277
|
+
const owned = component.evidence.occurrences.every((occurrence) =>
|
|
278
|
+
owners.has(occurrence.location),
|
|
279
|
+
);
|
|
280
|
+
if (!owned) {
|
|
281
|
+
component.properties = component.properties.filter(
|
|
282
|
+
(prop) => prop.name !== "cdx:os:installedBy",
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
recordActivity({
|
|
287
|
+
capability: "ruby-gem-probe",
|
|
288
|
+
kind: "read",
|
|
289
|
+
reason: `Read ${gemspecs.length} gemspec(s) from ${homes.length} gem home(s) in the fixed places${unreadable.length ? `; the ${unreadable.join(", ")} database could not be read in full, so gem ownership is incomplete` : ""}.`,
|
|
290
|
+
status: "completed",
|
|
291
|
+
target: rootPath,
|
|
292
|
+
});
|
|
293
|
+
if (byRef.size) {
|
|
294
|
+
thoughtLog(
|
|
295
|
+
`I found ${byRef.size} gem(s) in ${homes.length} gem home(s) on this host.`,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
return Array.from(byRef.values()).sort((a, b) =>
|
|
299
|
+
a["bom-ref"] < b["bom-ref"] ? -1 : a["bom-ref"] > b["bom-ref"] ? 1 : 0,
|
|
300
|
+
);
|
|
301
|
+
}
|