@cyclonedx/cdxgen 11.4.3 → 11.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 +63 -63
- package/bin/cdxgen.js +32 -9
- package/lib/cli/index.js +341 -98
- package/lib/helpers/envcontext.js +20 -18
- package/lib/helpers/logger.js +0 -2
- package/lib/helpers/utils.js +763 -63
- package/lib/helpers/utils.test.js +448 -27
- package/lib/helpers/validator.js +10 -0
- package/lib/managers/binary.js +89 -11
- package/lib/managers/docker.js +47 -37
- package/lib/server/server.js +120 -6
- package/lib/server/server.test.js +235 -0
- package/package.json +33 -14
- package/types/lib/cli/index.d.ts +8 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/envcontext.d.ts +3 -8
- package/types/lib/helpers/envcontext.d.ts.map +1 -1
- package/types/lib/helpers/logger.d.ts +0 -1
- package/types/lib/helpers/logger.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +67 -66
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/helpers/validator.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +22 -2
- package/types/lib/server/server.d.ts.map +1 -1
package/lib/cli/index.js
CHANGED
|
@@ -111,6 +111,8 @@ import {
|
|
|
111
111
|
parseCsProjAssetsData,
|
|
112
112
|
parseCsProjData,
|
|
113
113
|
parseEdnData,
|
|
114
|
+
parseFlakeLock,
|
|
115
|
+
parseFlakeNix,
|
|
114
116
|
parseGemfileLockData,
|
|
115
117
|
parseGemspecData,
|
|
116
118
|
parseGitHubWorkflowData,
|
|
@@ -524,29 +526,47 @@ const addFormulationSection = (options, context) => {
|
|
|
524
526
|
let environmentVars = gitBranch?.length
|
|
525
527
|
? [{ name: "GIT_BRANCH", value: gitBranch }]
|
|
526
528
|
: [];
|
|
529
|
+
const envPrefixes = [
|
|
530
|
+
"GIT",
|
|
531
|
+
"ANDROID",
|
|
532
|
+
"DENO",
|
|
533
|
+
"DOTNET",
|
|
534
|
+
"JAVA_",
|
|
535
|
+
"SDKMAN",
|
|
536
|
+
"CARGO",
|
|
537
|
+
"CONDA",
|
|
538
|
+
"RUST",
|
|
539
|
+
"GEM_",
|
|
540
|
+
"SCALA_",
|
|
541
|
+
"MAVEN_",
|
|
542
|
+
"GRADLE_",
|
|
543
|
+
"NODE_",
|
|
544
|
+
];
|
|
545
|
+
const envBlocklist = [
|
|
546
|
+
"key",
|
|
547
|
+
"token",
|
|
548
|
+
"pass",
|
|
549
|
+
"secret",
|
|
550
|
+
"user",
|
|
551
|
+
"email",
|
|
552
|
+
"auth",
|
|
553
|
+
"session",
|
|
554
|
+
"proxy",
|
|
555
|
+
"cred",
|
|
556
|
+
];
|
|
557
|
+
|
|
527
558
|
for (const aevar of Object.keys(process.env)) {
|
|
559
|
+
const lower = aevar.toLowerCase();
|
|
560
|
+
const value = process.env[aevar] ?? "";
|
|
528
561
|
if (
|
|
529
|
-
(aevar.startsWith(
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
aevar.startsWith("JAVA_") ||
|
|
534
|
-
aevar.startsWith("SDKMAN") ||
|
|
535
|
-
aevar.startsWith("CARGO") ||
|
|
536
|
-
aevar.startsWith("CONDA") ||
|
|
537
|
-
aevar.startsWith("RUST")) &&
|
|
538
|
-
!aevar.toLowerCase().includes("key") &&
|
|
539
|
-
!aevar.toLowerCase().includes("token") &&
|
|
540
|
-
!aevar.toLowerCase().includes("pass") &&
|
|
541
|
-
!aevar.toLowerCase().includes("secret") &&
|
|
542
|
-
!aevar.toLowerCase().includes("user") &&
|
|
543
|
-
!aevar.toLowerCase().includes("email") &&
|
|
544
|
-
process.env[aevar] &&
|
|
545
|
-
process.env[aevar].length
|
|
562
|
+
envPrefixes.some((p) => aevar.startsWith(p)) &&
|
|
563
|
+
!envBlocklist.some((b) => lower.includes(b)) &&
|
|
564
|
+
!envBlocklist.some((b) => value.includes(b)) &&
|
|
565
|
+
value.length
|
|
546
566
|
) {
|
|
547
567
|
environmentVars.push({
|
|
548
568
|
name: aevar,
|
|
549
|
-
value
|
|
569
|
+
value,
|
|
550
570
|
});
|
|
551
571
|
}
|
|
552
572
|
}
|
|
@@ -1385,6 +1405,10 @@ export async function createJarBom(path, options) {
|
|
|
1385
1405
|
if (!options.exclude) {
|
|
1386
1406
|
options.exclude = [];
|
|
1387
1407
|
}
|
|
1408
|
+
// We can look for jar files within node_modules directory
|
|
1409
|
+
if (typeof options.includeNodeModulesDir === "undefined" || options.deep) {
|
|
1410
|
+
options.includeNodeModulesDir = true;
|
|
1411
|
+
}
|
|
1388
1412
|
// Exclude certain directories during oci sbom generation
|
|
1389
1413
|
if (hasAnyProjectType(["oci"], options, false)) {
|
|
1390
1414
|
options.exclude.push("**/android-sdk*/**");
|
|
@@ -2305,7 +2329,7 @@ export async function createJavaBom(path, options) {
|
|
|
2305
2329
|
) {
|
|
2306
2330
|
bArgs = ["--bazelrc=.bazelrc", "build", bazelTarget];
|
|
2307
2331
|
}
|
|
2308
|
-
console.log("Executing", BAZEL_CMD,
|
|
2332
|
+
console.log("Executing", BAZEL_CMD, "in", basePath);
|
|
2309
2333
|
let result = safeSpawnSync(BAZEL_CMD, bArgs, {
|
|
2310
2334
|
cwd: basePath,
|
|
2311
2335
|
shell: true,
|
|
@@ -2639,7 +2663,7 @@ export async function createJavaBom(path, options) {
|
|
|
2639
2663
|
}
|
|
2640
2664
|
const millArgs = [...millCommonArgs, "__.ivyDepsTree"];
|
|
2641
2665
|
if (DEBUG_MODE) {
|
|
2642
|
-
console.log("Executing", millCmd,
|
|
2666
|
+
console.log("Executing", millCmd, "in", millRootPath);
|
|
2643
2667
|
}
|
|
2644
2668
|
let sresult = safeSpawnSync(millCmd, millArgs, {
|
|
2645
2669
|
cwd: millRootPath,
|
|
@@ -2900,6 +2924,10 @@ export async function createNodejsBom(path, options) {
|
|
|
2900
2924
|
pkgMgr = mgr;
|
|
2901
2925
|
} else if (pkgData?.engines?.yarn) {
|
|
2902
2926
|
pkgMgr = "yarn";
|
|
2927
|
+
} else if (
|
|
2928
|
+
isPackageManagerAllowed("npm", ["yarn", "pnpm", "rush"], options)
|
|
2929
|
+
) {
|
|
2930
|
+
pkgMgr = "npm";
|
|
2903
2931
|
} else if (
|
|
2904
2932
|
isPackageManagerAllowed("yarn", ["npm", "pnpm", "rush"], options)
|
|
2905
2933
|
) {
|
|
@@ -3327,10 +3355,10 @@ export async function createNodejsBom(path, options) {
|
|
|
3327
3355
|
});
|
|
3328
3356
|
}
|
|
3329
3357
|
if (safeExistsSync(pnpmLock)) {
|
|
3330
|
-
|
|
3358
|
+
const pnpmLockObj = await parsePnpmLock(pnpmLock);
|
|
3331
3359
|
if (allImports && Object.keys(allImports).length) {
|
|
3332
3360
|
pkgList = await addEvidenceForImports(
|
|
3333
|
-
pkgList,
|
|
3361
|
+
pnpmLockObj.pkgList,
|
|
3334
3362
|
allImports,
|
|
3335
3363
|
allExports,
|
|
3336
3364
|
options.deep,
|
|
@@ -3757,7 +3785,7 @@ export async function createPythonBom(path, options) {
|
|
|
3757
3785
|
// Retrieve the tree using virtualenv in deep mode and as a fallback
|
|
3758
3786
|
// This is a slow operation
|
|
3759
3787
|
if ((options.deep || !dependencies.length) && !f.endsWith("uv.lock")) {
|
|
3760
|
-
retMap = getPipFrozenTree(basePath, f, tempDir, parentComponent);
|
|
3788
|
+
retMap = await getPipFrozenTree(basePath, f, tempDir, parentComponent);
|
|
3761
3789
|
if (retMap.pkgList?.length) {
|
|
3762
3790
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
3763
3791
|
}
|
|
@@ -3835,9 +3863,49 @@ export async function createPythonBom(path, options) {
|
|
|
3835
3863
|
console.error("Pipfile.lock not found at", path);
|
|
3836
3864
|
options.failOnError && process.exit(1);
|
|
3837
3865
|
}
|
|
3838
|
-
} else if (
|
|
3839
|
-
|
|
3840
|
-
|
|
3866
|
+
} else if (reqDirFiles?.length) {
|
|
3867
|
+
for (const j in reqDirFiles) {
|
|
3868
|
+
const f = reqDirFiles[j];
|
|
3869
|
+
const dlist = await parseReqFile(f, false);
|
|
3870
|
+
if (dlist?.length) {
|
|
3871
|
+
pkgList = pkgList.concat(dlist);
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
metadataFilename = reqDirFiles.join(", ");
|
|
3875
|
+
} else if (reqFiles?.length) {
|
|
3876
|
+
for (const f of reqFiles) {
|
|
3877
|
+
const dlist = await parseReqFile(f, true);
|
|
3878
|
+
if (dlist?.length) {
|
|
3879
|
+
pkgList = pkgList.concat(dlist);
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
const parentDependsOn = new Set();
|
|
3886
|
+
|
|
3887
|
+
// Use atom in requirements, setup.py and pyproject.toml mode
|
|
3888
|
+
if (requirementsMode || setupPyMode || pyProjectMode || options.deep) {
|
|
3889
|
+
/**
|
|
3890
|
+
* The order of preference is pyproject.toml (newer) and then setup.py
|
|
3891
|
+
*/
|
|
3892
|
+
if (options.installDeps) {
|
|
3893
|
+
let pkgMap;
|
|
3894
|
+
if (pyProjectMode && !poetryMode) {
|
|
3895
|
+
pkgMap = await getPipFrozenTree(
|
|
3896
|
+
path,
|
|
3897
|
+
pyProjectFile,
|
|
3898
|
+
tempDir,
|
|
3899
|
+
parentComponent,
|
|
3900
|
+
);
|
|
3901
|
+
} else if (setupPyMode) {
|
|
3902
|
+
pkgMap = await getPipFrozenTree(
|
|
3903
|
+
path,
|
|
3904
|
+
setupPy,
|
|
3905
|
+
tempDir,
|
|
3906
|
+
parentComponent,
|
|
3907
|
+
);
|
|
3908
|
+
} else if (requirementsMode && reqFiles?.length) {
|
|
3841
3909
|
if (options.installDeps && DEBUG_MODE) {
|
|
3842
3910
|
console.log(
|
|
3843
3911
|
"cdxgen will now attempt to generate an SBOM for 'build' lifecycle phase for Python. This would take some time ...\nTo speed up this step, invoke cdxgen from within a virtual environment with all the dependencies installed.\nAlternatively, pass the argument '--lifecycle pre-build' to generate a faster but less precise SBOM.",
|
|
@@ -3845,13 +3913,8 @@ export async function createPythonBom(path, options) {
|
|
|
3845
3913
|
}
|
|
3846
3914
|
for (const f of reqFiles) {
|
|
3847
3915
|
const basePath = dirname(f);
|
|
3848
|
-
let reqData;
|
|
3849
|
-
let frozen = false;
|
|
3850
|
-
// Attempt to pip freeze in a virtualenv to improve precision
|
|
3851
3916
|
if (options.installDeps) {
|
|
3852
|
-
|
|
3853
|
-
// adding to the delay.
|
|
3854
|
-
const pkgMap = getPipFrozenTree(
|
|
3917
|
+
const pkgMap = await getPipFrozenTree(
|
|
3855
3918
|
basePath,
|
|
3856
3919
|
f,
|
|
3857
3920
|
tempDir,
|
|
@@ -3859,10 +3922,11 @@ export async function createPythonBom(path, options) {
|
|
|
3859
3922
|
);
|
|
3860
3923
|
if (pkgMap.pkgList?.length) {
|
|
3861
3924
|
pkgList = pkgList.concat(pkgMap.pkgList);
|
|
3862
|
-
|
|
3925
|
+
pkgList = trimComponents(pkgList);
|
|
3863
3926
|
}
|
|
3864
3927
|
if (pkgMap.formulationList?.length) {
|
|
3865
3928
|
formulationList = formulationList.concat(pkgMap.formulationList);
|
|
3929
|
+
formulationList = trimComponents(formulationList);
|
|
3866
3930
|
}
|
|
3867
3931
|
if (pkgMap.dependenciesList) {
|
|
3868
3932
|
dependencies = mergeDependencies(
|
|
@@ -3871,61 +3935,31 @@ export async function createPythonBom(path, options) {
|
|
|
3871
3935
|
parentComponent,
|
|
3872
3936
|
);
|
|
3873
3937
|
}
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3938
|
+
// Add the root packages from this file to the parent's dependencies
|
|
3939
|
+
for (const p of pkgMap.rootList) {
|
|
3940
|
+
if (
|
|
3941
|
+
parentComponent &&
|
|
3942
|
+
p.name === parentComponent.name &&
|
|
3943
|
+
(p.version === parentComponent.version ||
|
|
3944
|
+
p.version === "latest")
|
|
3945
|
+
) {
|
|
3946
|
+
continue;
|
|
3947
|
+
}
|
|
3948
|
+
parentDependsOn.add(
|
|
3949
|
+
`pkg:pypi/${p.name.toLowerCase()}@${p.version}`,
|
|
3883
3950
|
);
|
|
3884
3951
|
}
|
|
3885
|
-
reqData = readFileSync(f, { encoding: "utf-8" });
|
|
3886
|
-
const dlist = await parseReqFile(reqData, true);
|
|
3887
|
-
if (dlist?.length) {
|
|
3888
|
-
pkgList = pkgList.concat(dlist);
|
|
3889
|
-
}
|
|
3890
|
-
}
|
|
3891
|
-
} // for
|
|
3892
|
-
metadataFilename = reqFiles.join(", ");
|
|
3893
|
-
} else if (reqDirFiles?.length) {
|
|
3894
|
-
for (const j in reqDirFiles) {
|
|
3895
|
-
const f = reqDirFiles[j];
|
|
3896
|
-
const reqData = readFileSync(f, { encoding: "utf-8" });
|
|
3897
|
-
const dlist = await parseReqFile(reqData, false);
|
|
3898
|
-
if (dlist?.length) {
|
|
3899
|
-
pkgList = pkgList.concat(dlist);
|
|
3900
3952
|
}
|
|
3901
3953
|
}
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
}
|
|
3905
|
-
}
|
|
3906
|
-
// Use atom in requirements, setup.py and pyproject.toml mode
|
|
3907
|
-
if (requirementsMode || setupPyMode || pyProjectMode || options.deep) {
|
|
3908
|
-
/**
|
|
3909
|
-
* The order of preference is pyproject.toml (newer) and then setup.py
|
|
3910
|
-
*/
|
|
3911
|
-
if (options.installDeps) {
|
|
3912
|
-
let pkgMap;
|
|
3913
|
-
if (pyProjectMode && !poetryMode) {
|
|
3914
|
-
pkgMap = getPipFrozenTree(
|
|
3954
|
+
} else if (!poetryMode) {
|
|
3955
|
+
pkgMap = await getPipFrozenTree(
|
|
3915
3956
|
path,
|
|
3916
|
-
|
|
3957
|
+
undefined,
|
|
3917
3958
|
tempDir,
|
|
3918
3959
|
parentComponent,
|
|
3919
3960
|
);
|
|
3920
|
-
} else if (setupPyMode) {
|
|
3921
|
-
pkgMap = getPipFrozenTree(path, setupPy, tempDir, parentComponent);
|
|
3922
|
-
} else if (!poetryMode) {
|
|
3923
|
-
pkgMap = getPipFrozenTree(path, undefined, tempDir, parentComponent);
|
|
3924
3961
|
}
|
|
3925
3962
|
|
|
3926
|
-
// Get the imported modules and a dedupe list of packages
|
|
3927
|
-
const parentDependsOn = new Set();
|
|
3928
|
-
|
|
3929
3963
|
// ATOM parsedeps block
|
|
3930
3964
|
// Atom parsedeps slices can be used to identify packages that are not declared in manifests
|
|
3931
3965
|
// Since it is a slow operation, we only use it as a fallback or in deep mode
|
|
@@ -4249,11 +4283,21 @@ export async function createGoBom(path, options) {
|
|
|
4249
4283
|
}
|
|
4250
4284
|
if (gomodFiles.length) {
|
|
4251
4285
|
let shouldManuallyParse = false;
|
|
4286
|
+
// Sort go.mod files by depth (shallowest first) to prioritize root modules
|
|
4287
|
+
const sortedGomodFiles = gomodFiles.sort((a, b) => {
|
|
4288
|
+
const relativePathA = relative(path, a);
|
|
4289
|
+
const relativePathB = relative(path, b);
|
|
4290
|
+
const depthA = relativePathA.split("/").length;
|
|
4291
|
+
const depthB = relativePathB.split("/").length;
|
|
4292
|
+
return depthA - depthB;
|
|
4293
|
+
});
|
|
4294
|
+
|
|
4295
|
+
let rootParentComponent = null;
|
|
4252
4296
|
// Use the go list -deps and go mod why commands to generate a good quality BOM for non-docker invocations
|
|
4253
4297
|
if (
|
|
4254
4298
|
!hasAnyProjectType(["docker", "oci", "container", "os"], options, false)
|
|
4255
4299
|
) {
|
|
4256
|
-
for (const f of
|
|
4300
|
+
for (const f of sortedGomodFiles) {
|
|
4257
4301
|
const basePath = dirname(f);
|
|
4258
4302
|
// Ignore vendor packages
|
|
4259
4303
|
if (
|
|
@@ -4303,13 +4347,23 @@ export async function createGoBom(path, options) {
|
|
|
4303
4347
|
if (retMap.pkgList?.length) {
|
|
4304
4348
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
4305
4349
|
}
|
|
4306
|
-
//
|
|
4350
|
+
// Prioritize the shallowest module as the root component
|
|
4307
4351
|
if (
|
|
4308
4352
|
retMap.parentComponent &&
|
|
4309
4353
|
Object.keys(retMap.parentComponent).length
|
|
4310
4354
|
) {
|
|
4311
|
-
|
|
4312
|
-
|
|
4355
|
+
if (!rootParentComponent) {
|
|
4356
|
+
// First (shallowest) module becomes the root
|
|
4357
|
+
rootParentComponent = retMap.parentComponent;
|
|
4358
|
+
rootParentComponent.type = "application";
|
|
4359
|
+
parentComponent = rootParentComponent;
|
|
4360
|
+
} else {
|
|
4361
|
+
// Subsequent modules become subcomponents
|
|
4362
|
+
if (!parentComponent.components) {
|
|
4363
|
+
parentComponent.components = [];
|
|
4364
|
+
}
|
|
4365
|
+
parentComponent.components.push(retMap.parentComponent);
|
|
4366
|
+
}
|
|
4313
4367
|
}
|
|
4314
4368
|
if (DEBUG_MODE) {
|
|
4315
4369
|
console.log("Executing go mod graph in", basePath);
|
|
@@ -4395,11 +4449,14 @@ export async function createGoBom(path, options) {
|
|
|
4395
4449
|
parentComponent,
|
|
4396
4450
|
);
|
|
4397
4451
|
}
|
|
4398
|
-
// Retain the parent component hierarchy
|
|
4452
|
+
// Retain the parent component hierarchy, prioritizing the shallowest module
|
|
4399
4453
|
if (Object.keys(retMap.parentComponent).length) {
|
|
4400
|
-
if (
|
|
4401
|
-
|
|
4454
|
+
if (!rootParentComponent) {
|
|
4455
|
+
// First (shallowest) module becomes the root
|
|
4456
|
+
rootParentComponent = retMap.parentComponent;
|
|
4457
|
+
parentComponent = rootParentComponent;
|
|
4402
4458
|
} else {
|
|
4459
|
+
// Subsequent modules become subcomponents
|
|
4403
4460
|
parentComponent.components = parentComponent.components || [];
|
|
4404
4461
|
parentComponent.components.push(retMap.parentComponent);
|
|
4405
4462
|
}
|
|
@@ -4425,7 +4482,7 @@ export async function createGoBom(path, options) {
|
|
|
4425
4482
|
dependencies,
|
|
4426
4483
|
parentComponent,
|
|
4427
4484
|
src: path,
|
|
4428
|
-
filename:
|
|
4485
|
+
filename: sortedGomodFiles.join(", "),
|
|
4429
4486
|
});
|
|
4430
4487
|
}
|
|
4431
4488
|
}
|
|
@@ -4437,7 +4494,7 @@ export async function createGoBom(path, options) {
|
|
|
4437
4494
|
"Manually parsing go.mod files. The resultant BOM would be incomplete.",
|
|
4438
4495
|
);
|
|
4439
4496
|
}
|
|
4440
|
-
for (const f of
|
|
4497
|
+
for (const f of sortedGomodFiles) {
|
|
4441
4498
|
if (DEBUG_MODE) {
|
|
4442
4499
|
console.log(`Parsing ${f}`);
|
|
4443
4500
|
}
|
|
@@ -4446,11 +4503,14 @@ export async function createGoBom(path, options) {
|
|
|
4446
4503
|
if (retMap?.pkgList?.length) {
|
|
4447
4504
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
4448
4505
|
}
|
|
4449
|
-
// Retain the parent component hierarchy
|
|
4506
|
+
// Retain the parent component hierarchy, prioritizing the shallowest module
|
|
4450
4507
|
if (Object.keys(retMap.parentComponent).length) {
|
|
4451
|
-
if (
|
|
4452
|
-
|
|
4508
|
+
if (!rootParentComponent) {
|
|
4509
|
+
// First (shallowest) module becomes the root
|
|
4510
|
+
rootParentComponent = retMap.parentComponent;
|
|
4511
|
+
parentComponent = rootParentComponent;
|
|
4453
4512
|
} else {
|
|
4513
|
+
// Subsequent modules become subcomponents
|
|
4454
4514
|
parentComponent.components = parentComponent.components || [];
|
|
4455
4515
|
parentComponent.components.push(retMap.parentComponent);
|
|
4456
4516
|
}
|
|
@@ -4475,7 +4535,7 @@ export async function createGoBom(path, options) {
|
|
|
4475
4535
|
src: path,
|
|
4476
4536
|
dependencies,
|
|
4477
4537
|
parentComponent,
|
|
4478
|
-
filename:
|
|
4538
|
+
filename: sortedGomodFiles.join(", "),
|
|
4479
4539
|
});
|
|
4480
4540
|
}
|
|
4481
4541
|
if (gopkgLockFiles.length) {
|
|
@@ -4923,7 +4983,7 @@ export function createClojureBom(path, options) {
|
|
|
4923
4983
|
console.log(`Parsing ${f}`);
|
|
4924
4984
|
}
|
|
4925
4985
|
const basePath = dirname(f);
|
|
4926
|
-
console.log("Executing", LEIN_CMD,
|
|
4986
|
+
console.log("Executing", LEIN_CMD, "in", basePath);
|
|
4927
4987
|
const result = safeSpawnSync(LEIN_CMD, LEIN_ARGS, {
|
|
4928
4988
|
cwd: basePath,
|
|
4929
4989
|
encoding: "utf-8",
|
|
@@ -4972,7 +5032,7 @@ export function createClojureBom(path, options) {
|
|
|
4972
5032
|
}
|
|
4973
5033
|
for (const f of ednFiles) {
|
|
4974
5034
|
const basePath = dirname(f);
|
|
4975
|
-
console.log("Executing", CLJ_CMD,
|
|
5035
|
+
console.log("Executing", CLJ_CMD, "in", basePath);
|
|
4976
5036
|
const result = safeSpawnSync(CLJ_CMD, CLJ_ARGS, {
|
|
4977
5037
|
cwd: basePath,
|
|
4978
5038
|
encoding: "utf-8",
|
|
@@ -5580,6 +5640,128 @@ export async function createCocoaBom(path, options) {
|
|
|
5580
5640
|
}
|
|
5581
5641
|
}
|
|
5582
5642
|
|
|
5643
|
+
/**
|
|
5644
|
+
* Function to create bom string for Nix flakes
|
|
5645
|
+
*
|
|
5646
|
+
* @param {string} path to the project
|
|
5647
|
+
* @param {Object} options Parse options from the cli
|
|
5648
|
+
*/
|
|
5649
|
+
export async function createNixBom(path, options) {
|
|
5650
|
+
let pkgList = [];
|
|
5651
|
+
let dependencies = [];
|
|
5652
|
+
let parentComponent = {};
|
|
5653
|
+
|
|
5654
|
+
const flakeNixFiles = getAllFiles(
|
|
5655
|
+
path,
|
|
5656
|
+
`${options.multiProject ? "**/" : ""}flake.nix`,
|
|
5657
|
+
options,
|
|
5658
|
+
);
|
|
5659
|
+
const flakeLockFiles = getAllFiles(
|
|
5660
|
+
path,
|
|
5661
|
+
`${options.multiProject ? "**/" : ""}flake.lock`,
|
|
5662
|
+
options,
|
|
5663
|
+
);
|
|
5664
|
+
|
|
5665
|
+
for (const flakeNixFile of flakeNixFiles) {
|
|
5666
|
+
if (DEBUG_MODE) {
|
|
5667
|
+
console.log(`Parsing ${flakeNixFile}`);
|
|
5668
|
+
}
|
|
5669
|
+
const { pkgList: nixPkgs, dependencies: nixDeps } =
|
|
5670
|
+
parseFlakeNix(flakeNixFile);
|
|
5671
|
+
if (nixPkgs?.length) {
|
|
5672
|
+
pkgList = pkgList.concat(nixPkgs);
|
|
5673
|
+
}
|
|
5674
|
+
if (nixDeps?.length) {
|
|
5675
|
+
dependencies = dependencies.concat(nixDeps);
|
|
5676
|
+
}
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
for (const flakeLockFile of flakeLockFiles) {
|
|
5680
|
+
if (DEBUG_MODE) {
|
|
5681
|
+
console.log(`Parsing ${flakeLockFile}`);
|
|
5682
|
+
}
|
|
5683
|
+
const { pkgList: lockPkgs, dependencies: lockDeps } =
|
|
5684
|
+
parseFlakeLock(flakeLockFile);
|
|
5685
|
+
if (lockPkgs?.length) {
|
|
5686
|
+
const mergedPkgs = [];
|
|
5687
|
+
const existingNames = new Set();
|
|
5688
|
+
|
|
5689
|
+
for (const lockPkg of lockPkgs) {
|
|
5690
|
+
mergedPkgs.push(lockPkg);
|
|
5691
|
+
existingNames.add(lockPkg.name);
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5694
|
+
for (const nixPkg of pkgList) {
|
|
5695
|
+
if (!existingNames.has(nixPkg.name)) {
|
|
5696
|
+
mergedPkgs.push(nixPkg);
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
|
|
5700
|
+
pkgList = mergedPkgs;
|
|
5701
|
+
}
|
|
5702
|
+
if (lockDeps?.length) {
|
|
5703
|
+
dependencies = dependencies.concat(lockDeps);
|
|
5704
|
+
}
|
|
5705
|
+
|
|
5706
|
+
// Create parent component from flake.lock if found
|
|
5707
|
+
if (!Object.keys(parentComponent).length) {
|
|
5708
|
+
const flakeDir = dirname(flakeLockFile);
|
|
5709
|
+
const projectName = basename(flakeDir);
|
|
5710
|
+
parentComponent = {
|
|
5711
|
+
type: "application",
|
|
5712
|
+
name: projectName,
|
|
5713
|
+
version: "latest",
|
|
5714
|
+
description: `Nix flake project: ${projectName}`,
|
|
5715
|
+
"bom-ref": `pkg:nix/${projectName}@latest`,
|
|
5716
|
+
properties: [
|
|
5717
|
+
{
|
|
5718
|
+
name: "SrcFile",
|
|
5719
|
+
value: flakeLockFile,
|
|
5720
|
+
},
|
|
5721
|
+
{
|
|
5722
|
+
name: "cdx:nix:flake_dir",
|
|
5723
|
+
value: flakeDir,
|
|
5724
|
+
},
|
|
5725
|
+
],
|
|
5726
|
+
};
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
|
|
5730
|
+
// If no parent component was created from flake.lock, create one from flake.nix
|
|
5731
|
+
if (!Object.keys(parentComponent).length && flakeNixFiles.length > 0) {
|
|
5732
|
+
const flakeDir = dirname(flakeNixFiles[0]);
|
|
5733
|
+
const projectName = basename(flakeDir);
|
|
5734
|
+
parentComponent = {
|
|
5735
|
+
type: "application",
|
|
5736
|
+
name: projectName,
|
|
5737
|
+
version: "latest",
|
|
5738
|
+
description: `Nix flake project: ${projectName}`,
|
|
5739
|
+
"bom-ref": `pkg:nix/${projectName}@latest`,
|
|
5740
|
+
properties: [
|
|
5741
|
+
{
|
|
5742
|
+
name: "SrcFile",
|
|
5743
|
+
value: flakeNixFiles[0],
|
|
5744
|
+
},
|
|
5745
|
+
{
|
|
5746
|
+
name: "cdx:nix:flake_dir",
|
|
5747
|
+
value: flakeDir,
|
|
5748
|
+
},
|
|
5749
|
+
],
|
|
5750
|
+
};
|
|
5751
|
+
}
|
|
5752
|
+
|
|
5753
|
+
if (pkgList.length || Object.keys(parentComponent).length) {
|
|
5754
|
+
return buildBomNSData(options, pkgList, "nix", {
|
|
5755
|
+
src: path,
|
|
5756
|
+
filename: [...flakeNixFiles, ...flakeLockFiles].join(", "),
|
|
5757
|
+
dependencies,
|
|
5758
|
+
parentComponent,
|
|
5759
|
+
});
|
|
5760
|
+
}
|
|
5761
|
+
|
|
5762
|
+
return {};
|
|
5763
|
+
}
|
|
5764
|
+
|
|
5583
5765
|
/**
|
|
5584
5766
|
* Function to create bom string for docker compose
|
|
5585
5767
|
*
|
|
@@ -5917,6 +6099,10 @@ export async function createContainerSpecLikeBom(path, options) {
|
|
|
5917
6099
|
export function createPHPBom(path, options) {
|
|
5918
6100
|
let dependencies = [];
|
|
5919
6101
|
let parentComponent = {};
|
|
6102
|
+
// We can look for composer files within node_modules directory
|
|
6103
|
+
if (typeof options.includeNodeModulesDir === "undefined" || options.deep) {
|
|
6104
|
+
options.includeNodeModulesDir = true;
|
|
6105
|
+
}
|
|
5920
6106
|
const composerJsonFiles = getAllFiles(
|
|
5921
6107
|
path,
|
|
5922
6108
|
`${options.multiProject ? "**/" : ""}composer.json`,
|
|
@@ -6065,6 +6251,10 @@ export function createPHPBom(path, options) {
|
|
|
6065
6251
|
* @param {Object} options Parse options from the cli
|
|
6066
6252
|
*/
|
|
6067
6253
|
export async function createRubyBom(path, options) {
|
|
6254
|
+
// We can look for gem files within node_modules directory
|
|
6255
|
+
if (typeof options.includeNodeModulesDir === "undefined" || options.deep) {
|
|
6256
|
+
options.includeNodeModulesDir = true;
|
|
6257
|
+
}
|
|
6068
6258
|
const excludeList = (options.exclude || []).concat(["**/vendor/cache/**"]);
|
|
6069
6259
|
const gemLockExcludeList = (options.exclude || []).concat([
|
|
6070
6260
|
"**/vendor/bundle/ruby/**/Gemfile.lock",
|
|
@@ -6609,11 +6799,11 @@ export async function createCsharpBom(path, options) {
|
|
|
6609
6799
|
}
|
|
6610
6800
|
}
|
|
6611
6801
|
}
|
|
6802
|
+
const pkgNameVersions = {};
|
|
6612
6803
|
if (csProjFiles.length) {
|
|
6613
6804
|
manifestFiles = manifestFiles.concat(csProjFiles);
|
|
6614
6805
|
// Parsing csproj is quite error-prone. Some project files may not have versions specified
|
|
6615
6806
|
// To work around this, we make use of the version from the existing list
|
|
6616
|
-
const pkgNameVersions = {};
|
|
6617
6807
|
for (const p of pkgList) {
|
|
6618
6808
|
if (p.version) {
|
|
6619
6809
|
pkgNameVersions[p.name] = p.version;
|
|
@@ -6857,8 +7047,8 @@ export function trimComponents(components) {
|
|
|
6857
7047
|
}
|
|
6858
7048
|
// comp.evidence.identity can be an array or object
|
|
6859
7049
|
// Merge the evidence.identity based on methods or objects
|
|
6860
|
-
const
|
|
6861
|
-
const identities =
|
|
7050
|
+
const isIdentityArray = Array.isArray(comp.evidence.identity);
|
|
7051
|
+
const identities = isIdentityArray
|
|
6862
7052
|
? comp.evidence.identity
|
|
6863
7053
|
: [comp.evidence.identity];
|
|
6864
7054
|
for (const aident of identities) {
|
|
@@ -6889,9 +7079,23 @@ export function trimComponents(components) {
|
|
|
6889
7079
|
existingComponent.evidence.identity.push(aident);
|
|
6890
7080
|
}
|
|
6891
7081
|
}
|
|
6892
|
-
if (!
|
|
7082
|
+
if (!isIdentityArray) {
|
|
7083
|
+
const firstIdentity = existingComponent.evidence.identity[0];
|
|
7084
|
+
let identConfidence = firstIdentity?.confidence;
|
|
7085
|
+
// We need to set the confidence to the max of all confidences
|
|
7086
|
+
if (firstIdentity?.methods?.length > 1) {
|
|
7087
|
+
for (const aidentMethod of firstIdentity.methods) {
|
|
7088
|
+
if (
|
|
7089
|
+
aidentMethod?.confidence &&
|
|
7090
|
+
aidentMethod.confidence > identConfidence
|
|
7091
|
+
) {
|
|
7092
|
+
identConfidence = aidentMethod.confidence;
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
}
|
|
7096
|
+
firstIdentity.confidence = identConfidence;
|
|
6893
7097
|
existingComponent.evidence = {
|
|
6894
|
-
identity:
|
|
7098
|
+
identity: firstIdentity,
|
|
6895
7099
|
};
|
|
6896
7100
|
}
|
|
6897
7101
|
}
|
|
@@ -7631,6 +7835,27 @@ export async function createMultiXBom(pathList, options) {
|
|
|
7631
7835
|
}
|
|
7632
7836
|
}
|
|
7633
7837
|
}
|
|
7838
|
+
if (hasAnyProjectType(["oci", "nix"], options)) {
|
|
7839
|
+
bomData = await createNixBom(path, options);
|
|
7840
|
+
if (bomData?.bomJson?.components?.length) {
|
|
7841
|
+
if (DEBUG_MODE) {
|
|
7842
|
+
console.log(
|
|
7843
|
+
`Found ${bomData.bomJson.components.length} Nix flake packages at ${path}`,
|
|
7844
|
+
);
|
|
7845
|
+
}
|
|
7846
|
+
components = components.concat(bomData.bomJson.components);
|
|
7847
|
+
dependencies = mergeDependencies(
|
|
7848
|
+
dependencies,
|
|
7849
|
+
bomData.bomJson.dependencies,
|
|
7850
|
+
);
|
|
7851
|
+
if (
|
|
7852
|
+
bomData.parentComponent &&
|
|
7853
|
+
Object.keys(bomData.parentComponent).length
|
|
7854
|
+
) {
|
|
7855
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7634
7859
|
// Collect any crypto keys
|
|
7635
7860
|
if (options.specVersion >= 1.6 && options.includeCrypto) {
|
|
7636
7861
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -8059,6 +8284,21 @@ export async function createXBom(path, options) {
|
|
|
8059
8284
|
if (cocoaFiles.length) {
|
|
8060
8285
|
return await createCocoaBom(path, options);
|
|
8061
8286
|
}
|
|
8287
|
+
|
|
8288
|
+
// Nix flakes
|
|
8289
|
+
const flakeNixFiles = getAllFiles(
|
|
8290
|
+
path,
|
|
8291
|
+
`${options.multiProject ? "**/" : ""}flake.nix`,
|
|
8292
|
+
options,
|
|
8293
|
+
);
|
|
8294
|
+
const flakeLockFiles = getAllFiles(
|
|
8295
|
+
path,
|
|
8296
|
+
`${options.multiProject ? "**/" : ""}flake.lock`,
|
|
8297
|
+
options,
|
|
8298
|
+
);
|
|
8299
|
+
if (flakeNixFiles.length || flakeLockFiles.length) {
|
|
8300
|
+
return await createNixBom(path, options);
|
|
8301
|
+
}
|
|
8062
8302
|
}
|
|
8063
8303
|
|
|
8064
8304
|
/**
|
|
@@ -8304,6 +8544,9 @@ export async function createBom(path, options) {
|
|
|
8304
8544
|
if (PROJECT_TYPE_ALIASES["cocoa"].includes(projectType[0])) {
|
|
8305
8545
|
return await createCocoaBom(path, options);
|
|
8306
8546
|
}
|
|
8547
|
+
if (PROJECT_TYPE_ALIASES["nix"].includes(projectType[0])) {
|
|
8548
|
+
return await createNixBom(path, options);
|
|
8549
|
+
}
|
|
8307
8550
|
switch (projectType[0]) {
|
|
8308
8551
|
case "jar":
|
|
8309
8552
|
return createJarBom(path, options);
|