@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/cli/nativeBom.js
CHANGED
|
@@ -23,11 +23,16 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
checksumFile,
|
|
25
25
|
getAllFiles,
|
|
26
|
+
getTmpDir,
|
|
26
27
|
safeExistsSync,
|
|
27
28
|
safeSpawnSync,
|
|
28
29
|
} from "../core/fs.js";
|
|
29
30
|
import { thoughtLog } from "../core/logger.js";
|
|
30
31
|
import { dirNameStr, isMac } from "../core/paths.js";
|
|
32
|
+
import {
|
|
33
|
+
isProjectToolingAllowed,
|
|
34
|
+
reportProjectToolingSkipped,
|
|
35
|
+
} from "../core/projectTooling.js";
|
|
31
36
|
import { readCrateArchiveValues } from "../ecosystems/cargoCache.js";
|
|
32
37
|
import {
|
|
33
38
|
buildDependentPurl,
|
|
@@ -55,6 +60,7 @@ import {
|
|
|
55
60
|
} from "../ecosystems/parsers-elm.js";
|
|
56
61
|
import { parseGleamProject } from "../ecosystems/parsers-gleam.js";
|
|
57
62
|
import {
|
|
63
|
+
compareGoVersions,
|
|
58
64
|
isGoModWhyNotNeeded,
|
|
59
65
|
parseGoListDep,
|
|
60
66
|
parseGoModData,
|
|
@@ -198,6 +204,30 @@ export function getCargoCacheDir() {
|
|
|
198
204
|
);
|
|
199
205
|
}
|
|
200
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Add a Go module of the project, a nested go.mod or a go.work member, under
|
|
209
|
+
* the parent component. A module that is already the parent, one of its
|
|
210
|
+
* subcomponents, or a component (a directory replacement inside the project)
|
|
211
|
+
* is not added again, so no bom-ref appears twice.
|
|
212
|
+
*
|
|
213
|
+
* @param {Object} parentComponent The project's parent component
|
|
214
|
+
* @param {Object} moduleComponent The module to add
|
|
215
|
+
* @param {Object[]} pkgList Components found so far
|
|
216
|
+
*/
|
|
217
|
+
function addGoSubComponent(parentComponent, moduleComponent, pkgList) {
|
|
218
|
+
const ref = moduleComponent?.["bom-ref"];
|
|
219
|
+
if (
|
|
220
|
+
!ref ||
|
|
221
|
+
ref === parentComponent["bom-ref"] ||
|
|
222
|
+
parentComponent.components?.some((c) => c["bom-ref"] === ref) ||
|
|
223
|
+
pkgList.some((c) => c["bom-ref"] === ref)
|
|
224
|
+
) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
parentComponent.components = parentComponent.components || [];
|
|
228
|
+
parentComponent.components.push(moduleComponent);
|
|
229
|
+
}
|
|
230
|
+
|
|
201
231
|
/**
|
|
202
232
|
* Function to create bom string for Go projects
|
|
203
233
|
*
|
|
@@ -313,6 +343,20 @@ export async function createGoBom(path, options) {
|
|
|
313
343
|
}
|
|
314
344
|
}
|
|
315
345
|
}
|
|
346
|
+
// go mod why answers for a module path, but go.sum also names versions
|
|
347
|
+
// that only took part in version selection. Only a version whose content
|
|
348
|
+
// go.sum holds a checksum for was ever downloaded, and of those, MVS
|
|
349
|
+
// builds the highest.
|
|
350
|
+
const builtVersions = {};
|
|
351
|
+
for (const apkg of pkgList) {
|
|
352
|
+
const built = builtVersions[apkg.name];
|
|
353
|
+
if (
|
|
354
|
+
apkg._integrity &&
|
|
355
|
+
(!built || compareGoVersions(apkg.version, built) > 0)
|
|
356
|
+
) {
|
|
357
|
+
builtVersions[apkg.name] = apkg.version;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
316
360
|
// A module go reports as unneeded is optional, but only when every module
|
|
317
361
|
// was checked: once go mod why fails, the answers are incomplete, and a
|
|
318
362
|
// tool failure is not evidence that a module is unused.
|
|
@@ -321,7 +365,9 @@ export async function createGoBom(path, options) {
|
|
|
321
365
|
continue;
|
|
322
366
|
}
|
|
323
367
|
if (requiredModules.has(apkg.name)) {
|
|
324
|
-
apkg.
|
|
368
|
+
if (builtVersions[apkg.name] === apkg.version) {
|
|
369
|
+
apkg.scope = "required";
|
|
370
|
+
}
|
|
325
371
|
} else if (!circuitBreak && unneededModules.has(apkg.name)) {
|
|
326
372
|
apkg.scope = "optional";
|
|
327
373
|
}
|
|
@@ -384,6 +430,9 @@ export async function createGoBom(path, options) {
|
|
|
384
430
|
});
|
|
385
431
|
|
|
386
432
|
let rootParentComponent = null;
|
|
433
|
+
// Whether the go toolchain resolved a module graph. A module without
|
|
434
|
+
// dependencies resolves to no components, and that is a complete answer.
|
|
435
|
+
let resolvedByToolchain = false;
|
|
387
436
|
// Use the go list -deps and go mod why commands to generate a good quality BOM for non-docker invocations
|
|
388
437
|
if (
|
|
389
438
|
!hasAnyProjectType(["docker", "oci", "container", "os"], options, false)
|
|
@@ -403,13 +452,15 @@ export async function createGoBom(path, options) {
|
|
|
403
452
|
console.log("Executing go list -deps in", basePath);
|
|
404
453
|
}
|
|
405
454
|
// TODO: Replacing this with -json gives us more interesting data points such as GoFiles, Imports, and Deps
|
|
455
|
+
// The fields after Dir name the module content checksum and, for a
|
|
456
|
+
// module a replace directive swapped out, the replacement Go builds.
|
|
406
457
|
let result = safeSpawnSync(
|
|
407
458
|
"go",
|
|
408
459
|
[
|
|
409
460
|
"list",
|
|
410
461
|
"-deps",
|
|
411
462
|
"-f",
|
|
412
|
-
"'{{with .Module}}{{.Path}}|{{.Version}}|{{.Indirect}}|{{.GoMod}}|{{.GoVersion}}|{{.Main}}|{{.Time}}|{{.Deprecated}}|{{.GoModSum}}|{{.Dir}}{{end}}'",
|
|
463
|
+
"'{{with .Module}}{{.Path}}|{{.Version}}|{{.Indirect}}|{{.GoMod}}|{{.GoVersion}}|{{.Main}}|{{.Time}}|{{.Deprecated}}|{{.GoModSum}}|{{.Dir}}|{{.Sum}}|{{with .Replace}}{{.Path}}{{end}}|{{with .Replace}}{{.Version}}{{end}}|{{with .Replace}}{{.Sum}}{{end}}{{end}}'",
|
|
413
464
|
"./...",
|
|
414
465
|
],
|
|
415
466
|
{
|
|
@@ -453,7 +504,9 @@ export async function createGoBom(path, options) {
|
|
|
453
504
|
const stdout = result.stdout;
|
|
454
505
|
if (stdout) {
|
|
455
506
|
let cmdOutput = Buffer.from(stdout).toString();
|
|
456
|
-
const retMap = await parseGoListDep(cmdOutput, gosumMap
|
|
507
|
+
const retMap = await parseGoListDep(cmdOutput, gosumMap, {
|
|
508
|
+
mainModuleDir: basePath,
|
|
509
|
+
});
|
|
457
510
|
if (retMap.pkgList?.length) {
|
|
458
511
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
459
512
|
}
|
|
@@ -462,6 +515,7 @@ export async function createGoBom(path, options) {
|
|
|
462
515
|
retMap.parentComponent &&
|
|
463
516
|
Object.keys(retMap.parentComponent).length
|
|
464
517
|
) {
|
|
518
|
+
resolvedByToolchain = true;
|
|
465
519
|
if (!rootParentComponent) {
|
|
466
520
|
// First (shallowest) module becomes the root
|
|
467
521
|
rootParentComponent = retMap.parentComponent;
|
|
@@ -469,12 +523,18 @@ export async function createGoBom(path, options) {
|
|
|
469
523
|
parentComponent = rootParentComponent;
|
|
470
524
|
} else {
|
|
471
525
|
// Subsequent modules become subcomponents
|
|
472
|
-
|
|
473
|
-
parentComponent
|
|
474
|
-
|
|
475
|
-
|
|
526
|
+
addGoSubComponent(
|
|
527
|
+
parentComponent,
|
|
528
|
+
retMap.parentComponent,
|
|
529
|
+
pkgList,
|
|
530
|
+
);
|
|
476
531
|
}
|
|
477
532
|
}
|
|
533
|
+
// The other members of a go.work workspace are modules of the same
|
|
534
|
+
// project, which no registry holds.
|
|
535
|
+
for (const workspaceModule of retMap.workspaceModules || []) {
|
|
536
|
+
addGoSubComponent(parentComponent, workspaceModule, pkgList);
|
|
537
|
+
}
|
|
478
538
|
if (DEBUG_MODE) {
|
|
479
539
|
console.log("Executing go mod graph in", basePath);
|
|
480
540
|
}
|
|
@@ -524,8 +584,11 @@ export async function createGoBom(path, options) {
|
|
|
524
584
|
}
|
|
525
585
|
// Retain the parent component hierarchy
|
|
526
586
|
if (Object.keys(retMap.parentComponent).length) {
|
|
527
|
-
|
|
528
|
-
|
|
587
|
+
addGoSubComponent(
|
|
588
|
+
parentComponent,
|
|
589
|
+
retMap.parentComponent,
|
|
590
|
+
pkgList,
|
|
591
|
+
);
|
|
529
592
|
}
|
|
530
593
|
}
|
|
531
594
|
} else {
|
|
@@ -538,6 +601,7 @@ export async function createGoBom(path, options) {
|
|
|
538
601
|
shell: isWin,
|
|
539
602
|
});
|
|
540
603
|
if (result.stdout) {
|
|
604
|
+
resolvedByToolchain = true;
|
|
541
605
|
const cmdOutput = Buffer.from(result.stdout).toString();
|
|
542
606
|
// The arguments to parseGoModGraph are slightly different to force inclusion of all packages
|
|
543
607
|
const retMap = await parseGoModGraph(
|
|
@@ -566,8 +630,11 @@ export async function createGoBom(path, options) {
|
|
|
566
630
|
parentComponent = rootParentComponent;
|
|
567
631
|
} else {
|
|
568
632
|
// Subsequent modules become subcomponents
|
|
569
|
-
|
|
570
|
-
|
|
633
|
+
addGoSubComponent(
|
|
634
|
+
parentComponent,
|
|
635
|
+
retMap.parentComponent,
|
|
636
|
+
pkgList,
|
|
637
|
+
);
|
|
571
638
|
}
|
|
572
639
|
}
|
|
573
640
|
} else {
|
|
@@ -589,7 +656,7 @@ export async function createGoBom(path, options) {
|
|
|
589
656
|
}
|
|
590
657
|
}
|
|
591
658
|
}
|
|
592
|
-
if (pkgList.length && !shouldManuallyParse) {
|
|
659
|
+
if ((pkgList.length || resolvedByToolchain) && !shouldManuallyParse) {
|
|
593
660
|
return buildBomNSData(options, pkgList, "golang", {
|
|
594
661
|
dependencies,
|
|
595
662
|
parentComponent,
|
|
@@ -732,11 +799,19 @@ function cargoAnswersVersion(cwd) {
|
|
|
732
799
|
* @param {string} [filterPlatform] Target triple to resolve the graph for
|
|
733
800
|
* @returns {object|undefined} Parsed `cargo metadata` output
|
|
734
801
|
*/
|
|
735
|
-
function runCargoMetadata(cargoTomlFile, filterPlatform) {
|
|
736
|
-
|
|
802
|
+
function runCargoMetadata(cargoTomlFile, filterPlatform, options = {}) {
|
|
803
|
+
// cargo reads `.cargo/config.toml` (rustc, rustc-wrapper, [env]) and rustup
|
|
804
|
+
// reads `rust-toolchain.toml` from the working directory, and `cargo
|
|
805
|
+
// metadata` runs rustc to learn the target. Without permission to run the
|
|
806
|
+
// project's tooling, cargo starts outside the project and stays offline.
|
|
807
|
+
const toolingAllowed = isProjectToolingAllowed(options);
|
|
808
|
+
const basePath = toolingAllowed ? dirname(cargoTomlFile) : getTmpDir();
|
|
737
809
|
const platformArgs = filterPlatform
|
|
738
810
|
? ["--filter-platform", filterPlatform]
|
|
739
811
|
: [];
|
|
812
|
+
if (!toolingAllowed) {
|
|
813
|
+
platformArgs.push("--offline");
|
|
814
|
+
}
|
|
740
815
|
// `--locked` keeps the user's lock file untouched; a workspace that is out of
|
|
741
816
|
// date with its lock file needs the retry without it.
|
|
742
817
|
for (const lockArgs of [["--locked"], []]) {
|
|
@@ -810,7 +885,7 @@ function annotateCargoDependencyKinds(
|
|
|
810
885
|
let anyMetadata = false;
|
|
811
886
|
let everyMetadata = true;
|
|
812
887
|
for (const cargoTomlFile of cargoFiles) {
|
|
813
|
-
const metadata = runCargoMetadata(cargoTomlFile);
|
|
888
|
+
const metadata = runCargoMetadata(cargoTomlFile, undefined, options);
|
|
814
889
|
if (!metadata) {
|
|
815
890
|
everyMetadata = false;
|
|
816
891
|
continue;
|
|
@@ -824,7 +899,11 @@ function annotateCargoDependencyKinds(
|
|
|
824
899
|
visitedWorkspaceRoots.add(workspaceRoot);
|
|
825
900
|
anyMetadata = true;
|
|
826
901
|
if (CARGO_TARGET) {
|
|
827
|
-
const targetMetadata = runCargoMetadata(
|
|
902
|
+
const targetMetadata = runCargoMetadata(
|
|
903
|
+
cargoTomlFile,
|
|
904
|
+
CARGO_TARGET,
|
|
905
|
+
options,
|
|
906
|
+
);
|
|
828
907
|
if (targetMetadata) {
|
|
829
908
|
for (const key of parseCargoMetadataResolve(targetMetadata)
|
|
830
909
|
.resolvedKeys) {
|
|
@@ -962,13 +1041,31 @@ export async function createRustBom(path, options) {
|
|
|
962
1041
|
`${options.multiProject ? "**/" : ""}Cargo.toml`,
|
|
963
1042
|
options,
|
|
964
1043
|
);
|
|
1044
|
+
// `cargo check` compiles the project, running its build scripts and
|
|
1045
|
+
// procedural macros, so --deep runs it only when the project's own tooling
|
|
1046
|
+
// may run.
|
|
1047
|
+
const cargoToolingAllowed = isProjectToolingAllowed(options);
|
|
1048
|
+
if (
|
|
1049
|
+
options.deep &&
|
|
1050
|
+
cargoFiles.length &&
|
|
1051
|
+
!cargoToolingAllowed &&
|
|
1052
|
+
!hasAnyProjectType(["oci"], options, false)
|
|
1053
|
+
) {
|
|
1054
|
+
reportProjectToolingSkipped(
|
|
1055
|
+
options,
|
|
1056
|
+
null,
|
|
1057
|
+
"Skipping 'cargo check' since dependency installation is disabled. Build scripts and procedural macros are not run, so the files each crate uses are not recorded.",
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
965
1060
|
// Attempt to build or generate lock files automatically
|
|
966
1061
|
for (const f of cargoFiles) {
|
|
967
1062
|
// If there are no cargo.lock files, we can attempt a cargo install when
|
|
968
1063
|
// options.deep is true or options.lifecycle == build or post-build with installDeps
|
|
969
1064
|
// Why the need for installDeps? It currently defaults to true, so let's obey if someone wants no installs
|
|
970
1065
|
if (
|
|
971
|
-
(options.deep &&
|
|
1066
|
+
(options.deep &&
|
|
1067
|
+
cargoToolingAllowed &&
|
|
1068
|
+
!hasAnyProjectType(["oci"], options, false)) ||
|
|
972
1069
|
(options.installDeps &&
|
|
973
1070
|
!safeExistsSync(f.replace(".toml", ".lock")) &&
|
|
974
1071
|
["build", "post-build"].includes(options.lifecycle))
|
|
@@ -1870,11 +1967,35 @@ export function createClojureBom(path, options) {
|
|
|
1870
1967
|
if (readEnvironmentVariable("LEIN_ARGS")) {
|
|
1871
1968
|
LEIN_ARGS = readEnvironmentVariable("LEIN_ARGS").split(" ");
|
|
1872
1969
|
}
|
|
1970
|
+
// `lein deps :tree-data` evaluates project.clj, which is a Clojure
|
|
1971
|
+
// program, so it belongs to the dependency-tooling surface disabled by
|
|
1972
|
+
// --no-install-deps and the pre-build lifecycle.
|
|
1973
|
+
const leinToolingAllowed = isProjectToolingAllowed(options);
|
|
1873
1974
|
for (const f of leinFiles) {
|
|
1874
1975
|
if (DEBUG_MODE) {
|
|
1875
1976
|
console.log(`Parsing ${f}`);
|
|
1876
1977
|
}
|
|
1877
1978
|
const basePath = dirname(f);
|
|
1979
|
+
if (!leinToolingAllowed) {
|
|
1980
|
+
reportProjectToolingSkipped(
|
|
1981
|
+
options,
|
|
1982
|
+
"clojure.lein.tooling-disabled",
|
|
1983
|
+
"Skipping 'lein deps :tree-data' since dependency installation is disabled. Only the dependencies declared in project.clj will be used.",
|
|
1984
|
+
{
|
|
1985
|
+
ecosystem: "clojure",
|
|
1986
|
+
tool: "lein",
|
|
1987
|
+
impact: "transitive-deps",
|
|
1988
|
+
detail:
|
|
1989
|
+
"Dependency installation is disabled, so project.clj was not evaluated and only its directly declared dependencies are available.",
|
|
1990
|
+
},
|
|
1991
|
+
);
|
|
1992
|
+
const leinData = readFileSync(f, { encoding: "utf-8" });
|
|
1993
|
+
const dlist = parseLeiningenData(leinData);
|
|
1994
|
+
if (dlist?.length) {
|
|
1995
|
+
pkgList = pkgList.concat(dlist);
|
|
1996
|
+
}
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1878
1999
|
console.log("Executing", LEIN_CMD, "in", basePath);
|
|
1879
2000
|
const result = safeSpawnSync(LEIN_CMD, LEIN_ARGS, {
|
|
1880
2001
|
cwd: basePath,
|
|
@@ -2235,7 +2356,39 @@ export async function createSwiftBom(path, options) {
|
|
|
2235
2356
|
);
|
|
2236
2357
|
}
|
|
2237
2358
|
if (swiftFiles.length) {
|
|
2359
|
+
// A SwiftPM manifest is a Swift program, so `swift package dump-package`
|
|
2360
|
+
// and `show-dependencies` execute the scanned project's code (and fetch
|
|
2361
|
+
// missing dependencies). They belong to the dependency-tooling surface
|
|
2362
|
+
// disabled by --no-install-deps and the pre-build lifecycle; the BOM then
|
|
2363
|
+
// comes from Package.resolved alone.
|
|
2364
|
+
const swiftToolingAllowed = isProjectToolingAllowed(options);
|
|
2365
|
+
if (!swiftToolingAllowed) {
|
|
2366
|
+
reportProjectToolingSkipped(
|
|
2367
|
+
options,
|
|
2368
|
+
"swift.tooling-disabled",
|
|
2369
|
+
"Skipping 'swift package show-dependencies' since dependency installation is disabled. Only the packages in Package.resolved will be used.",
|
|
2370
|
+
{
|
|
2371
|
+
ecosystem: "swift",
|
|
2372
|
+
tool: "swift",
|
|
2373
|
+
impact: "transitive-deps",
|
|
2374
|
+
detail:
|
|
2375
|
+
"Dependency installation is disabled, so the Package.swift manifest was not evaluated and only the packages from Package.resolved are available.",
|
|
2376
|
+
},
|
|
2377
|
+
);
|
|
2378
|
+
// The manifest is not run, so a project without Package.resolved still
|
|
2379
|
+
// gets a parent component named after its directory.
|
|
2380
|
+
if (!Object.keys(parentComponent).length) {
|
|
2381
|
+
parentComponent = createDefaultParentComponent(
|
|
2382
|
+
swiftFiles[0],
|
|
2383
|
+
"swift",
|
|
2384
|
+
options,
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2238
2388
|
for (const f of swiftFiles) {
|
|
2389
|
+
if (!swiftToolingAllowed) {
|
|
2390
|
+
break;
|
|
2391
|
+
}
|
|
2239
2392
|
const basePath = dirname(f);
|
|
2240
2393
|
if (completedPath.includes(basePath)) {
|
|
2241
2394
|
continue;
|
|
@@ -2461,10 +2614,33 @@ export async function createCocoaBom(path, options) {
|
|
|
2461
2614
|
allDependencies.set(name, podDependencies);
|
|
2462
2615
|
}
|
|
2463
2616
|
const targetDependencies = new Map();
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2617
|
+
// `pod ipc podfile-json` evaluates the Podfile, which is a Ruby program,
|
|
2618
|
+
// so per-target filtering through it belongs to the dependency-tooling
|
|
2619
|
+
// surface disabled by --no-install-deps and the pre-build lifecycle. A
|
|
2620
|
+
// gated scan then keeps every pod the lockfile resolved (the union across
|
|
2621
|
+
// targets) instead of the configured target subset.
|
|
2622
|
+
const cocoaTargetsConfigured =
|
|
2623
|
+
readEnvironmentVariable("COCOA_INCLUDED_TARGETS") ||
|
|
2624
|
+
readEnvironmentVariable("COCOA_EXCLUDED_TARGETS");
|
|
2625
|
+
const cocoaToolingAllowed = isProjectToolingAllowed(options);
|
|
2626
|
+
// True only when target names the Podfile actually resolved are known, so
|
|
2627
|
+
// the include/exclude filters below can be applied to real targets.
|
|
2628
|
+
let targetFilteringActive = false;
|
|
2629
|
+
if (!cocoaTargetsConfigured || !cocoaToolingAllowed) {
|
|
2630
|
+
if (cocoaTargetsConfigured) {
|
|
2631
|
+
reportProjectToolingSkipped(
|
|
2632
|
+
options,
|
|
2633
|
+
"cocoa.podfile-json.tooling-disabled",
|
|
2634
|
+
"Skipping 'pod ipc podfile-json' since dependency installation is disabled. Every pod from Podfile.lock is included without per-target filtering.",
|
|
2635
|
+
{
|
|
2636
|
+
ecosystem: "cocoa",
|
|
2637
|
+
tool: "pod",
|
|
2638
|
+
impact: "components",
|
|
2639
|
+
detail:
|
|
2640
|
+
"Dependency installation is disabled, so the Podfile was not evaluated and target filtering fell back to all pods resolved in Podfile.lock.",
|
|
2641
|
+
},
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2468
2644
|
targetDependencies.set("Pods", podfileLock["DEPENDENCIES"]);
|
|
2469
2645
|
} else {
|
|
2470
2646
|
const result = executePodCommand(
|
|
@@ -2472,18 +2648,39 @@ export async function createCocoaBom(path, options) {
|
|
|
2472
2648
|
projectPath,
|
|
2473
2649
|
options,
|
|
2474
2650
|
);
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
resolvedPodFile
|
|
2478
|
-
|
|
2479
|
-
|
|
2651
|
+
let resolvedPodFile;
|
|
2652
|
+
try {
|
|
2653
|
+
resolvedPodFile = JSON.parse(result.stdout);
|
|
2654
|
+
} catch (_e) {
|
|
2655
|
+
resolvedPodFile = undefined;
|
|
2656
|
+
}
|
|
2657
|
+
if (resolvedPodFile?.target_definitions?.length) {
|
|
2658
|
+
parsePodfileTargets(
|
|
2659
|
+
resolvedPodFile["target_definitions"][0],
|
|
2660
|
+
targetDependencies,
|
|
2661
|
+
);
|
|
2662
|
+
targetFilteringActive = true;
|
|
2663
|
+
} else {
|
|
2664
|
+
// pod failed or printed nothing usable (it refuses to run as root,
|
|
2665
|
+
// for one), and an unparseable Podfile used to abort the whole scan.
|
|
2666
|
+
// Keep every locked pod rather than none.
|
|
2667
|
+
console.log(
|
|
2668
|
+
"The Podfile could not be resolved with CocoaPods, so all pods from Podfile.lock are included without per-target filtering.",
|
|
2669
|
+
);
|
|
2670
|
+
targetDependencies.set("Pods", podfileLock["DEPENDENCIES"]);
|
|
2671
|
+
}
|
|
2480
2672
|
}
|
|
2481
2673
|
const usedTargets = new Set(
|
|
2482
|
-
readEnvironmentVariable("COCOA_INCLUDED_TARGETS")
|
|
2674
|
+
targetFilteringActive && readEnvironmentVariable("COCOA_INCLUDED_TARGETS")
|
|
2483
2675
|
? readEnvironmentVariable("COCOA_INCLUDED_TARGETS").split(",")
|
|
2484
2676
|
: targetDependencies.keys(),
|
|
2485
2677
|
);
|
|
2486
|
-
|
|
2678
|
+
// Without the resolved Podfile there are no target names to exclude, and
|
|
2679
|
+
// excluding the lockfile's stand-in "Pods" target would drop every pod.
|
|
2680
|
+
if (
|
|
2681
|
+
targetFilteringActive &&
|
|
2682
|
+
readEnvironmentVariable("COCOA_EXCLUDED_TARGETS")
|
|
2683
|
+
) {
|
|
2487
2684
|
readEnvironmentVariable("COCOA_EXCLUDED_TARGETS")
|
|
2488
2685
|
.split(",")
|
|
2489
2686
|
.forEach((excludedTarget) => {
|
package/lib/core/env.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
1
|
+
import { join, win32 } from "node:path";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
buildReadCountSuffix,
|
|
@@ -301,6 +301,13 @@ export const SWIFT_CMD = readEnvironmentVariable("SWIFT_CMD") || "swift";
|
|
|
301
301
|
/** Resolved Ruby executable command (RUBY_CMD env var, or "ruby"). */
|
|
302
302
|
export const RUBY_CMD = readEnvironmentVariable("RUBY_CMD") || "ruby";
|
|
303
303
|
|
|
304
|
+
/** Resolved PHP executable command (PHP_CMD env var, or "php"). */
|
|
305
|
+
export const PHP_CMD = readEnvironmentVariable("PHP_CMD") || "php";
|
|
306
|
+
|
|
307
|
+
/** Resolved Composer executable command (COMPOSER_CMD env var, or "composer"). */
|
|
308
|
+
export const COMPOSER_CMD =
|
|
309
|
+
readEnvironmentVariable("COMPOSER_CMD") || "composer";
|
|
310
|
+
|
|
304
311
|
/** Python package components that can be excluded from the generated BOM. */
|
|
305
312
|
export const PYTHON_EXCLUDED_COMPONENTS = [
|
|
306
313
|
"pip",
|
|
@@ -707,23 +714,38 @@ export function hasAnyProjectType(projectTypes, options, defaultStatus = true) {
|
|
|
707
714
|
return shouldInclude;
|
|
708
715
|
}
|
|
709
716
|
|
|
717
|
+
/**
|
|
718
|
+
* Extract the lowercase command name from an invoked command path, without
|
|
719
|
+
* directories or a trailing `.js`, `.cjs`, `.mjs` or `.exe` extension. Both
|
|
720
|
+
* `/` and `\\` separators are handled, so a Windows path is inspected the
|
|
721
|
+
* same way on any host.
|
|
722
|
+
*
|
|
723
|
+
* @param {string} [commandPath] Invoked command path or name
|
|
724
|
+
* @returns {string} Lowercase command name
|
|
725
|
+
*/
|
|
726
|
+
export function getCommandName(commandPath) {
|
|
727
|
+
return win32
|
|
728
|
+
.basename(`${commandPath || ""}`)
|
|
729
|
+
.toLowerCase()
|
|
730
|
+
.replace(/\.(?:[cm]?js|exe)$/u, "");
|
|
731
|
+
}
|
|
732
|
+
|
|
710
733
|
/**
|
|
711
734
|
* Determine whether the predictive dependency audit should run for the current
|
|
712
735
|
* CLI invocation.
|
|
713
736
|
*
|
|
714
|
-
* OBOM
|
|
715
|
-
*
|
|
737
|
+
* OBOM and HBOM runs (commands named `obom` or `hbom`, including `hbom-slim`)
|
|
738
|
+
* and explicit `-t os` / OS / hardware project types alone keep the direct BOM
|
|
739
|
+
* audit findings but skip the predictive dependency audit. Only the command
|
|
740
|
+
* name is tested, never the directories leading to it.
|
|
716
741
|
*
|
|
717
742
|
* @param {object} options CLI options
|
|
718
743
|
* @param {string} [commandPath] Invoked command path or name
|
|
719
744
|
* @returns {boolean} True when predictive dependency audit should run
|
|
720
745
|
*/
|
|
721
746
|
export function shouldRunPredictiveBomAudit(options, commandPath) {
|
|
722
|
-
const
|
|
723
|
-
if (
|
|
724
|
-
return false;
|
|
725
|
-
}
|
|
726
|
-
if (normalizedCommandPath.includes("hbom")) {
|
|
747
|
+
const commandName = getCommandName(commandPath);
|
|
748
|
+
if (commandName.startsWith("obom") || commandName.startsWith("hbom")) {
|
|
727
749
|
return false;
|
|
728
750
|
}
|
|
729
751
|
const projectTypes = Array.isArray(options?.projectType)
|
package/lib/core/fs.js
CHANGED
|
@@ -522,8 +522,13 @@ function isWindowsShellHijackRisk(command, options) {
|
|
|
522
522
|
const absCwd = resolve(cwd);
|
|
523
523
|
for (const candidate of candidates) {
|
|
524
524
|
const candidatePath = path.join(absCwd, candidate);
|
|
525
|
-
|
|
526
|
-
|
|
525
|
+
// cmd.exe only runs files, so a directory named like the tool is harmless.
|
|
526
|
+
try {
|
|
527
|
+
if (statSync(candidatePath).isFile()) {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
} catch (_err) {
|
|
531
|
+
// Not present.
|
|
527
532
|
}
|
|
528
533
|
}
|
|
529
534
|
return false;
|
|
@@ -831,24 +836,28 @@ export function safeSpawnSync(command, args, options) {
|
|
|
831
836
|
error: new Error("No execute permission"),
|
|
832
837
|
};
|
|
833
838
|
}
|
|
839
|
+
// cmd.exe looks in the working directory before PATH, so a scanned project
|
|
840
|
+
// could otherwise replace the tool cdxgen means to run. This holds outside
|
|
841
|
+
// secure mode too, since --no-install-deps scans still run version probes
|
|
842
|
+
// and read-only tools with the project as the working directory.
|
|
843
|
+
if (isWindowsShellHijackRisk(command, options)) {
|
|
844
|
+
const blockedReason = `${command} matches local file in cwd (Windows shell hijack risk)`;
|
|
845
|
+
console.warn(`\x1b[1;31mSecurity Alert: ${blockedReason}\x1b[0m`);
|
|
846
|
+
recordActivity({
|
|
847
|
+
kind: activityDescriptor.kind,
|
|
848
|
+
...activityDescriptor.metadata,
|
|
849
|
+
reason: blockedReason,
|
|
850
|
+
status: "blocked",
|
|
851
|
+
target: activityDescriptor.target,
|
|
852
|
+
});
|
|
853
|
+
return {
|
|
854
|
+
status: 1,
|
|
855
|
+
stdout: undefined,
|
|
856
|
+
stderr: undefined,
|
|
857
|
+
error: new Error(blockedReason),
|
|
858
|
+
};
|
|
859
|
+
}
|
|
834
860
|
if (isSecureMode) {
|
|
835
|
-
if (isWindowsShellHijackRisk(command, options)) {
|
|
836
|
-
const blockedReason = `${command} matches local file in cwd (Windows shell hijack risk)`;
|
|
837
|
-
console.warn(`\x1b[1;31mSecurity Alert: ${blockedReason}\x1b[0m`);
|
|
838
|
-
recordActivity({
|
|
839
|
-
kind: activityDescriptor.kind,
|
|
840
|
-
...activityDescriptor.metadata,
|
|
841
|
-
reason: blockedReason,
|
|
842
|
-
status: "blocked",
|
|
843
|
-
target: activityDescriptor.target,
|
|
844
|
-
});
|
|
845
|
-
return {
|
|
846
|
-
status: 1,
|
|
847
|
-
stdout: undefined,
|
|
848
|
-
stderr: undefined,
|
|
849
|
-
error: new Error(blockedReason),
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
861
|
if (options?.cwd && options.cwd !== resolve(options.cwd)) {
|
|
853
862
|
if (DEBUG_MODE) {
|
|
854
863
|
console.log(
|