@cyclonedx/cdxgen 11.4.4 → 11.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -64
- package/bin/cdxgen.js +31 -9
- package/lib/cli/index.js +336 -99
- package/lib/evinser/evinser.js +3 -0
- package/lib/evinser/{evinser.test.js → evinser.poku.js} +51 -33
- package/lib/evinser/{swiftsem.test.js → swiftsem.poku.js} +46 -45
- package/lib/helpers/cbomutils.poku.js +8 -0
- package/lib/helpers/{display.test.js → display.poku.js} +2 -2
- package/lib/helpers/dotnetutils.js +132 -0
- package/lib/helpers/dotnetutils.poku.js +429 -0
- package/lib/helpers/envcontext.js +20 -18
- package/lib/helpers/{envcontext.test.js → envcontext.poku.js} +24 -24
- package/lib/helpers/logger.js +0 -2
- package/lib/helpers/{protobom.test.js → protobom.poku.js} +12 -10
- package/lib/helpers/utils.js +853 -88
- package/lib/helpers/{utils.test.js → utils.poku.js} +1999 -1349
- package/lib/helpers/validator.js +10 -0
- package/lib/managers/binary.js +89 -11
- package/lib/managers/docker.js +47 -37
- package/lib/managers/{docker.test.js → docker.poku.js} +70 -59
- package/lib/parsers/iri.js +504 -0
- package/lib/parsers/iri.poku.js +406 -0
- package/lib/server/server.js +130 -7
- package/lib/server/server.poku.js +387 -0
- package/lib/stages/postgen/{annotator.test.js → annotator.poku.js} +11 -14
- package/lib/stages/postgen/{postgen.test.js → postgen.poku.js} +15 -15
- package/package.json +34 -16
- package/types/cli/index.d.ts +295 -0
- package/types/cli/index.d.ts.map +1 -0
- package/types/evinser/scalasem.d.ts +6 -0
- package/types/evinser/scalasem.d.ts.map +1 -0
- package/types/evinser/swiftsem.d.ts +103 -0
- package/types/evinser/swiftsem.d.ts.map +1 -0
- package/types/helpers/analyzer.d.ts +5 -0
- package/types/helpers/analyzer.d.ts.map +1 -0
- package/types/helpers/cbomutils.d.ts +15 -0
- package/types/helpers/cbomutils.d.ts.map +1 -0
- package/types/helpers/db.d.ts +19 -0
- package/types/helpers/db.d.ts.map +1 -0
- package/types/helpers/display.d.ts +12 -0
- package/types/helpers/display.d.ts.map +1 -0
- package/types/helpers/dotnetutils.d.ts +45 -0
- package/types/helpers/dotnetutils.d.ts.map +1 -0
- package/types/helpers/envcontext.d.ts +264 -0
- package/types/helpers/envcontext.d.ts.map +1 -0
- package/types/helpers/logger.d.ts +12 -0
- package/types/helpers/logger.d.ts.map +1 -0
- package/types/helpers/protobom.d.ts +3 -0
- package/types/helpers/protobom.d.ts.map +1 -0
- package/types/helpers/utils.d.ts +1556 -0
- package/types/helpers/utils.d.ts.map +1 -0
- package/types/helpers/validator.d.ts +11 -0
- package/types/helpers/validator.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts +8 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +2 -1
- package/types/lib/helpers/dotnetutils.d.ts +45 -0
- package/types/lib/helpers/dotnetutils.d.ts.map +1 -0
- 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 +68 -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/types/managers/binary.d.ts +37 -0
- package/types/managers/binary.d.ts.map +1 -0
- package/types/managers/docker.d.ts +56 -0
- package/types/managers/docker.d.ts.map +1 -0
- package/types/managers/oci.d.ts +2 -0
- package/types/managers/oci.d.ts.map +1 -0
- package/types/managers/piptree.d.ts +2 -0
- package/types/managers/piptree.d.ts.map +1 -0
- package/types/parsers/iri.d.ts +72 -0
- package/types/parsers/iri.d.ts.map +1 -0
- package/types/server/server.d.ts +34 -0
- package/types/server/server.d.ts.map +1 -0
- package/types/stages/postgen/annotator.d.ts +27 -0
- package/types/stages/postgen/annotator.d.ts.map +1 -0
- package/types/stages/postgen/postgen.d.ts +51 -0
- package/types/stages/postgen/postgen.d.ts.map +1 -0
- package/types/stages/pregen/pregen.d.ts +59 -0
- package/types/stages/pregen/pregen.d.ts.map +1 -0
- package/jest.config.js +0 -7
- package/lib/helpers/cbomutils.test.js +0 -8
- package/lib/server/server.test.js +0 -126
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*/**");
|
|
@@ -1751,9 +1775,7 @@ export async function createJavaBom(path, options) {
|
|
|
1751
1775
|
`**MAVEN**: Let's use Maven to collect packages from ${basePath}.`,
|
|
1752
1776
|
);
|
|
1753
1777
|
if (DEBUG_MODE) {
|
|
1754
|
-
console.log(
|
|
1755
|
-
`Executing 'mvn ${mvnTreeArgs.join(" ")}' in ${basePath}`,
|
|
1756
|
-
);
|
|
1778
|
+
console.log(`Executing 'mvn dependency:tree ...' in ${basePath}`);
|
|
1757
1779
|
}
|
|
1758
1780
|
// Prefer the built-in maven
|
|
1759
1781
|
result = safeSpawnSync(
|
|
@@ -2305,7 +2327,7 @@ export async function createJavaBom(path, options) {
|
|
|
2305
2327
|
) {
|
|
2306
2328
|
bArgs = ["--bazelrc=.bazelrc", "build", bazelTarget];
|
|
2307
2329
|
}
|
|
2308
|
-
console.log("Executing", BAZEL_CMD,
|
|
2330
|
+
console.log("Executing", BAZEL_CMD, "in", basePath);
|
|
2309
2331
|
let result = safeSpawnSync(BAZEL_CMD, bArgs, {
|
|
2310
2332
|
cwd: basePath,
|
|
2311
2333
|
shell: true,
|
|
@@ -2639,7 +2661,7 @@ export async function createJavaBom(path, options) {
|
|
|
2639
2661
|
}
|
|
2640
2662
|
const millArgs = [...millCommonArgs, "__.ivyDepsTree"];
|
|
2641
2663
|
if (DEBUG_MODE) {
|
|
2642
|
-
console.log("Executing", millCmd,
|
|
2664
|
+
console.log("Executing", millCmd, "in", millRootPath);
|
|
2643
2665
|
}
|
|
2644
2666
|
let sresult = safeSpawnSync(millCmd, millArgs, {
|
|
2645
2667
|
cwd: millRootPath,
|
|
@@ -3761,7 +3783,7 @@ export async function createPythonBom(path, options) {
|
|
|
3761
3783
|
// Retrieve the tree using virtualenv in deep mode and as a fallback
|
|
3762
3784
|
// This is a slow operation
|
|
3763
3785
|
if ((options.deep || !dependencies.length) && !f.endsWith("uv.lock")) {
|
|
3764
|
-
retMap = getPipFrozenTree(basePath, f, tempDir, parentComponent);
|
|
3786
|
+
retMap = await getPipFrozenTree(basePath, f, tempDir, parentComponent);
|
|
3765
3787
|
if (retMap.pkgList?.length) {
|
|
3766
3788
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
3767
3789
|
}
|
|
@@ -3839,9 +3861,49 @@ export async function createPythonBom(path, options) {
|
|
|
3839
3861
|
console.error("Pipfile.lock not found at", path);
|
|
3840
3862
|
options.failOnError && process.exit(1);
|
|
3841
3863
|
}
|
|
3842
|
-
} else if (
|
|
3843
|
-
|
|
3844
|
-
|
|
3864
|
+
} else if (reqDirFiles?.length) {
|
|
3865
|
+
for (const j in reqDirFiles) {
|
|
3866
|
+
const f = reqDirFiles[j];
|
|
3867
|
+
const dlist = await parseReqFile(f, false);
|
|
3868
|
+
if (dlist?.length) {
|
|
3869
|
+
pkgList = pkgList.concat(dlist);
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
metadataFilename = reqDirFiles.join(", ");
|
|
3873
|
+
} else if (reqFiles?.length) {
|
|
3874
|
+
for (const f of reqFiles) {
|
|
3875
|
+
const dlist = await parseReqFile(f, true);
|
|
3876
|
+
if (dlist?.length) {
|
|
3877
|
+
pkgList = pkgList.concat(dlist);
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
const parentDependsOn = new Set();
|
|
3884
|
+
|
|
3885
|
+
// Use atom in requirements, setup.py and pyproject.toml mode
|
|
3886
|
+
if (requirementsMode || setupPyMode || pyProjectMode || options.deep) {
|
|
3887
|
+
/**
|
|
3888
|
+
* The order of preference is pyproject.toml (newer) and then setup.py
|
|
3889
|
+
*/
|
|
3890
|
+
if (options.installDeps) {
|
|
3891
|
+
let pkgMap;
|
|
3892
|
+
if (pyProjectMode && !poetryMode) {
|
|
3893
|
+
pkgMap = await getPipFrozenTree(
|
|
3894
|
+
path,
|
|
3895
|
+
pyProjectFile,
|
|
3896
|
+
tempDir,
|
|
3897
|
+
parentComponent,
|
|
3898
|
+
);
|
|
3899
|
+
} else if (setupPyMode) {
|
|
3900
|
+
pkgMap = await getPipFrozenTree(
|
|
3901
|
+
path,
|
|
3902
|
+
setupPy,
|
|
3903
|
+
tempDir,
|
|
3904
|
+
parentComponent,
|
|
3905
|
+
);
|
|
3906
|
+
} else if (requirementsMode && reqFiles?.length) {
|
|
3845
3907
|
if (options.installDeps && DEBUG_MODE) {
|
|
3846
3908
|
console.log(
|
|
3847
3909
|
"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.",
|
|
@@ -3849,13 +3911,8 @@ export async function createPythonBom(path, options) {
|
|
|
3849
3911
|
}
|
|
3850
3912
|
for (const f of reqFiles) {
|
|
3851
3913
|
const basePath = dirname(f);
|
|
3852
|
-
let reqData;
|
|
3853
|
-
let frozen = false;
|
|
3854
|
-
// Attempt to pip freeze in a virtualenv to improve precision
|
|
3855
3914
|
if (options.installDeps) {
|
|
3856
|
-
|
|
3857
|
-
// adding to the delay.
|
|
3858
|
-
const pkgMap = getPipFrozenTree(
|
|
3915
|
+
const pkgMap = await getPipFrozenTree(
|
|
3859
3916
|
basePath,
|
|
3860
3917
|
f,
|
|
3861
3918
|
tempDir,
|
|
@@ -3863,10 +3920,11 @@ export async function createPythonBom(path, options) {
|
|
|
3863
3920
|
);
|
|
3864
3921
|
if (pkgMap.pkgList?.length) {
|
|
3865
3922
|
pkgList = pkgList.concat(pkgMap.pkgList);
|
|
3866
|
-
|
|
3923
|
+
pkgList = trimComponents(pkgList);
|
|
3867
3924
|
}
|
|
3868
3925
|
if (pkgMap.formulationList?.length) {
|
|
3869
3926
|
formulationList = formulationList.concat(pkgMap.formulationList);
|
|
3927
|
+
formulationList = trimComponents(formulationList);
|
|
3870
3928
|
}
|
|
3871
3929
|
if (pkgMap.dependenciesList) {
|
|
3872
3930
|
dependencies = mergeDependencies(
|
|
@@ -3875,61 +3933,31 @@ export async function createPythonBom(path, options) {
|
|
|
3875
3933
|
parentComponent,
|
|
3876
3934
|
);
|
|
3877
3935
|
}
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3936
|
+
// Add the root packages from this file to the parent's dependencies
|
|
3937
|
+
for (const p of pkgMap.rootList) {
|
|
3938
|
+
if (
|
|
3939
|
+
parentComponent &&
|
|
3940
|
+
p.name === parentComponent.name &&
|
|
3941
|
+
(p.version === parentComponent.version ||
|
|
3942
|
+
p.version === "latest")
|
|
3943
|
+
) {
|
|
3944
|
+
continue;
|
|
3945
|
+
}
|
|
3946
|
+
parentDependsOn.add(
|
|
3947
|
+
`pkg:pypi/${p.name.toLowerCase()}@${p.version}`,
|
|
3887
3948
|
);
|
|
3888
3949
|
}
|
|
3889
|
-
reqData = readFileSync(f, { encoding: "utf-8" });
|
|
3890
|
-
const dlist = await parseReqFile(reqData, true);
|
|
3891
|
-
if (dlist?.length) {
|
|
3892
|
-
pkgList = pkgList.concat(dlist);
|
|
3893
|
-
}
|
|
3894
|
-
}
|
|
3895
|
-
} // for
|
|
3896
|
-
metadataFilename = reqFiles.join(", ");
|
|
3897
|
-
} else if (reqDirFiles?.length) {
|
|
3898
|
-
for (const j in reqDirFiles) {
|
|
3899
|
-
const f = reqDirFiles[j];
|
|
3900
|
-
const reqData = readFileSync(f, { encoding: "utf-8" });
|
|
3901
|
-
const dlist = await parseReqFile(reqData, false);
|
|
3902
|
-
if (dlist?.length) {
|
|
3903
|
-
pkgList = pkgList.concat(dlist);
|
|
3904
3950
|
}
|
|
3905
3951
|
}
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
}
|
|
3909
|
-
}
|
|
3910
|
-
// Use atom in requirements, setup.py and pyproject.toml mode
|
|
3911
|
-
if (requirementsMode || setupPyMode || pyProjectMode || options.deep) {
|
|
3912
|
-
/**
|
|
3913
|
-
* The order of preference is pyproject.toml (newer) and then setup.py
|
|
3914
|
-
*/
|
|
3915
|
-
if (options.installDeps) {
|
|
3916
|
-
let pkgMap;
|
|
3917
|
-
if (pyProjectMode && !poetryMode) {
|
|
3918
|
-
pkgMap = getPipFrozenTree(
|
|
3952
|
+
} else if (!poetryMode) {
|
|
3953
|
+
pkgMap = await getPipFrozenTree(
|
|
3919
3954
|
path,
|
|
3920
|
-
|
|
3955
|
+
undefined,
|
|
3921
3956
|
tempDir,
|
|
3922
3957
|
parentComponent,
|
|
3923
3958
|
);
|
|
3924
|
-
} else if (setupPyMode) {
|
|
3925
|
-
pkgMap = getPipFrozenTree(path, setupPy, tempDir, parentComponent);
|
|
3926
|
-
} else if (!poetryMode) {
|
|
3927
|
-
pkgMap = getPipFrozenTree(path, undefined, tempDir, parentComponent);
|
|
3928
3959
|
}
|
|
3929
3960
|
|
|
3930
|
-
// Get the imported modules and a dedupe list of packages
|
|
3931
|
-
const parentDependsOn = new Set();
|
|
3932
|
-
|
|
3933
3961
|
// ATOM parsedeps block
|
|
3934
3962
|
// Atom parsedeps slices can be used to identify packages that are not declared in manifests
|
|
3935
3963
|
// Since it is a slow operation, we only use it as a fallback or in deep mode
|
|
@@ -4253,11 +4281,21 @@ export async function createGoBom(path, options) {
|
|
|
4253
4281
|
}
|
|
4254
4282
|
if (gomodFiles.length) {
|
|
4255
4283
|
let shouldManuallyParse = false;
|
|
4284
|
+
// Sort go.mod files by depth (shallowest first) to prioritize root modules
|
|
4285
|
+
const sortedGomodFiles = gomodFiles.sort((a, b) => {
|
|
4286
|
+
const relativePathA = relative(path, a);
|
|
4287
|
+
const relativePathB = relative(path, b);
|
|
4288
|
+
const depthA = relativePathA.split("/").length;
|
|
4289
|
+
const depthB = relativePathB.split("/").length;
|
|
4290
|
+
return depthA - depthB;
|
|
4291
|
+
});
|
|
4292
|
+
|
|
4293
|
+
let rootParentComponent = null;
|
|
4256
4294
|
// Use the go list -deps and go mod why commands to generate a good quality BOM for non-docker invocations
|
|
4257
4295
|
if (
|
|
4258
4296
|
!hasAnyProjectType(["docker", "oci", "container", "os"], options, false)
|
|
4259
4297
|
) {
|
|
4260
|
-
for (const f of
|
|
4298
|
+
for (const f of sortedGomodFiles) {
|
|
4261
4299
|
const basePath = dirname(f);
|
|
4262
4300
|
// Ignore vendor packages
|
|
4263
4301
|
if (
|
|
@@ -4307,13 +4345,23 @@ export async function createGoBom(path, options) {
|
|
|
4307
4345
|
if (retMap.pkgList?.length) {
|
|
4308
4346
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
4309
4347
|
}
|
|
4310
|
-
//
|
|
4348
|
+
// Prioritize the shallowest module as the root component
|
|
4311
4349
|
if (
|
|
4312
4350
|
retMap.parentComponent &&
|
|
4313
4351
|
Object.keys(retMap.parentComponent).length
|
|
4314
4352
|
) {
|
|
4315
|
-
|
|
4316
|
-
|
|
4353
|
+
if (!rootParentComponent) {
|
|
4354
|
+
// First (shallowest) module becomes the root
|
|
4355
|
+
rootParentComponent = retMap.parentComponent;
|
|
4356
|
+
rootParentComponent.type = "application";
|
|
4357
|
+
parentComponent = rootParentComponent;
|
|
4358
|
+
} else {
|
|
4359
|
+
// Subsequent modules become subcomponents
|
|
4360
|
+
if (!parentComponent.components) {
|
|
4361
|
+
parentComponent.components = [];
|
|
4362
|
+
}
|
|
4363
|
+
parentComponent.components.push(retMap.parentComponent);
|
|
4364
|
+
}
|
|
4317
4365
|
}
|
|
4318
4366
|
if (DEBUG_MODE) {
|
|
4319
4367
|
console.log("Executing go mod graph in", basePath);
|
|
@@ -4399,11 +4447,14 @@ export async function createGoBom(path, options) {
|
|
|
4399
4447
|
parentComponent,
|
|
4400
4448
|
);
|
|
4401
4449
|
}
|
|
4402
|
-
// Retain the parent component hierarchy
|
|
4450
|
+
// Retain the parent component hierarchy, prioritizing the shallowest module
|
|
4403
4451
|
if (Object.keys(retMap.parentComponent).length) {
|
|
4404
|
-
if (
|
|
4405
|
-
|
|
4452
|
+
if (!rootParentComponent) {
|
|
4453
|
+
// First (shallowest) module becomes the root
|
|
4454
|
+
rootParentComponent = retMap.parentComponent;
|
|
4455
|
+
parentComponent = rootParentComponent;
|
|
4406
4456
|
} else {
|
|
4457
|
+
// Subsequent modules become subcomponents
|
|
4407
4458
|
parentComponent.components = parentComponent.components || [];
|
|
4408
4459
|
parentComponent.components.push(retMap.parentComponent);
|
|
4409
4460
|
}
|
|
@@ -4429,7 +4480,7 @@ export async function createGoBom(path, options) {
|
|
|
4429
4480
|
dependencies,
|
|
4430
4481
|
parentComponent,
|
|
4431
4482
|
src: path,
|
|
4432
|
-
filename:
|
|
4483
|
+
filename: sortedGomodFiles.join(", "),
|
|
4433
4484
|
});
|
|
4434
4485
|
}
|
|
4435
4486
|
}
|
|
@@ -4441,7 +4492,7 @@ export async function createGoBom(path, options) {
|
|
|
4441
4492
|
"Manually parsing go.mod files. The resultant BOM would be incomplete.",
|
|
4442
4493
|
);
|
|
4443
4494
|
}
|
|
4444
|
-
for (const f of
|
|
4495
|
+
for (const f of sortedGomodFiles) {
|
|
4445
4496
|
if (DEBUG_MODE) {
|
|
4446
4497
|
console.log(`Parsing ${f}`);
|
|
4447
4498
|
}
|
|
@@ -4450,11 +4501,14 @@ export async function createGoBom(path, options) {
|
|
|
4450
4501
|
if (retMap?.pkgList?.length) {
|
|
4451
4502
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
4452
4503
|
}
|
|
4453
|
-
// Retain the parent component hierarchy
|
|
4504
|
+
// Retain the parent component hierarchy, prioritizing the shallowest module
|
|
4454
4505
|
if (Object.keys(retMap.parentComponent).length) {
|
|
4455
|
-
if (
|
|
4456
|
-
|
|
4506
|
+
if (!rootParentComponent) {
|
|
4507
|
+
// First (shallowest) module becomes the root
|
|
4508
|
+
rootParentComponent = retMap.parentComponent;
|
|
4509
|
+
parentComponent = rootParentComponent;
|
|
4457
4510
|
} else {
|
|
4511
|
+
// Subsequent modules become subcomponents
|
|
4458
4512
|
parentComponent.components = parentComponent.components || [];
|
|
4459
4513
|
parentComponent.components.push(retMap.parentComponent);
|
|
4460
4514
|
}
|
|
@@ -4479,7 +4533,7 @@ export async function createGoBom(path, options) {
|
|
|
4479
4533
|
src: path,
|
|
4480
4534
|
dependencies,
|
|
4481
4535
|
parentComponent,
|
|
4482
|
-
filename:
|
|
4536
|
+
filename: sortedGomodFiles.join(", "),
|
|
4483
4537
|
});
|
|
4484
4538
|
}
|
|
4485
4539
|
if (gopkgLockFiles.length) {
|
|
@@ -4927,7 +4981,7 @@ export function createClojureBom(path, options) {
|
|
|
4927
4981
|
console.log(`Parsing ${f}`);
|
|
4928
4982
|
}
|
|
4929
4983
|
const basePath = dirname(f);
|
|
4930
|
-
console.log("Executing", LEIN_CMD,
|
|
4984
|
+
console.log("Executing", LEIN_CMD, "in", basePath);
|
|
4931
4985
|
const result = safeSpawnSync(LEIN_CMD, LEIN_ARGS, {
|
|
4932
4986
|
cwd: basePath,
|
|
4933
4987
|
encoding: "utf-8",
|
|
@@ -4976,7 +5030,7 @@ export function createClojureBom(path, options) {
|
|
|
4976
5030
|
}
|
|
4977
5031
|
for (const f of ednFiles) {
|
|
4978
5032
|
const basePath = dirname(f);
|
|
4979
|
-
console.log("Executing", CLJ_CMD,
|
|
5033
|
+
console.log("Executing", CLJ_CMD, "in", basePath);
|
|
4980
5034
|
const result = safeSpawnSync(CLJ_CMD, CLJ_ARGS, {
|
|
4981
5035
|
cwd: basePath,
|
|
4982
5036
|
encoding: "utf-8",
|
|
@@ -5584,6 +5638,128 @@ export async function createCocoaBom(path, options) {
|
|
|
5584
5638
|
}
|
|
5585
5639
|
}
|
|
5586
5640
|
|
|
5641
|
+
/**
|
|
5642
|
+
* Function to create bom string for Nix flakes
|
|
5643
|
+
*
|
|
5644
|
+
* @param {string} path to the project
|
|
5645
|
+
* @param {Object} options Parse options from the cli
|
|
5646
|
+
*/
|
|
5647
|
+
export async function createNixBom(path, options) {
|
|
5648
|
+
let pkgList = [];
|
|
5649
|
+
let dependencies = [];
|
|
5650
|
+
let parentComponent = {};
|
|
5651
|
+
|
|
5652
|
+
const flakeNixFiles = getAllFiles(
|
|
5653
|
+
path,
|
|
5654
|
+
`${options.multiProject ? "**/" : ""}flake.nix`,
|
|
5655
|
+
options,
|
|
5656
|
+
);
|
|
5657
|
+
const flakeLockFiles = getAllFiles(
|
|
5658
|
+
path,
|
|
5659
|
+
`${options.multiProject ? "**/" : ""}flake.lock`,
|
|
5660
|
+
options,
|
|
5661
|
+
);
|
|
5662
|
+
|
|
5663
|
+
for (const flakeNixFile of flakeNixFiles) {
|
|
5664
|
+
if (DEBUG_MODE) {
|
|
5665
|
+
console.log(`Parsing ${flakeNixFile}`);
|
|
5666
|
+
}
|
|
5667
|
+
const { pkgList: nixPkgs, dependencies: nixDeps } =
|
|
5668
|
+
parseFlakeNix(flakeNixFile);
|
|
5669
|
+
if (nixPkgs?.length) {
|
|
5670
|
+
pkgList = pkgList.concat(nixPkgs);
|
|
5671
|
+
}
|
|
5672
|
+
if (nixDeps?.length) {
|
|
5673
|
+
dependencies = dependencies.concat(nixDeps);
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5677
|
+
for (const flakeLockFile of flakeLockFiles) {
|
|
5678
|
+
if (DEBUG_MODE) {
|
|
5679
|
+
console.log(`Parsing ${flakeLockFile}`);
|
|
5680
|
+
}
|
|
5681
|
+
const { pkgList: lockPkgs, dependencies: lockDeps } =
|
|
5682
|
+
parseFlakeLock(flakeLockFile);
|
|
5683
|
+
if (lockPkgs?.length) {
|
|
5684
|
+
const mergedPkgs = [];
|
|
5685
|
+
const existingNames = new Set();
|
|
5686
|
+
|
|
5687
|
+
for (const lockPkg of lockPkgs) {
|
|
5688
|
+
mergedPkgs.push(lockPkg);
|
|
5689
|
+
existingNames.add(lockPkg.name);
|
|
5690
|
+
}
|
|
5691
|
+
|
|
5692
|
+
for (const nixPkg of pkgList) {
|
|
5693
|
+
if (!existingNames.has(nixPkg.name)) {
|
|
5694
|
+
mergedPkgs.push(nixPkg);
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
|
|
5698
|
+
pkgList = mergedPkgs;
|
|
5699
|
+
}
|
|
5700
|
+
if (lockDeps?.length) {
|
|
5701
|
+
dependencies = dependencies.concat(lockDeps);
|
|
5702
|
+
}
|
|
5703
|
+
|
|
5704
|
+
// Create parent component from flake.lock if found
|
|
5705
|
+
if (!Object.keys(parentComponent).length) {
|
|
5706
|
+
const flakeDir = dirname(flakeLockFile);
|
|
5707
|
+
const projectName = basename(flakeDir);
|
|
5708
|
+
parentComponent = {
|
|
5709
|
+
type: "application",
|
|
5710
|
+
name: projectName,
|
|
5711
|
+
version: "latest",
|
|
5712
|
+
description: `Nix flake project: ${projectName}`,
|
|
5713
|
+
"bom-ref": `pkg:nix/${projectName}@latest`,
|
|
5714
|
+
properties: [
|
|
5715
|
+
{
|
|
5716
|
+
name: "SrcFile",
|
|
5717
|
+
value: flakeLockFile,
|
|
5718
|
+
},
|
|
5719
|
+
{
|
|
5720
|
+
name: "cdx:nix:flake_dir",
|
|
5721
|
+
value: flakeDir,
|
|
5722
|
+
},
|
|
5723
|
+
],
|
|
5724
|
+
};
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
|
|
5728
|
+
// If no parent component was created from flake.lock, create one from flake.nix
|
|
5729
|
+
if (!Object.keys(parentComponent).length && flakeNixFiles.length > 0) {
|
|
5730
|
+
const flakeDir = dirname(flakeNixFiles[0]);
|
|
5731
|
+
const projectName = basename(flakeDir);
|
|
5732
|
+
parentComponent = {
|
|
5733
|
+
type: "application",
|
|
5734
|
+
name: projectName,
|
|
5735
|
+
version: "latest",
|
|
5736
|
+
description: `Nix flake project: ${projectName}`,
|
|
5737
|
+
"bom-ref": `pkg:nix/${projectName}@latest`,
|
|
5738
|
+
properties: [
|
|
5739
|
+
{
|
|
5740
|
+
name: "SrcFile",
|
|
5741
|
+
value: flakeNixFiles[0],
|
|
5742
|
+
},
|
|
5743
|
+
{
|
|
5744
|
+
name: "cdx:nix:flake_dir",
|
|
5745
|
+
value: flakeDir,
|
|
5746
|
+
},
|
|
5747
|
+
],
|
|
5748
|
+
};
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5751
|
+
if (pkgList.length || Object.keys(parentComponent).length) {
|
|
5752
|
+
return buildBomNSData(options, pkgList, "nix", {
|
|
5753
|
+
src: path,
|
|
5754
|
+
filename: [...flakeNixFiles, ...flakeLockFiles].join(", "),
|
|
5755
|
+
dependencies,
|
|
5756
|
+
parentComponent,
|
|
5757
|
+
});
|
|
5758
|
+
}
|
|
5759
|
+
|
|
5760
|
+
return {};
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5587
5763
|
/**
|
|
5588
5764
|
* Function to create bom string for docker compose
|
|
5589
5765
|
*
|
|
@@ -5921,6 +6097,10 @@ export async function createContainerSpecLikeBom(path, options) {
|
|
|
5921
6097
|
export function createPHPBom(path, options) {
|
|
5922
6098
|
let dependencies = [];
|
|
5923
6099
|
let parentComponent = {};
|
|
6100
|
+
// We can look for composer files within node_modules directory
|
|
6101
|
+
if (typeof options.includeNodeModulesDir === "undefined" || options.deep) {
|
|
6102
|
+
options.includeNodeModulesDir = true;
|
|
6103
|
+
}
|
|
5924
6104
|
const composerJsonFiles = getAllFiles(
|
|
5925
6105
|
path,
|
|
5926
6106
|
`${options.multiProject ? "**/" : ""}composer.json`,
|
|
@@ -6069,6 +6249,10 @@ export function createPHPBom(path, options) {
|
|
|
6069
6249
|
* @param {Object} options Parse options from the cli
|
|
6070
6250
|
*/
|
|
6071
6251
|
export async function createRubyBom(path, options) {
|
|
6252
|
+
// We can look for gem files within node_modules directory
|
|
6253
|
+
if (typeof options.includeNodeModulesDir === "undefined" || options.deep) {
|
|
6254
|
+
options.includeNodeModulesDir = true;
|
|
6255
|
+
}
|
|
6072
6256
|
const excludeList = (options.exclude || []).concat(["**/vendor/cache/**"]);
|
|
6073
6257
|
const gemLockExcludeList = (options.exclude || []).concat([
|
|
6074
6258
|
"**/vendor/bundle/ruby/**/Gemfile.lock",
|
|
@@ -6495,7 +6679,7 @@ export async function createCsharpBom(path, options) {
|
|
|
6495
6679
|
console.log(`Parsing ${nf}`);
|
|
6496
6680
|
}
|
|
6497
6681
|
const retMap = await parseNupkg(nf);
|
|
6498
|
-
if (retMap?.pkgList
|
|
6682
|
+
if (retMap?.pkgList?.length) {
|
|
6499
6683
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
6500
6684
|
for (const d of retMap.pkgList) {
|
|
6501
6685
|
parentDependsOn.add(d["bom-ref"]);
|
|
@@ -6861,8 +7045,8 @@ export function trimComponents(components) {
|
|
|
6861
7045
|
}
|
|
6862
7046
|
// comp.evidence.identity can be an array or object
|
|
6863
7047
|
// Merge the evidence.identity based on methods or objects
|
|
6864
|
-
const
|
|
6865
|
-
const identities =
|
|
7048
|
+
const isIdentityArray = Array.isArray(comp.evidence.identity);
|
|
7049
|
+
const identities = isIdentityArray
|
|
6866
7050
|
? comp.evidence.identity
|
|
6867
7051
|
: [comp.evidence.identity];
|
|
6868
7052
|
for (const aident of identities) {
|
|
@@ -6893,9 +7077,23 @@ export function trimComponents(components) {
|
|
|
6893
7077
|
existingComponent.evidence.identity.push(aident);
|
|
6894
7078
|
}
|
|
6895
7079
|
}
|
|
6896
|
-
if (!
|
|
7080
|
+
if (!isIdentityArray) {
|
|
7081
|
+
const firstIdentity = existingComponent.evidence.identity[0];
|
|
7082
|
+
let identConfidence = firstIdentity?.confidence;
|
|
7083
|
+
// We need to set the confidence to the max of all confidences
|
|
7084
|
+
if (firstIdentity?.methods?.length > 1) {
|
|
7085
|
+
for (const aidentMethod of firstIdentity.methods) {
|
|
7086
|
+
if (
|
|
7087
|
+
aidentMethod?.confidence &&
|
|
7088
|
+
aidentMethod.confidence > identConfidence
|
|
7089
|
+
) {
|
|
7090
|
+
identConfidence = aidentMethod.confidence;
|
|
7091
|
+
}
|
|
7092
|
+
}
|
|
7093
|
+
}
|
|
7094
|
+
firstIdentity.confidence = identConfidence;
|
|
6897
7095
|
existingComponent.evidence = {
|
|
6898
|
-
identity:
|
|
7096
|
+
identity: firstIdentity,
|
|
6899
7097
|
};
|
|
6900
7098
|
}
|
|
6901
7099
|
}
|
|
@@ -7635,6 +7833,27 @@ export async function createMultiXBom(pathList, options) {
|
|
|
7635
7833
|
}
|
|
7636
7834
|
}
|
|
7637
7835
|
}
|
|
7836
|
+
if (hasAnyProjectType(["oci", "nix"], options)) {
|
|
7837
|
+
bomData = await createNixBom(path, options);
|
|
7838
|
+
if (bomData?.bomJson?.components?.length) {
|
|
7839
|
+
if (DEBUG_MODE) {
|
|
7840
|
+
console.log(
|
|
7841
|
+
`Found ${bomData.bomJson.components.length} Nix flake packages at ${path}`,
|
|
7842
|
+
);
|
|
7843
|
+
}
|
|
7844
|
+
components = components.concat(bomData.bomJson.components);
|
|
7845
|
+
dependencies = mergeDependencies(
|
|
7846
|
+
dependencies,
|
|
7847
|
+
bomData.bomJson.dependencies,
|
|
7848
|
+
);
|
|
7849
|
+
if (
|
|
7850
|
+
bomData.parentComponent &&
|
|
7851
|
+
Object.keys(bomData.parentComponent).length
|
|
7852
|
+
) {
|
|
7853
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
7854
|
+
}
|
|
7855
|
+
}
|
|
7856
|
+
}
|
|
7638
7857
|
// Collect any crypto keys
|
|
7639
7858
|
if (options.specVersion >= 1.6 && options.includeCrypto) {
|
|
7640
7859
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -8063,6 +8282,21 @@ export async function createXBom(path, options) {
|
|
|
8063
8282
|
if (cocoaFiles.length) {
|
|
8064
8283
|
return await createCocoaBom(path, options);
|
|
8065
8284
|
}
|
|
8285
|
+
|
|
8286
|
+
// Nix flakes
|
|
8287
|
+
const flakeNixFiles = getAllFiles(
|
|
8288
|
+
path,
|
|
8289
|
+
`${options.multiProject ? "**/" : ""}flake.nix`,
|
|
8290
|
+
options,
|
|
8291
|
+
);
|
|
8292
|
+
const flakeLockFiles = getAllFiles(
|
|
8293
|
+
path,
|
|
8294
|
+
`${options.multiProject ? "**/" : ""}flake.lock`,
|
|
8295
|
+
options,
|
|
8296
|
+
);
|
|
8297
|
+
if (flakeNixFiles.length || flakeLockFiles.length) {
|
|
8298
|
+
return await createNixBom(path, options);
|
|
8299
|
+
}
|
|
8066
8300
|
}
|
|
8067
8301
|
|
|
8068
8302
|
/**
|
|
@@ -8308,6 +8542,9 @@ export async function createBom(path, options) {
|
|
|
8308
8542
|
if (PROJECT_TYPE_ALIASES["cocoa"].includes(projectType[0])) {
|
|
8309
8543
|
return await createCocoaBom(path, options);
|
|
8310
8544
|
}
|
|
8545
|
+
if (PROJECT_TYPE_ALIASES["nix"].includes(projectType[0])) {
|
|
8546
|
+
return await createNixBom(path, options);
|
|
8547
|
+
}
|
|
8311
8548
|
switch (projectType[0]) {
|
|
8312
8549
|
case "jar":
|
|
8313
8550
|
return createJarBom(path, options);
|
package/lib/evinser/evinser.js
CHANGED
|
@@ -944,6 +944,9 @@ export function parseSemanticSlices(language, components, semanticsSlice) {
|
|
|
944
944
|
if (language === "scala") {
|
|
945
945
|
return findPurlLocations(components, semanticsSlice);
|
|
946
946
|
}
|
|
947
|
+
if (!components) {
|
|
948
|
+
return undefined;
|
|
949
|
+
}
|
|
947
950
|
const componentNamePurlMap = {};
|
|
948
951
|
const componentSymbolsMap = {};
|
|
949
952
|
const allObfuscationsMap = {};
|