@cdxgen/cdxgen 13.4.0 → 13.5.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 +7 -7
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +25 -5
- package/bin/evinse.js +5 -0
- package/bin/hbom.js +15 -7
- package/bin/repl.js +29 -0
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +283 -1
- 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/lib/audit/index.js +14 -0
- package/lib/cli/bomAssembly.js +28 -3
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +73 -4
- package/lib/cli/jvmBom.js +126 -20
- package/lib/cli/managedBom.js +711 -71
- package/lib/cli/nativeBom.js +225 -28
- package/lib/core/env.js +30 -8
- package/lib/core/fs.js +28 -19
- package/lib/core/httpClient.js +74 -45
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- package/lib/ecosystems/golem.js +550 -6
- package/lib/ecosystems/gradleutils.js +30 -20
- package/lib/ecosystems/parsers-dotnet.js +117 -18
- package/lib/ecosystems/parsers-go.js +1090 -367
- package/lib/ecosystems/parsers-jvm.js +259 -17
- package/lib/ecosystems/parsers-misc.js +229 -44
- package/lib/ecosystems/parsers-python.js +11 -10
- package/lib/ecosystems/rubyutils.js +6 -3
- package/lib/ecosystems/rusi.js +10 -0
- package/lib/ecosystems/scalacliutils.js +2 -1
- package/lib/ecosystems/scalasem.js +3 -1
- package/lib/evinser/evinser.js +126 -18
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +33 -0
- package/lib/inventory/auditCategories.js +3 -3
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +6 -4
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +500 -58
- package/lib/inventory/dynamic.js +45 -3
- package/lib/inventory/envcontext.js +168 -5
- package/lib/inventory/evidenceUtils.js +436 -23
- package/lib/inventory/formulationParsers.js +3 -2
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +89 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/phpBuildInfo.js +487 -0
- package/lib/inventory/phpHostProbe.js +641 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/source.js +207 -3
- 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 +529 -183
- package/lib/managers/docker.js +166 -12
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +34 -2
- package/lib/server/server.js +186 -1
- 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 +58 -1
- package/lib/stages/postgen/specVersionCompat.js +30 -0
- package/lib/stages/pregen/pregen.js +117 -5
- package/lib/validator/bomValidator.js +26 -5
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +26 -7
- package/lib/validator/generated/validate-1.7.mjs +31 -12
- package/lib/validator/generated/validate-2.0.mjs +30 -11
- package/package.json +17 -17
- 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 +9 -2
- 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/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/env.d.ts +18 -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.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/ecosystems/cppEvidence.d.ts +1 -1
- 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 +10 -8
- package/types/lib/ecosystems/gradleutils.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-jvm.d.ts +28 -2
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +18 -58
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +4 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- 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/evinser/evinser.d.ts +25 -0
- 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 +14 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.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 +145 -6
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- 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 +40 -2
- 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/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/hostTopology.d.ts.map +1 -1
- 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 +6 -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/source.d.ts +28 -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 +12 -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 +13 -0
- 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/pregen.d.ts +14 -1
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/server/server.js
CHANGED
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
isSecureMode,
|
|
15
15
|
readEnvironmentVariable,
|
|
16
16
|
} from "../core/activity.js";
|
|
17
|
+
import { PROJECT_TYPE_ALIASES } from "../core/env.js";
|
|
17
18
|
import { isWin } from "../core/paths.js";
|
|
19
|
+
import { isSecureModeActive } from "../core/projectTooling.js";
|
|
18
20
|
import { resetRunState } from "../core/runState.js";
|
|
19
21
|
import { CDXGEN_VERSION } from "../core/state.js";
|
|
20
22
|
import { installConsoleShim, ui } from "../core/ui.js";
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
getGitAllowProtocol,
|
|
28
30
|
gitClone,
|
|
29
31
|
isAllowedPath,
|
|
32
|
+
isAllowedRealPath,
|
|
30
33
|
isAllowedWinPath,
|
|
31
34
|
maybePurlSource,
|
|
32
35
|
maybeRemotePath,
|
|
@@ -82,6 +85,11 @@ const ALLOWED_PARAMS = [
|
|
|
82
85
|
"tlpClassification",
|
|
83
86
|
];
|
|
84
87
|
|
|
88
|
+
// Options that install packages or run the scanned project's own build tools
|
|
89
|
+
// (for example `cargo check`, which runs build.rs), and so execute code from
|
|
90
|
+
// the scanned repository.
|
|
91
|
+
const CODE_EXECUTION_PARAMS = ["installDeps", "deep"];
|
|
92
|
+
|
|
85
93
|
const app = connect();
|
|
86
94
|
|
|
87
95
|
export { isAllowedHttpHost };
|
|
@@ -156,6 +164,7 @@ export function parseValue(raw) {
|
|
|
156
164
|
* @returns {Object} Populated options object
|
|
157
165
|
*/
|
|
158
166
|
export function parseQueryString(q, body = {}, options = {}) {
|
|
167
|
+
const operatorOptions = { ...options };
|
|
159
168
|
// Priority is query params followed by body
|
|
160
169
|
for (const param of ALLOWED_PARAMS) {
|
|
161
170
|
const raw = q[param] ?? body[param];
|
|
@@ -163,7 +172,7 @@ export function parseQueryString(q, body = {}, options = {}) {
|
|
|
163
172
|
options[param] = parseValue(raw);
|
|
164
173
|
}
|
|
165
174
|
}
|
|
166
|
-
options.projectType = options.type
|
|
175
|
+
options.projectType = toProjectTypeList(options.type);
|
|
167
176
|
delete options.type;
|
|
168
177
|
if (options.lifecycle === "pre-build") {
|
|
169
178
|
options.installDeps = false;
|
|
@@ -171,9 +180,170 @@ export function parseQueryString(q, body = {}, options = {}) {
|
|
|
171
180
|
if (options.profile) {
|
|
172
181
|
applyProfileOptions(options);
|
|
173
182
|
}
|
|
183
|
+
// failOnError ends the process, so one failing request would stop the server
|
|
184
|
+
// for every client.
|
|
185
|
+
options.failOnError = false;
|
|
186
|
+
restrictRequestOptions(options, operatorOptions);
|
|
174
187
|
return options;
|
|
175
188
|
}
|
|
176
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Normalise a `type` value from the request or the start-up options, which may
|
|
192
|
+
* be a comma separated string or an array, into a list of project types.
|
|
193
|
+
*
|
|
194
|
+
* @param {string|string[]|undefined} type Project type value
|
|
195
|
+
* @returns {string[]|undefined} Project types, or undefined when none are given
|
|
196
|
+
*/
|
|
197
|
+
function toProjectTypeList(type) {
|
|
198
|
+
if (type === undefined || type === null || type === "") {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
const types = (Array.isArray(type) ? type : [type])
|
|
202
|
+
.flatMap((t) => String(t).split(","))
|
|
203
|
+
.map((t) => t.trim())
|
|
204
|
+
.filter(Boolean);
|
|
205
|
+
return types.length ? types : undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Stop a request from turning on options that execute code from the scanned
|
|
210
|
+
* repository when the operator has not. In secure mode, or when the server was
|
|
211
|
+
* started with --no-install-deps, a request may turn these options off but
|
|
212
|
+
* never on.
|
|
213
|
+
*
|
|
214
|
+
* @param {Object} options Options parsed from the request
|
|
215
|
+
* @param {Object} operatorOptions Options the server was started with
|
|
216
|
+
* @returns {void}
|
|
217
|
+
*/
|
|
218
|
+
function restrictRequestOptions(options, operatorOptions) {
|
|
219
|
+
if (!isSecureModeActive() && operatorOptions.installDeps !== false) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
for (const param of CODE_EXECUTION_PARAMS) {
|
|
223
|
+
// Only an explicit false from the request changes the operator's choice.
|
|
224
|
+
// Values such as "" or 0 would otherwise pass gates that test `!== false`.
|
|
225
|
+
options[param] =
|
|
226
|
+
operatorOptions[param] === true && options[param] !== false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Project types that inventory the host the server runs on, such as installed
|
|
231
|
+
// packages, users, shell history, IDE and browser extensions, or the Maven,
|
|
232
|
+
// Gradle, sbt, Cargo and Helm caches in the server user's home directory.
|
|
233
|
+
// They ignore the scan path, so the path allowlist does not limit them.
|
|
234
|
+
const HOST_SCOPE_PROJECT_TYPES = new Set(
|
|
235
|
+
[
|
|
236
|
+
"os",
|
|
237
|
+
"hbom",
|
|
238
|
+
"cargo-cache",
|
|
239
|
+
"helm-index",
|
|
240
|
+
"gradle-index",
|
|
241
|
+
"sbt-index",
|
|
242
|
+
"maven-index",
|
|
243
|
+
"vscode-extension",
|
|
244
|
+
"chrome-extension",
|
|
245
|
+
"agentic",
|
|
246
|
+
]
|
|
247
|
+
.flatMap((key) => PROJECT_TYPE_ALIASES[key] || [key])
|
|
248
|
+
.concat(["maven-repo"]),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Check request options that the server must refuse outright.
|
|
253
|
+
*
|
|
254
|
+
* @param {Object} reqOptions Options parsed from the request
|
|
255
|
+
* @param {Object} operatorOptions Options the server was started with
|
|
256
|
+
* @returns {Object|null} Error object with status, error and details, or null
|
|
257
|
+
*/
|
|
258
|
+
export function validateRequestOptions(reqOptions, operatorOptions = {}) {
|
|
259
|
+
const operatorTypes = new Set(
|
|
260
|
+
toProjectTypeList(operatorOptions.type ?? operatorOptions.projectType) ||
|
|
261
|
+
[],
|
|
262
|
+
);
|
|
263
|
+
const hostScopeType = (reqOptions.projectType || []).find(
|
|
264
|
+
(t) => HOST_SCOPE_PROJECT_TYPES.has(t) && !operatorTypes.has(t),
|
|
265
|
+
);
|
|
266
|
+
if (hostScopeType) {
|
|
267
|
+
return {
|
|
268
|
+
status: 403,
|
|
269
|
+
error: "Project Type Not Allowed",
|
|
270
|
+
details: `The '${hostScopeType}' project type scans the server host. Start the server with this type to allow it.`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
const excludes = Array.isArray(reqOptions.exclude)
|
|
274
|
+
? reqOptions.exclude
|
|
275
|
+
: [reqOptions.exclude];
|
|
276
|
+
// Brace patterns expand before matching, so one short pattern such as
|
|
277
|
+
// {1..100000} can keep the server busy for minutes.
|
|
278
|
+
if (
|
|
279
|
+
reqOptions.exclude !== operatorOptions.exclude &&
|
|
280
|
+
excludes.some(
|
|
281
|
+
(e) => typeof e === "string" && (e.includes("{") || e.length > 512),
|
|
282
|
+
)
|
|
283
|
+
) {
|
|
284
|
+
return {
|
|
285
|
+
status: 400,
|
|
286
|
+
error: "Invalid Exclude Pattern",
|
|
287
|
+
details:
|
|
288
|
+
"Exclude patterns sent to the server cannot use braces or exceed 512 characters.",
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (
|
|
292
|
+
reqOptions.serverUrl &&
|
|
293
|
+
reqOptions.serverUrl !== operatorOptions.serverUrl
|
|
294
|
+
) {
|
|
295
|
+
const requestedOrigin = urlOrigin(reqOptions.serverUrl);
|
|
296
|
+
if (!requestedOrigin) {
|
|
297
|
+
return {
|
|
298
|
+
status: 400,
|
|
299
|
+
error: "Invalid Server URL",
|
|
300
|
+
details: "The Dependency-Track server URL is invalid.",
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (requestedOrigin !== urlOrigin(operatorOptions.serverUrl)) {
|
|
304
|
+
// The operator's API key only ever goes to the operator's server.
|
|
305
|
+
if (
|
|
306
|
+
operatorOptions.apiKey &&
|
|
307
|
+
reqOptions.apiKey === operatorOptions.apiKey
|
|
308
|
+
) {
|
|
309
|
+
return {
|
|
310
|
+
status: 400,
|
|
311
|
+
error: "API Key Required",
|
|
312
|
+
details:
|
|
313
|
+
"A request that names its own Dependency-Track server must send its own API key.",
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
// CDXGEN_SERVER_ALLOWED_HOSTS only covers git hosts, so in secure mode a
|
|
317
|
+
// server the operator did not configure must be on CDXGEN_ALLOWED_HOSTS.
|
|
318
|
+
if (isSecureMode && !readEnvironmentVariable("CDXGEN_ALLOWED_HOSTS")) {
|
|
319
|
+
return {
|
|
320
|
+
status: 403,
|
|
321
|
+
error: "Host Not Allowed",
|
|
322
|
+
details:
|
|
323
|
+
"Set CDXGEN_ALLOWED_HOSTS to let requests choose a Dependency-Track server.",
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (isSecureMode && reqOptions.includeFormulation && reqOptions.serverUrl) {
|
|
329
|
+
return {
|
|
330
|
+
status: 400,
|
|
331
|
+
error: "Formulation Not Allowed",
|
|
332
|
+
details:
|
|
333
|
+
"Formulation data may contain secrets and is not submitted to Dependency-Track in secure mode.",
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function urlOrigin(value) {
|
|
340
|
+
try {
|
|
341
|
+
return new URL(value).origin;
|
|
342
|
+
} catch (_err) {
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
177
347
|
/**
|
|
178
348
|
* Extracts query parameters from an incoming HTTP request object.
|
|
179
349
|
* Handles repeated keys by collecting their values into an array.
|
|
@@ -391,6 +561,18 @@ const start = (options) => {
|
|
|
391
561
|
}),
|
|
392
562
|
);
|
|
393
563
|
}
|
|
564
|
+
const optionsError = validateRequestOptions(reqOptions, options);
|
|
565
|
+
if (optionsError) {
|
|
566
|
+
res.writeHead(optionsError.status, {
|
|
567
|
+
"Content-Type": "application/json",
|
|
568
|
+
});
|
|
569
|
+
return res.end(
|
|
570
|
+
JSON.stringify({
|
|
571
|
+
error: optionsError.error,
|
|
572
|
+
details: optionsError.details,
|
|
573
|
+
}),
|
|
574
|
+
);
|
|
575
|
+
}
|
|
394
576
|
// Enforce the v13 CycloneDX spec floor on the HTTP entry point too, so the
|
|
395
577
|
// server cannot be used to request a generation target the CLI rejects.
|
|
396
578
|
if (reqOptions.specVersion !== undefined) {
|
|
@@ -502,8 +684,11 @@ const start = (options) => {
|
|
|
502
684
|
cleanup = true;
|
|
503
685
|
} else {
|
|
504
686
|
srcDir = sourcePath;
|
|
687
|
+
// Check where a symlinked path really points as well, so a link inside
|
|
688
|
+
// an allowed directory cannot open a scan of a directory outside it.
|
|
505
689
|
if (
|
|
506
690
|
!isAllowedPath(path.resolve(srcDir)) ||
|
|
691
|
+
!isAllowedRealPath(srcDir) ||
|
|
507
692
|
(isWin && !isAllowedWinPath(srcDir))
|
|
508
693
|
) {
|
|
509
694
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
@@ -463,6 +463,15 @@ export function textualMetadata(bomJson) {
|
|
|
463
463
|
const swidCount = bomJson?.components?.filter((c) =>
|
|
464
464
|
c?.purl?.startsWith("pkg:swid"),
|
|
465
465
|
).length;
|
|
466
|
+
// Homebrew packages are pkg:brew, not pkg:swid, so they are not counted as
|
|
467
|
+
// applications; formulae and casks are told apart by a property.
|
|
468
|
+
const brewComponents =
|
|
469
|
+
bomJson?.components?.filter((c) => c?.purl?.startsWith("pkg:brew/")) || [];
|
|
470
|
+
const brewCaskCount = brewComponents.filter((c) =>
|
|
471
|
+
c.properties?.some(
|
|
472
|
+
(p) => p.name === "cdx:homebrew:type" && p.value === "cask",
|
|
473
|
+
),
|
|
474
|
+
).length;
|
|
466
475
|
const { unpackagedExecutableCount, unpackagedSharedLibraryCount } =
|
|
467
476
|
getContainerFileInventoryStats(bomJson?.components);
|
|
468
477
|
const githubStats = getGitHubWorkflowStats(bomJson?.components);
|
|
@@ -711,7 +720,14 @@ export function textualMetadata(bomJson) {
|
|
|
711
720
|
text = `${text} The system appears to be set up for remote development, with ${vsixCount} Visual Studio Code extensions installed.`;
|
|
712
721
|
}
|
|
713
722
|
if (swidCount > 0) {
|
|
714
|
-
text = `${text} In addition, there are ${swidCount} applications installed on the system.`;
|
|
723
|
+
text = `${text} In addition, there ${swidCount === 1 ? "is 1 application" : `are ${swidCount} applications`} installed on the system.`;
|
|
724
|
+
}
|
|
725
|
+
if (brewComponents.length > 0) {
|
|
726
|
+
const brewCount = brewComponents.length;
|
|
727
|
+
const casks = brewCaskCount
|
|
728
|
+
? `, of which ${brewCaskCount} ${brewCaskCount === 1 ? "is a cask" : "are casks"}`
|
|
729
|
+
: "";
|
|
730
|
+
text = `${text} The system also has ${brewCount} Homebrew package${brewCount === 1 ? "" : "s"} installed${casks}.`;
|
|
715
731
|
}
|
|
716
732
|
}
|
|
717
733
|
if (bomType === "HBOM" && hbomSummary) {
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
isVersionedJvmToolProjectType,
|
|
37
37
|
PROJECT_TYPE_ALIASES,
|
|
38
38
|
} from "../../../core/env.js";
|
|
39
|
+
import { safeExistsSync } from "../../../core/fs.js";
|
|
39
40
|
import { dirNameStr } from "../../../core/paths.js";
|
|
40
41
|
import { CDXGEN_VERSION } from "../../../core/state.js";
|
|
41
42
|
import { evaluateRules, loadRules } from "../ruleEngine.js";
|
|
@@ -389,7 +390,9 @@ function resolveLedgerEvents(context) {
|
|
|
389
390
|
};
|
|
390
391
|
}
|
|
391
392
|
const sidecarPath = readEnvironmentVariable("CDXGEN_INTROSPECT_LEDGER");
|
|
392
|
-
|
|
393
|
+
// A sidecar the run could not create (the permission model denies writes
|
|
394
|
+
// outside the granted paths) holds nothing; the buffer still has the events.
|
|
395
|
+
if (sidecarPath && safeExistsSync(sidecarPath)) {
|
|
393
396
|
return { events: loadLedgerFile(sidecarPath), source: "sidecar" };
|
|
394
397
|
}
|
|
395
398
|
const events = getLedgerEvents();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
|
|
5
5
|
import { Purl } from "@cdxgen/cdx-purl";
|
|
@@ -943,10 +943,10 @@ function applyCustomMetadataProperties(bomJson, options) {
|
|
|
943
943
|
export function applyStandards(bomJson, options) {
|
|
944
944
|
if (options.standard && Array.isArray(options.standard)) {
|
|
945
945
|
for (let astandard of options.standard) {
|
|
946
|
-
// See issue: #1953
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
946
|
+
// See issue: #1953. Server requests can send either separator, and on
|
|
947
|
+
// Windows both "/" and "\" separate path segments, so keep only the
|
|
948
|
+
// last segment of either kind.
|
|
949
|
+
astandard = String(astandard).split(/[\\/]/).pop();
|
|
950
950
|
const templateFile = join(
|
|
951
951
|
dirNameStr,
|
|
952
952
|
"data",
|
|
@@ -165,7 +165,18 @@ function hasProperty(obj, propName, expectedValue) {
|
|
|
165
165
|
if (expectedValue === undefined) {
|
|
166
166
|
return value !== null;
|
|
167
167
|
}
|
|
168
|
-
|
|
168
|
+
// A repeatable property (cdx:crypto:sourceType on a certificate found by
|
|
169
|
+
// several producers) matches when any of its values does.
|
|
170
|
+
return extractPropertyValues(obj, propName).includes(String(expectedValue));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function extractPropertyValues(obj, propName) {
|
|
174
|
+
if (!Array.isArray(obj?.properties)) {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
return obj.properties
|
|
178
|
+
.filter((p) => p?.name === propName && p.value !== undefined)
|
|
179
|
+
.map((p) => String(p.value));
|
|
169
180
|
}
|
|
170
181
|
|
|
171
182
|
/**
|
|
@@ -315,6 +326,9 @@ function registerCdxHelpers(expression, helpers = {}) {
|
|
|
315
326
|
expression.registerFunction("prop", (obj, propName) =>
|
|
316
327
|
extractProperty(obj, propName),
|
|
317
328
|
);
|
|
329
|
+
expression.registerFunction("propValues", (obj, propName) =>
|
|
330
|
+
extractPropertyValues(obj, propName),
|
|
331
|
+
);
|
|
318
332
|
expression.registerFunction("nullSafeProp", (obj, propName) => {
|
|
319
333
|
const value = extractProperty(obj, propName);
|
|
320
334
|
return value === null ? "" : value;
|
|
@@ -385,6 +399,13 @@ function registerCdxHelpers(expression, helpers = {}) {
|
|
|
385
399
|
expression.registerFunction("safeStr", (val) => {
|
|
386
400
|
return val === null || val === undefined ? "" : String(val).trim();
|
|
387
401
|
});
|
|
402
|
+
// End of validity in epoch milliseconds. A date-only value (2026-01-31)
|
|
403
|
+
// is valid through the end of that day in UTC; a full RFC 3339 timestamp
|
|
404
|
+
// is taken as is; anything else, or an impossible date, gives no value so
|
|
405
|
+
// the rule skips the component.
|
|
406
|
+
expression.registerFunction("certEndMillis", (val) =>
|
|
407
|
+
parseCertificateEndMillis(val),
|
|
408
|
+
);
|
|
388
409
|
expression.registerFunction("parseSizeBytes", (val) => {
|
|
389
410
|
if (val === null || val === undefined || val === "") {
|
|
390
411
|
return null;
|
|
@@ -519,6 +540,42 @@ function registerCdxHelpers(expression, helpers = {}) {
|
|
|
519
540
|
* @param {string} rulesDir - Path to directory containing .yaml rule files
|
|
520
541
|
* @returns {Promise<Array>} Array of parsed rule objects
|
|
521
542
|
*/
|
|
543
|
+
const FULL_TIMESTAMP_RE =
|
|
544
|
+
/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]([01]\d|2[0-3]):[0-5]\d)$/;
|
|
545
|
+
const DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Parse a certificate notValidAfter value.
|
|
549
|
+
*
|
|
550
|
+
* @param {*} value timestamp or date-only string
|
|
551
|
+
* @returns {number|undefined} epoch milliseconds, or undefined when the value
|
|
552
|
+
* does not parse
|
|
553
|
+
*/
|
|
554
|
+
export function parseCertificateEndMillis(value) {
|
|
555
|
+
if (typeof value !== "string") {
|
|
556
|
+
return undefined;
|
|
557
|
+
}
|
|
558
|
+
const text = value.trim();
|
|
559
|
+
const dateOnly = DATE_ONLY_RE.exec(text);
|
|
560
|
+
if (dateOnly) {
|
|
561
|
+
const [year, month, day] = dateOnly.slice(1).map(Number);
|
|
562
|
+
const start = new Date(Date.UTC(year, month - 1, day));
|
|
563
|
+
if (
|
|
564
|
+
start.getUTCFullYear() !== year ||
|
|
565
|
+
start.getUTCMonth() !== month - 1 ||
|
|
566
|
+
start.getUTCDate() !== day
|
|
567
|
+
) {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
return start.getTime() + 24 * 60 * 60 * 1000 - 1;
|
|
571
|
+
}
|
|
572
|
+
if (!FULL_TIMESTAMP_RE.test(text)) {
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
const millis = Date.parse(text);
|
|
576
|
+
return Number.isNaN(millis) ? undefined : millis;
|
|
577
|
+
}
|
|
578
|
+
|
|
522
579
|
export async function loadRules(rulesDir) {
|
|
523
580
|
const rules = [];
|
|
524
581
|
if (!safeExistsSync(rulesDir)) {
|
|
@@ -604,9 +604,39 @@ function downgradeSubjectForSpecVersion(subject, specVersion, parentKey) {
|
|
|
604
604
|
certificateProperties.certificateExtension =
|
|
605
605
|
certificateProperties.certificateFileExtension;
|
|
606
606
|
}
|
|
607
|
+
// `relatedCryptographicAssets` and `certificateExtensions` are 1.7-only;
|
|
608
|
+
// the deprecated `subjectPublicKeyRef`/`signatureAlgorithmRef` (valid in
|
|
609
|
+
// 1.6, see data/bom-1.6.schema.json) carry the same links, and the
|
|
610
|
+
// extension facts survive as `cdx:crypto:` properties.
|
|
611
|
+
const relatedAssets = certificateProperties.relatedCryptographicAssets;
|
|
612
|
+
if (Array.isArray(relatedAssets)) {
|
|
613
|
+
for (const asset of relatedAssets) {
|
|
614
|
+
if (
|
|
615
|
+
asset?.type === "publicKey" &&
|
|
616
|
+
!certificateProperties.subjectPublicKeyRef
|
|
617
|
+
) {
|
|
618
|
+
certificateProperties.subjectPublicKeyRef = asset.ref;
|
|
619
|
+
} else if (
|
|
620
|
+
asset?.type === "algorithm" &&
|
|
621
|
+
!certificateProperties.signatureAlgorithmRef
|
|
622
|
+
) {
|
|
623
|
+
certificateProperties.signatureAlgorithmRef = asset.ref;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
delete certificateProperties.relatedCryptographicAssets;
|
|
627
|
+
}
|
|
628
|
+
delete certificateProperties.certificateExtensions;
|
|
607
629
|
delete certificateProperties.serialNumber;
|
|
608
630
|
delete certificateProperties.certificateFileExtension;
|
|
609
631
|
delete certificateProperties.fingerprint;
|
|
632
|
+
} else if (
|
|
633
|
+
specVersion < 1.7 &&
|
|
634
|
+
subject.cryptoProperties?.assetType === "related-crypto-material" &&
|
|
635
|
+
subject.cryptoProperties.relatedCryptoMaterialProperties
|
|
636
|
+
) {
|
|
637
|
+
// The public-key `fingerprint` object is 1.7-only; the SPKI digest stays
|
|
638
|
+
// available as the `cdx:crypto:spkiSha256` property.
|
|
639
|
+
delete subject.cryptoProperties.relatedCryptoMaterialProperties.fingerprint;
|
|
610
640
|
}
|
|
611
641
|
if (specVersion < 1.7 && subject.cryptoProperties?.algorithmProperties) {
|
|
612
642
|
// `algorithmFamily` and `ellipticCurve` are CycloneDX 1.7 additions to
|
|
@@ -28,6 +28,13 @@ import {
|
|
|
28
28
|
TIMEOUT_MS,
|
|
29
29
|
} from "../../core/fs.js";
|
|
30
30
|
import { isMac, isWin } from "../../core/paths.js";
|
|
31
|
+
import { isProjectToolingAllowed } from "../../core/projectTooling.js";
|
|
32
|
+
import {
|
|
33
|
+
dosaiDotnetRequirement,
|
|
34
|
+
dosaiDotnetRequirementAdvice,
|
|
35
|
+
dosaiDotnetRequirementDetail,
|
|
36
|
+
markDosaiLaunchFailureReported,
|
|
37
|
+
} from "../../inventory/dosai.js";
|
|
31
38
|
import {
|
|
32
39
|
bundleInstallWithDocker,
|
|
33
40
|
collectJavaInfo,
|
|
@@ -60,6 +67,7 @@ import {
|
|
|
60
67
|
readToolVersionOverride,
|
|
61
68
|
resolvePartialVersion,
|
|
62
69
|
} from "../../inventory/jvmToolEnv.js";
|
|
70
|
+
import { resolvePluginBinary } from "../../inventory/plugins.js";
|
|
63
71
|
import {
|
|
64
72
|
classifyProbeResult,
|
|
65
73
|
describeSpawnRestriction,
|
|
@@ -74,12 +82,20 @@ import {
|
|
|
74
82
|
* @param {Object} options CLI options
|
|
75
83
|
*/
|
|
76
84
|
export function prepareEnv(filePath, options) {
|
|
77
|
-
if (
|
|
85
|
+
if (isSecureMode) {
|
|
78
86
|
return;
|
|
79
87
|
}
|
|
80
88
|
if (filePath) {
|
|
81
89
|
filePath = resolve(filePath);
|
|
82
90
|
}
|
|
91
|
+
// Runs before the project type check: a .NET tree is also found when cdxgen
|
|
92
|
+
// detects the project types itself.
|
|
93
|
+
if (!isDryRun) {
|
|
94
|
+
prepareDotnetEnv(filePath, options);
|
|
95
|
+
}
|
|
96
|
+
if (!options.projectType) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
83
99
|
// The JVM preparation provisions the JDK ahead of the build tools, so an
|
|
84
100
|
// alias it already installed must not be installed a second time here.
|
|
85
101
|
// It runs under dry-run too, where it reports the toolchain it would set up
|
|
@@ -104,6 +120,91 @@ export function prepareEnv(filePath, options) {
|
|
|
104
120
|
prepareRubyEnv(filePath, options);
|
|
105
121
|
}
|
|
106
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Check that dosai, which deep runs use for .NET evidence, can start. The
|
|
125
|
+
* dosai bundled with cdxgen-plugins-bin is framework-dependent, so it needs a
|
|
126
|
+
* .NET runtime at least as new as the one it targets (.NET 11 from dosai
|
|
127
|
+
* 5.0.0). Running it lets the .NET host apply its own version rules. Skipped
|
|
128
|
+
* in the container images, which ship a dosai that works with their SDK, and
|
|
129
|
+
* when DOSAI_CMD names another build, such as the self-contained -full one.
|
|
130
|
+
*
|
|
131
|
+
* @param {String} filePath Project path
|
|
132
|
+
* @param {Object} options CLI options
|
|
133
|
+
*/
|
|
134
|
+
export function prepareDotnetEnv(filePath, options) {
|
|
135
|
+
if (
|
|
136
|
+
!options.deep ||
|
|
137
|
+
!filePath ||
|
|
138
|
+
readEnvironmentVariable("CDXGEN_IN_CONTAINER") === "true" ||
|
|
139
|
+
readEnvironmentVariable("DOSAI_CMD") ||
|
|
140
|
+
!isDotnetInScope(filePath, options)
|
|
141
|
+
) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const dosaiBin = resolvePluginBinary("dosai");
|
|
145
|
+
if (!dosaiBin) {
|
|
146
|
+
// A missing plugin is reported where dosai runs
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const result = safeSpawnSync(dosaiBin, ["--version"], {
|
|
150
|
+
encoding: "utf-8",
|
|
151
|
+
timeout: TIMEOUT_MS,
|
|
152
|
+
});
|
|
153
|
+
if (result?.status === 0 && !result.error) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const output = commandOutputText(result) || "";
|
|
157
|
+
const requirement = dosaiDotnetRequirement(output);
|
|
158
|
+
const detail = requirement
|
|
159
|
+
? dosaiDotnetRequirementDetail(requirement)
|
|
160
|
+
: "dosai could not start";
|
|
161
|
+
console.log(
|
|
162
|
+
`WARNING: ${detail}, so this deep run has no dosai evidence for .NET (call graph, reachability, crypto). ${dosaiDotnetRequirementAdvice(requirement)}`,
|
|
163
|
+
);
|
|
164
|
+
if (requirement) {
|
|
165
|
+
markDosaiLaunchFailureReported(dosaiBin);
|
|
166
|
+
}
|
|
167
|
+
recordLedgerEvent(LEDGER_EVENT_KINDS.EVIDENCE_DEGRADED, {
|
|
168
|
+
ecosystem: "csharp",
|
|
169
|
+
tool: "dosai",
|
|
170
|
+
detail: `${detail}; .NET evidence from dosai is unavailable.`,
|
|
171
|
+
impact: LEDGER_EVENT_IMPACTS.COMPONENTS,
|
|
172
|
+
});
|
|
173
|
+
deferFailOnError(options, {
|
|
174
|
+
ecosystem: "csharp",
|
|
175
|
+
tool: "dosai",
|
|
176
|
+
detail,
|
|
177
|
+
exitCode: typeof result?.status === "number" ? result.status : undefined,
|
|
178
|
+
command: `${dosaiBin} --version`,
|
|
179
|
+
outputExcerpt: output.trim() || undefined,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Whether the run covers .NET: a .NET project type, or, when cdxgen detects
|
|
185
|
+
* the types itself, a .NET project or solution file in the tree.
|
|
186
|
+
*
|
|
187
|
+
* @param {String} filePath Project path
|
|
188
|
+
* @param {Object} options CLI options
|
|
189
|
+
*
|
|
190
|
+
* @returns {Boolean}
|
|
191
|
+
*/
|
|
192
|
+
function isDotnetInScope(filePath, options) {
|
|
193
|
+
if (options.projectType?.length) {
|
|
194
|
+
return hasAnyProjectType(["csharp"], options, false);
|
|
195
|
+
}
|
|
196
|
+
if (!hasAnyProjectType(["csharp"], options, true)) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
return (
|
|
200
|
+
getAllFiles(
|
|
201
|
+
filePath,
|
|
202
|
+
`${options.multiProject ? "**/" : ""}*.{csproj,vbproj,fsproj,sln,slnx}`,
|
|
203
|
+
options,
|
|
204
|
+
).length > 0
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
107
208
|
/**
|
|
108
209
|
* Record the tool requirements the project declares through its well-known
|
|
109
210
|
* tool pin files (`.tool-versions`, `.nvmrc`, `package.json`, `go.mod`, …),
|
|
@@ -1205,7 +1306,7 @@ export function prepareNodeEnv(filePath, options) {
|
|
|
1205
1306
|
}
|
|
1206
1307
|
}
|
|
1207
1308
|
if (nvmNodePath) {
|
|
1208
|
-
doNpmInstall(filePath, nvmNodePath);
|
|
1309
|
+
doNpmInstall(filePath, nvmNodePath, options);
|
|
1209
1310
|
npmInstallAttempted = true;
|
|
1210
1311
|
} else {
|
|
1211
1312
|
console.log(
|
|
@@ -1228,7 +1329,7 @@ export function prepareNodeEnv(filePath, options) {
|
|
|
1228
1329
|
);
|
|
1229
1330
|
continue;
|
|
1230
1331
|
}
|
|
1231
|
-
doNpmInstall(filePath, nvmNodePath);
|
|
1332
|
+
doNpmInstall(filePath, nvmNodePath, options);
|
|
1232
1333
|
npmInstallAttempted = true;
|
|
1233
1334
|
}
|
|
1234
1335
|
}
|
|
@@ -1302,12 +1403,18 @@ export function tryLoadNvmAndInstallTool(nodeVersion) {
|
|
|
1302
1403
|
*
|
|
1303
1404
|
* @param {String} filePath Path
|
|
1304
1405
|
* @param {String} nvmNodePath Path to node version in nvm
|
|
1406
|
+
* @param {Object} [options] CLI options
|
|
1305
1407
|
*/
|
|
1306
|
-
export function doNpmInstall(filePath, nvmNodePath) {
|
|
1408
|
+
export function doNpmInstall(filePath, nvmNodePath, options = {}) {
|
|
1307
1409
|
// we do not install if INSTALL_ARGS set false
|
|
1308
1410
|
if (["0", "false"].includes(readEnvironmentVariable("NODE_INSTALL_ARGS"))) {
|
|
1309
1411
|
return;
|
|
1310
1412
|
}
|
|
1413
|
+
// npm install runs the project's own lifecycle scripts, such as preinstall,
|
|
1414
|
+
// even with --package-lock-only.
|
|
1415
|
+
if (options.installDeps === false) {
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1311
1418
|
const newPath = `${nvmNodePath}${delimiter}${readEnvironmentVariable("PATH")}`;
|
|
1312
1419
|
let installArgs =
|
|
1313
1420
|
readEnvironmentVariable("NPM_INSTALL_ARGS") || "--package-lock-only";
|
|
@@ -1386,7 +1493,12 @@ export function prepareSwiftEnv(filePath, options) {
|
|
|
1386
1493
|
"INFO: Swift for Linux has known issues on non x64 machines. Run the cdxgen container image with --platform=linux/amd64 for best experience.",
|
|
1387
1494
|
);
|
|
1388
1495
|
}
|
|
1389
|
-
|
|
1496
|
+
// `swift package resolve` runs the project's Package.swift, so it waits for
|
|
1497
|
+
// the same permission as the scan's other project tooling.
|
|
1498
|
+
if (
|
|
1499
|
+
(options.deep || options?.lifecycle?.includes("post-build")) &&
|
|
1500
|
+
isProjectToolingAllowed(options)
|
|
1501
|
+
) {
|
|
1390
1502
|
const swiftFiles = getAllFiles(
|
|
1391
1503
|
filePath,
|
|
1392
1504
|
`${options.multiProject ? "**/" : ""}Package*.swift`,
|