@cyclonedx/cdxgen 11.1.9 → 11.2.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/bin/cdxgen.js +25 -9
- package/bin/repl.js +21 -12
- package/lib/cli/index.js +227 -51
- package/lib/evinser/evinser.js +1 -1
- package/lib/helpers/display.js +4 -2
- package/lib/helpers/logger.js +2 -1
- package/lib/helpers/package_specific/gradleutils.js +48 -0
- package/lib/helpers/package_specific/gradleutils.test.js +65 -0
- package/lib/helpers/utils.js +97 -26
- package/lib/helpers/utils.test.js +50 -4
- package/lib/helpers/validator.js +15 -0
- package/lib/managers/docker.js +37 -6
- package/lib/stages/postgen/annotator.js +40 -2
- package/lib/stages/postgen/postgen.js +89 -8
- package/lib/stages/pregen/pregen.js +1 -1
- package/package.json +3 -3
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/logger.d.ts.map +1 -1
- package/types/lib/helpers/package_specific/gradleutils.d.ts +10 -0
- package/types/lib/helpers/package_specific/gradleutils.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +2 -0
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/helpers/validator.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +4 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +2 -2
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
package/bin/cdxgen.js
CHANGED
|
@@ -579,13 +579,25 @@ const applyAdvancedOptions = (options) => {
|
|
|
579
579
|
break;
|
|
580
580
|
}
|
|
581
581
|
// When the user specifies source-code-analysis as a technique, then enable deep and evidence mode.
|
|
582
|
-
if (
|
|
583
|
-
options?.technique
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
options.
|
|
588
|
-
|
|
582
|
+
if (options?.technique && Array.isArray(options.technique)) {
|
|
583
|
+
if (options?.technique?.includes("source-code-analysis")) {
|
|
584
|
+
options.deep = true;
|
|
585
|
+
options.evidence = true;
|
|
586
|
+
}
|
|
587
|
+
if (options.technique.length === 1) {
|
|
588
|
+
thoughtLog(
|
|
589
|
+
`Wait, the user wants me to use only the following technique: '${options.technique.join(", ")}'.`,
|
|
590
|
+
);
|
|
591
|
+
} else {
|
|
592
|
+
thoughtLog(
|
|
593
|
+
`Alright, I will use only the following techniques: '${options.technique.join(", ")}' for the final BOM.`,
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (!options.installDeps) {
|
|
598
|
+
thoughtLog(
|
|
599
|
+
"I must avoid any package installations and focus solely on the available artefacts, such as lock files.",
|
|
600
|
+
);
|
|
589
601
|
}
|
|
590
602
|
return options;
|
|
591
603
|
};
|
|
@@ -764,7 +776,11 @@ const checkPermissions = (filePath, options) => {
|
|
|
764
776
|
prepareEnv(filePath, options);
|
|
765
777
|
thoughtLog("Getting ready to generate the BOM ⚡️.");
|
|
766
778
|
let bomNSData = (await createBom(filePath, options)) || {};
|
|
767
|
-
|
|
779
|
+
if (bomNSData?.bomJson) {
|
|
780
|
+
thoughtLog(
|
|
781
|
+
"Tweaking the generated BOM data with useful annotations and properties.",
|
|
782
|
+
);
|
|
783
|
+
}
|
|
768
784
|
// Add extra metadata and annotations with post processing
|
|
769
785
|
bomNSData = postProcess(bomNSData, options);
|
|
770
786
|
if (
|
|
@@ -972,7 +988,7 @@ const checkPermissions = (filePath, options) => {
|
|
|
972
988
|
}
|
|
973
989
|
}
|
|
974
990
|
// Perform automatic validation
|
|
975
|
-
if (options.validate) {
|
|
991
|
+
if (options.validate && bomNSData?.bomJson) {
|
|
976
992
|
thoughtLog("Wait, let's check the generated BOM file for any issues.");
|
|
977
993
|
if (!validateBom(bomNSData.bomJson)) {
|
|
978
994
|
process.exit(1);
|
package/bin/repl.js
CHANGED
|
@@ -137,6 +137,19 @@ cdxgenRepl.defineCommand("import", {
|
|
|
137
137
|
this.displayPrompt();
|
|
138
138
|
},
|
|
139
139
|
});
|
|
140
|
+
cdxgenRepl.defineCommand("summary", {
|
|
141
|
+
help: "summarize an existing BOM",
|
|
142
|
+
action() {
|
|
143
|
+
if (sbom) {
|
|
144
|
+
printSummary(sbom);
|
|
145
|
+
} else {
|
|
146
|
+
console.log(
|
|
147
|
+
"⚠ No BOM is loaded. Use .import command to import an existing BOM",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
this.displayPrompt();
|
|
151
|
+
},
|
|
152
|
+
});
|
|
140
153
|
cdxgenRepl.defineCommand("exit", {
|
|
141
154
|
help: "exit",
|
|
142
155
|
action() {
|
|
@@ -162,13 +175,13 @@ cdxgenRepl.defineCommand("search", {
|
|
|
162
175
|
if (sbom) {
|
|
163
176
|
if (searchStr) {
|
|
164
177
|
try {
|
|
165
|
-
|
|
166
|
-
let dependenciesSearchStr =
|
|
167
|
-
if (!
|
|
168
|
-
dependenciesSearchStr = `dependencies[ref ~> /${
|
|
169
|
-
|
|
178
|
+
let fixedSearchStr = searchStr.replaceAll("/", "\\/");
|
|
179
|
+
let dependenciesSearchStr = fixedSearchStr;
|
|
180
|
+
if (!fixedSearchStr.includes("~>")) {
|
|
181
|
+
dependenciesSearchStr = `dependencies[ref ~> /${fixedSearchStr}/i or dependsOn ~> /${fixedSearchStr}/i or provides ~> /${fixedSearchStr}/i]`;
|
|
182
|
+
fixedSearchStr = `components[group ~> /${fixedSearchStr}/i or name ~> /${fixedSearchStr}/i or description ~> /${fixedSearchStr}/i or publisher ~> /${fixedSearchStr}/i or purl ~> /${fixedSearchStr}/i or tags ~> /${fixedSearchStr}/i]`;
|
|
170
183
|
}
|
|
171
|
-
const expression = jsonata(
|
|
184
|
+
const expression = jsonata(fixedSearchStr);
|
|
172
185
|
let components = await expression.evaluate(sbom);
|
|
173
186
|
const dexpression = jsonata(dependenciesSearchStr);
|
|
174
187
|
let dependencies = await dexpression.evaluate(sbom);
|
|
@@ -181,16 +194,12 @@ cdxgenRepl.defineCommand("search", {
|
|
|
181
194
|
if (!components) {
|
|
182
195
|
console.log("No results found!");
|
|
183
196
|
} else {
|
|
184
|
-
printTable(
|
|
185
|
-
{ components, dependencies },
|
|
186
|
-
undefined,
|
|
187
|
-
originalSearchString,
|
|
188
|
-
);
|
|
197
|
+
printTable({ components, dependencies }, undefined, searchStr);
|
|
189
198
|
if (dependencies?.length) {
|
|
190
199
|
printDependencyTree(
|
|
191
200
|
{ components, dependencies },
|
|
192
201
|
"dependsOn",
|
|
193
|
-
|
|
202
|
+
searchStr,
|
|
194
203
|
);
|
|
195
204
|
}
|
|
196
205
|
}
|
package/lib/cli/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from "../helpers/envcontext.js";
|
|
33
33
|
import { thoughtLog } from "../helpers/logger.js";
|
|
34
34
|
|
|
35
|
+
import { analyzeBuildSettings } from "../helpers/package_specific/gradleutils.js";
|
|
35
36
|
import {
|
|
36
37
|
CARGO_CMD,
|
|
37
38
|
CLJ_CMD,
|
|
@@ -80,6 +81,7 @@ import {
|
|
|
80
81
|
hasAnyProjectType,
|
|
81
82
|
includeMavenTestScope,
|
|
82
83
|
isFeatureEnabled,
|
|
84
|
+
isMac,
|
|
83
85
|
isPackageManagerAllowed,
|
|
84
86
|
isPartialTree,
|
|
85
87
|
isSecureMode,
|
|
@@ -1627,6 +1629,13 @@ export async function createJavaBom(path, options) {
|
|
|
1627
1629
|
tmpParentComponent.type = "application";
|
|
1628
1630
|
if (dlist?.length) {
|
|
1629
1631
|
pkgList = pkgList.concat(dlist);
|
|
1632
|
+
if (dlist.length > 1) {
|
|
1633
|
+
thoughtLog(`Obtained ${dlist.length} components from maven.`);
|
|
1634
|
+
} else {
|
|
1635
|
+
thoughtLog(
|
|
1636
|
+
`"Received very few components from the maven dependency tree command for ${basePath}."`,
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1630
1639
|
}
|
|
1631
1640
|
// Retain the parent hierarchy
|
|
1632
1641
|
if (!Object.keys(parentComponent).length) {
|
|
@@ -1635,12 +1644,18 @@ export async function createJavaBom(path, options) {
|
|
|
1635
1644
|
} else {
|
|
1636
1645
|
parentComponent.components.push(tmpParentComponent);
|
|
1637
1646
|
}
|
|
1638
|
-
if (parsedList.dependenciesList
|
|
1647
|
+
if (parsedList.dependenciesList) {
|
|
1639
1648
|
dependencies = mergeDependencies(
|
|
1640
1649
|
dependencies,
|
|
1641
1650
|
parsedList.dependenciesList,
|
|
1642
1651
|
tmpParentComponent,
|
|
1643
1652
|
);
|
|
1653
|
+
} else {
|
|
1654
|
+
if (dlist?.length) {
|
|
1655
|
+
thoughtLog(
|
|
1656
|
+
`Hmm, I didn't find any dependencies after executing '${basename(mavenCmd)}'. However, I did get ${dlist.length} components, which is confusing.`,
|
|
1657
|
+
);
|
|
1658
|
+
}
|
|
1644
1659
|
}
|
|
1645
1660
|
unlinkSync(tempMvnTree);
|
|
1646
1661
|
}
|
|
@@ -1755,6 +1770,11 @@ export async function createJavaBom(path, options) {
|
|
|
1755
1770
|
) {
|
|
1756
1771
|
gradleRootPath = dirname(gradleFiles[0]);
|
|
1757
1772
|
}
|
|
1773
|
+
if (safeExistsSync(join(gradleRootPath, "gradle.properties"))) {
|
|
1774
|
+
thoughtLog(
|
|
1775
|
+
"Hmm, there is a gradle.properties file. Do we need any private modules or custom JVM arguments for this project 🤔?",
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1758
1778
|
// Execute gradle properties
|
|
1759
1779
|
if (
|
|
1760
1780
|
gradleFiles?.length &&
|
|
@@ -1763,10 +1783,47 @@ export async function createJavaBom(path, options) {
|
|
|
1763
1783
|
let rootProjects = [null];
|
|
1764
1784
|
let allProjectsStr = [];
|
|
1765
1785
|
let rootGradleModule = {};
|
|
1786
|
+
let includedProjectsFound = false;
|
|
1766
1787
|
if (process.env.GRADLE_INCLUDED_BUILDS) {
|
|
1767
|
-
|
|
1768
|
-
|
|
1788
|
+
// Automatically add the colon prefix
|
|
1789
|
+
const includedBuilds = process.env.GRADLE_INCLUDED_BUILDS.split(",").map(
|
|
1790
|
+
(b) => (!b.startsWith(":") ? `:${b}` : b),
|
|
1791
|
+
);
|
|
1792
|
+
rootProjects = rootProjects.concat(includedBuilds);
|
|
1793
|
+
includedProjectsFound = true;
|
|
1794
|
+
} else {
|
|
1795
|
+
// Automatically detect included builds
|
|
1796
|
+
// Only from the root path for now
|
|
1797
|
+
for (const abuildFile of [
|
|
1798
|
+
join(gradleRootPath, "build.gradle"),
|
|
1799
|
+
join(gradleRootPath, "build.gradle.kts"),
|
|
1800
|
+
join(gradleRootPath, "settings.gradle"),
|
|
1801
|
+
join(gradleRootPath, "settings.gradle.kts"),
|
|
1802
|
+
]) {
|
|
1803
|
+
if (!safeExistsSync(abuildFile)) {
|
|
1804
|
+
continue;
|
|
1805
|
+
}
|
|
1806
|
+
const buildSettings = analyzeBuildSettings(abuildFile);
|
|
1807
|
+
if (buildSettings?.includedBuilds?.length) {
|
|
1808
|
+
for (const aib of buildSettings.includedBuilds) {
|
|
1809
|
+
if (!rootProjects.includes(aib)) {
|
|
1810
|
+
rootProjects.push(aib);
|
|
1811
|
+
includedProjectsFound = true;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
break;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
if (includedProjectsFound) {
|
|
1819
|
+
thoughtLog(
|
|
1820
|
+
`Wait, this gradle project uses composite builds. I must carefully process these ${rootProjects.length} projects including the root.`,
|
|
1769
1821
|
);
|
|
1822
|
+
if (DEBUG_MODE) {
|
|
1823
|
+
console.log(
|
|
1824
|
+
`Additional root projects: ${rootProjects.join(" ").trim()}.`,
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1770
1827
|
}
|
|
1771
1828
|
const parallelPropTaskOut = executeParallelGradleProperties(
|
|
1772
1829
|
gradleRootPath,
|
|
@@ -1787,14 +1844,14 @@ export async function createJavaBom(path, options) {
|
|
|
1787
1844
|
gradleModules.set(key, rootComponent);
|
|
1788
1845
|
if (!rootProjects.includes(key)) {
|
|
1789
1846
|
if (rootGradleModule.name) {
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1847
|
+
if (DEBUG_MODE) {
|
|
1848
|
+
console.log(
|
|
1849
|
+
`Received new root component: ${rootComponent.name} with key ${key}. Please verify the value used for included builds. Using the name ${rootGradleModule.name}.`,
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
} else {
|
|
1853
|
+
rootGradleModule = rootComponent;
|
|
1796
1854
|
}
|
|
1797
|
-
rootGradleModule = rootComponent;
|
|
1798
1855
|
} else if (!allProjectsAddedPurls.includes(rootComponent["purl"])) {
|
|
1799
1856
|
allProjects.push(rootComponent);
|
|
1800
1857
|
rootDependsOn.add(rootComponent["bom-ref"]);
|
|
@@ -1809,7 +1866,11 @@ export async function createJavaBom(path, options) {
|
|
|
1809
1866
|
const modulesToSkip = process.env.GRADLE_SKIP_MODULES
|
|
1810
1867
|
? process.env.GRADLE_SKIP_MODULES.split(",")
|
|
1811
1868
|
: [];
|
|
1812
|
-
|
|
1869
|
+
if (modulesToSkip.length) {
|
|
1870
|
+
thoughtLog(
|
|
1871
|
+
`Good news. I know there are ${allProjectsStr.length} gradle modules at ${gradleRootPath}. I must skip ${modulesToSkip.length} out of these.`,
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1813
1874
|
const parallelPropTaskOut = executeParallelGradleProperties(
|
|
1814
1875
|
gradleRootPath,
|
|
1815
1876
|
allProjectsStr.filter((module) => !modulesToSkip.includes(module)),
|
|
@@ -1886,12 +1947,17 @@ export async function createJavaBom(path, options) {
|
|
|
1886
1947
|
? process.env.GRADLE_ARGS_DEPENDENCIES.split(" ")
|
|
1887
1948
|
: [],
|
|
1888
1949
|
);
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1950
|
+
if (DEBUG_MODE) {
|
|
1951
|
+
console.log(
|
|
1952
|
+
"Executing",
|
|
1953
|
+
gradleCmd,
|
|
1954
|
+
`${gradleArguments.join(" ").substring(0, 150)} ...`,
|
|
1955
|
+
"in",
|
|
1956
|
+
gradleRootPath,
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
thoughtLog(
|
|
1960
|
+
`Let's invoke '${basename(gradleCmd)}' with the arguments '${gradleArguments.join(" ").substring(0, 100)} ...'.`,
|
|
1895
1961
|
);
|
|
1896
1962
|
const sresult = spawnSync(gradleCmd, gradleArguments, {
|
|
1897
1963
|
cwd: gradleRootPath,
|
|
@@ -1899,7 +1965,6 @@ export async function createJavaBom(path, options) {
|
|
|
1899
1965
|
timeout: TIMEOUT_MS,
|
|
1900
1966
|
maxBuffer: MAX_BUFFER,
|
|
1901
1967
|
});
|
|
1902
|
-
|
|
1903
1968
|
if (sresult.status !== 0 || sresult.error) {
|
|
1904
1969
|
if (options.failOnError || DEBUG_MODE) {
|
|
1905
1970
|
console.error(sresult.stdout, sresult.stderr);
|
|
@@ -1920,22 +1985,20 @@ export async function createJavaBom(path, options) {
|
|
|
1920
1985
|
gradleRootPath,
|
|
1921
1986
|
);
|
|
1922
1987
|
const dlist = parsedList.pkgList;
|
|
1923
|
-
if (parsedList.dependenciesList
|
|
1988
|
+
if (parsedList.dependenciesList) {
|
|
1924
1989
|
dependencies = mergeDependencies(
|
|
1925
1990
|
dependencies,
|
|
1926
1991
|
parsedList.dependenciesList,
|
|
1927
1992
|
parentComponent,
|
|
1928
1993
|
);
|
|
1929
|
-
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
"Found",
|
|
1934
|
-
dlist.length,
|
|
1935
|
-
"packages in gradle project",
|
|
1936
|
-
key,
|
|
1994
|
+
} else {
|
|
1995
|
+
if (dlist?.length) {
|
|
1996
|
+
thoughtLog(
|
|
1997
|
+
`Hmm, I didn't find any dependencies after executing '${basename(gradleCmd)}' for the project ${key}. However, I did get ${dlist.length} components, which is confusing.`,
|
|
1937
1998
|
);
|
|
1938
1999
|
}
|
|
2000
|
+
}
|
|
2001
|
+
if (dlist?.length) {
|
|
1939
2002
|
pkgList = pkgList.concat(dlist);
|
|
1940
2003
|
}
|
|
1941
2004
|
}
|
|
@@ -1950,11 +2013,12 @@ export async function createJavaBom(path, options) {
|
|
|
1950
2013
|
);
|
|
1951
2014
|
}
|
|
1952
2015
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
pkgList.length,
|
|
1956
|
-
"from this gradle project. De-duping this list ...",
|
|
2016
|
+
thoughtLog(
|
|
2017
|
+
`Obtained ${pkgList.length} components by executing the '${basename(gradleCmd)}' command.`,
|
|
1957
2018
|
);
|
|
2019
|
+
if (DEBUG_MODE) {
|
|
2020
|
+
console.log("Obtained", pkgList.length, "from this gradle project.");
|
|
2021
|
+
}
|
|
1958
2022
|
} else {
|
|
1959
2023
|
thoughtLog(
|
|
1960
2024
|
"**GRADLE:** SBOM is incomplete. I recommend troubleshooting the issue to improve the BOM precision.",
|
|
@@ -2405,19 +2469,18 @@ export async function createNodejsBom(path, options) {
|
|
|
2405
2469
|
`${options.multiProject ? "**/" : ""}package.json`,
|
|
2406
2470
|
options,
|
|
2407
2471
|
);
|
|
2408
|
-
const yarnLockFile = getAllFiles(path, "yarn.lock", options);
|
|
2409
|
-
const pnpmLockFile = getAllFiles(path, "pnpm-lock.yaml", options);
|
|
2410
2472
|
const npmInstallCount = Number.parseInt(process.env.NPM_INSTALL_COUNT) || 2;
|
|
2411
2473
|
// Automatic npm install logic.
|
|
2412
2474
|
// Only perform npm install for smaller projects (< 2 package.json) without the correct number of lock files
|
|
2413
2475
|
if (
|
|
2414
2476
|
(pkgJsonLockFiles?.length === 0 ||
|
|
2415
2477
|
pkgJsonLockFiles?.length < pkgJsonFiles?.length) &&
|
|
2416
|
-
|
|
2417
|
-
|
|
2478
|
+
yarnLockFiles?.length === 0 &&
|
|
2479
|
+
pnpmLockFiles?.length === 0 &&
|
|
2418
2480
|
pkgJsonFiles?.length <= npmInstallCount &&
|
|
2419
2481
|
options.installDeps
|
|
2420
2482
|
) {
|
|
2483
|
+
let anyInstallSuccess = false;
|
|
2421
2484
|
for (const apkgJson of pkgJsonFiles) {
|
|
2422
2485
|
let pkgMgr = "npm";
|
|
2423
2486
|
const supPkgMgrs = ["npm", "yarn", "yarnpkg", "pnpm", "pnpx"];
|
|
@@ -2427,8 +2490,21 @@ export async function createNodejsBom(path, options) {
|
|
|
2427
2490
|
if (mgrData) {
|
|
2428
2491
|
mgr = mgrData.split("@")[0];
|
|
2429
2492
|
}
|
|
2430
|
-
|
|
2493
|
+
// Try harder to identify the correct package manager
|
|
2494
|
+
if (options?.projectType?.includes("npm")) {
|
|
2495
|
+
pkgMgr = "npm";
|
|
2496
|
+
} else if (supPkgMgrs.includes(mgr)) {
|
|
2431
2497
|
pkgMgr = mgr;
|
|
2498
|
+
} else if (pkgData?.engines?.yarn) {
|
|
2499
|
+
pkgMgr = "yarn";
|
|
2500
|
+
} else if (
|
|
2501
|
+
isPackageManagerAllowed("yarn", ["npm", "pnpm", "rush"], options)
|
|
2502
|
+
) {
|
|
2503
|
+
pkgMgr = "yarn";
|
|
2504
|
+
} else if (
|
|
2505
|
+
isPackageManagerAllowed("pnpm", ["npm", "yarn", "rush"], options)
|
|
2506
|
+
) {
|
|
2507
|
+
pkgMgr = "pnpm";
|
|
2432
2508
|
}
|
|
2433
2509
|
let installCommand = "install";
|
|
2434
2510
|
if (pkgMgr === "npm" && isSecureMode && pkgJsonLockFiles?.length > 0) {
|
|
@@ -2463,6 +2539,16 @@ export async function createNodejsBom(path, options) {
|
|
|
2463
2539
|
installArgs.push("--package-lock");
|
|
2464
2540
|
}
|
|
2465
2541
|
}
|
|
2542
|
+
if (pkgMgr === "npm" && yarnLockFiles.length) {
|
|
2543
|
+
thoughtLog(
|
|
2544
|
+
`Wait, there are ${yarnLockFiles.length} yarn.lock files in this project; however, I'm about to invoke the '${pkgMgr} ${installArgs[0]}' command.`,
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
if (pkgMgr !== "npm") {
|
|
2548
|
+
thoughtLog(
|
|
2549
|
+
`**PACKAGE MANAGER**: Let's run the '${pkgMgr}' command with the arguments '${installArgs.join(" ")}' to generate the needed lock files.`,
|
|
2550
|
+
);
|
|
2551
|
+
}
|
|
2466
2552
|
console.log(
|
|
2467
2553
|
`Executing '${pkgMgr} ${installArgs.join(" ")}' in`,
|
|
2468
2554
|
basePath,
|
|
@@ -2477,6 +2563,9 @@ export async function createNodejsBom(path, options) {
|
|
|
2477
2563
|
console.error(
|
|
2478
2564
|
`${pkgMgr} install has failed. Generated SBOM will be empty or with a lower precision.`,
|
|
2479
2565
|
);
|
|
2566
|
+
thoughtLog(
|
|
2567
|
+
"It looks like the install command has failed. I'm considering some troubleshooting ideas.",
|
|
2568
|
+
);
|
|
2480
2569
|
if (DEBUG_MODE && result.stdout) {
|
|
2481
2570
|
if (result.stdout.includes("EBADENGINE Unsupported engine")) {
|
|
2482
2571
|
console.log(
|
|
@@ -2523,6 +2612,8 @@ export async function createNodejsBom(path, options) {
|
|
|
2523
2612
|
console.log(result.stderr);
|
|
2524
2613
|
}
|
|
2525
2614
|
options.failOnError && process.exit(1);
|
|
2615
|
+
} else {
|
|
2616
|
+
anyInstallSuccess = true;
|
|
2526
2617
|
}
|
|
2527
2618
|
}
|
|
2528
2619
|
pkgLockFiles = getAllFiles(
|
|
@@ -2540,6 +2631,16 @@ export async function createNodejsBom(path, options) {
|
|
|
2540
2631
|
`${options.multiProject ? "**/" : ""}yarn.lock`,
|
|
2541
2632
|
options,
|
|
2542
2633
|
);
|
|
2634
|
+
if (
|
|
2635
|
+
anyInstallSuccess &&
|
|
2636
|
+
!pkgLockFiles.length &&
|
|
2637
|
+
!pnpmLockFiles.length &&
|
|
2638
|
+
!yarnLockFiles.length
|
|
2639
|
+
) {
|
|
2640
|
+
thoughtLog(
|
|
2641
|
+
`Despite a successful installation step, I didn't find any lock files. Perhaps they're being created elsewhere, such as in the root directory. I am currently checking the directory at ${path}.`,
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2543
2644
|
}
|
|
2544
2645
|
if (
|
|
2545
2646
|
pnpmLockFiles?.length &&
|
|
@@ -2977,6 +3078,17 @@ export async function createNodejsBom(path, options) {
|
|
|
2977
3078
|
}
|
|
2978
3079
|
}
|
|
2979
3080
|
}
|
|
3081
|
+
if (!pkgList.length && (yarnLockFiles.length || pkgLockFiles.length)) {
|
|
3082
|
+
if (options.projectType.length) {
|
|
3083
|
+
thoughtLog(
|
|
3084
|
+
`Despite seeing some lock files, I didn't find any components in this Node.js project. Is there an issue with the project type '${options.projectType.join(", ")}' used 🤔? I recommend trying again with a different type.`,
|
|
3085
|
+
);
|
|
3086
|
+
} else {
|
|
3087
|
+
thoughtLog(
|
|
3088
|
+
"Despite seeing some lock files, I didn't find any components in this Node.js project. Feels like a bug.",
|
|
3089
|
+
);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
2980
3092
|
// Retain the components of parent component
|
|
2981
3093
|
if (parentSubComponents.length) {
|
|
2982
3094
|
parentComponent.components = parentSubComponents;
|
|
@@ -4774,6 +4886,7 @@ export async function createSwiftBom(path, options) {
|
|
|
4774
4886
|
let dependencies = [];
|
|
4775
4887
|
let parentComponent = {};
|
|
4776
4888
|
const completedPath = [];
|
|
4889
|
+
let packageArgsMessageShown = false;
|
|
4777
4890
|
if (pkgResolvedFiles.length) {
|
|
4778
4891
|
for (const f of pkgResolvedFiles) {
|
|
4779
4892
|
if (!parentComponent || !Object.keys(parentComponent).length) {
|
|
@@ -4787,6 +4900,9 @@ export async function createSwiftBom(path, options) {
|
|
|
4787
4900
|
pkgList = pkgList.concat(dlist);
|
|
4788
4901
|
}
|
|
4789
4902
|
}
|
|
4903
|
+
thoughtLog(
|
|
4904
|
+
`It looks like we have ${pkgResolvedFiles.length} Package.resolved files, which is good. To compute the dependency tree, let's try using the swift package command 📦.`,
|
|
4905
|
+
);
|
|
4790
4906
|
}
|
|
4791
4907
|
if (swiftFiles.length) {
|
|
4792
4908
|
for (const f of swiftFiles) {
|
|
@@ -4795,7 +4911,25 @@ export async function createSwiftBom(path, options) {
|
|
|
4795
4911
|
continue;
|
|
4796
4912
|
}
|
|
4797
4913
|
let treeData = undefined;
|
|
4798
|
-
let packageArgs = ["package"
|
|
4914
|
+
let packageArgs = ["package"];
|
|
4915
|
+
// Additional arguments to pass to the swift package command.
|
|
4916
|
+
// Example: --swift-sdks-path <swift-sdks-path> --jobs <jobs>
|
|
4917
|
+
if (process.env.SWIFT_PACKAGE_ARGS) {
|
|
4918
|
+
packageArgs = packageArgs.concat(
|
|
4919
|
+
process.env.SWIFT_PACKAGE_ARGS.split(" "),
|
|
4920
|
+
);
|
|
4921
|
+
if (!packageArgsMessageShown) {
|
|
4922
|
+
thoughtLog(
|
|
4923
|
+
`Wait, let's use the additional arguments '${process.env.SWIFT_PACKAGE_ARGS}' for the swift package command.`,
|
|
4924
|
+
);
|
|
4925
|
+
packageArgsMessageShown = true;
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
packageArgs = packageArgs.concat([
|
|
4929
|
+
"show-dependencies",
|
|
4930
|
+
"--format",
|
|
4931
|
+
"json",
|
|
4932
|
+
]);
|
|
4799
4933
|
let swiftCommand = SWIFT_CMD;
|
|
4800
4934
|
if (swiftCommand.startsWith("xcrun")) {
|
|
4801
4935
|
swiftCommand = "xcrun";
|
|
@@ -4803,8 +4937,7 @@ export async function createSwiftBom(path, options) {
|
|
|
4803
4937
|
}
|
|
4804
4938
|
if (DEBUG_MODE) {
|
|
4805
4939
|
console.log(
|
|
4806
|
-
`Executing '${swiftCommand} ${packageArgs.join(" ")}' in
|
|
4807
|
-
basePath,
|
|
4940
|
+
`Executing '${swiftCommand} ${packageArgs.join(" ")}' in ${basePath}. Please wait ...`,
|
|
4808
4941
|
);
|
|
4809
4942
|
}
|
|
4810
4943
|
const result = spawnSync(swiftCommand, packageArgs, {
|
|
@@ -4813,15 +4946,29 @@ export async function createSwiftBom(path, options) {
|
|
|
4813
4946
|
timeout: TIMEOUT_MS,
|
|
4814
4947
|
maxBuffer: MAX_BUFFER,
|
|
4815
4948
|
});
|
|
4816
|
-
if (result.
|
|
4949
|
+
if (result.stdout) {
|
|
4817
4950
|
completedPath.push(basePath);
|
|
4818
4951
|
treeData = Buffer.from(result.stdout).toString();
|
|
4819
4952
|
const retData = parseSwiftJsonTree(treeData, f);
|
|
4820
|
-
if (retData.
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4953
|
+
if (retData.rootList?.length) {
|
|
4954
|
+
if (!Object.keys(parentComponent).length) {
|
|
4955
|
+
parentComponent = retData.rootList[0];
|
|
4956
|
+
if (retData.rootList.length > 1) {
|
|
4957
|
+
if (!parentComponent.components) {
|
|
4958
|
+
parentComponent.components = [];
|
|
4959
|
+
}
|
|
4960
|
+
for (const p of retData.rootList.splice(0, 1)) {
|
|
4961
|
+
parentComponent.components.push(p);
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
} else {
|
|
4965
|
+
if (!parentComponent.components) {
|
|
4966
|
+
parentComponent.components = [];
|
|
4967
|
+
}
|
|
4968
|
+
parentComponent.components.concat(retData.rootList);
|
|
4969
|
+
}
|
|
4824
4970
|
}
|
|
4971
|
+
pkgList = pkgList.concat(retData.pkgList);
|
|
4825
4972
|
if (retData.dependenciesList) {
|
|
4826
4973
|
dependencies = mergeDependencies(
|
|
4827
4974
|
dependencies,
|
|
@@ -4829,11 +4976,25 @@ export async function createSwiftBom(path, options) {
|
|
|
4829
4976
|
parentComponent,
|
|
4830
4977
|
);
|
|
4831
4978
|
}
|
|
4832
|
-
}
|
|
4833
|
-
|
|
4979
|
+
}
|
|
4980
|
+
if (result.status !== 0 || result.error) {
|
|
4981
|
+
if (result?.stderr?.includes("Source files for target")) {
|
|
4982
|
+
console.log(
|
|
4983
|
+
"The Sources directory is missing. Please run cdxgen from the directory that contains the complete source code.",
|
|
4984
|
+
);
|
|
4985
|
+
thoughtLog(
|
|
4986
|
+
`It looks like the 'Sources' directory is missing, so we are missing the components and dependencies for '${basename(basePath)}'.`,
|
|
4987
|
+
);
|
|
4988
|
+
} else if (process.env.CDXGEN_IN_CONTAINER !== "true") {
|
|
4834
4989
|
console.log(
|
|
4835
|
-
"
|
|
4990
|
+
"Consider using the cdxgen container image (`ghcr.io/cyclonedx/cdxgen`), which includes Swift and additional build tools.",
|
|
4836
4991
|
);
|
|
4992
|
+
if (!isMac) {
|
|
4993
|
+
console.log("Alternatively, try building this project from a Mac.");
|
|
4994
|
+
thoughtLog(
|
|
4995
|
+
"I'm wondering if the results might be better on a Mac 🤔.",
|
|
4996
|
+
);
|
|
4997
|
+
}
|
|
4837
4998
|
}
|
|
4838
4999
|
console.error(result.stderr);
|
|
4839
5000
|
options.failOnError && process.exit(1);
|
|
@@ -7259,6 +7420,7 @@ export async function createBom(path, options) {
|
|
|
7259
7420
|
console.log(
|
|
7260
7421
|
`OS BOM generation has failed due to problems with exporting the image ${path}`,
|
|
7261
7422
|
);
|
|
7423
|
+
options.failOnError && process.exit(1);
|
|
7262
7424
|
return {};
|
|
7263
7425
|
}
|
|
7264
7426
|
isContainerMode = true;
|
|
@@ -7277,6 +7439,14 @@ export async function createBom(path, options) {
|
|
|
7277
7439
|
if (exportData) {
|
|
7278
7440
|
isContainerMode = true;
|
|
7279
7441
|
} else {
|
|
7442
|
+
// Fail early for oci types
|
|
7443
|
+
if (hasAnyProjectType(["oci"], options, false)) {
|
|
7444
|
+
console.log(
|
|
7445
|
+
`OCI BOM generation has failed due to problems with exporting the image ${path}.`,
|
|
7446
|
+
);
|
|
7447
|
+
options.failOnError && process.exit(1);
|
|
7448
|
+
return {};
|
|
7449
|
+
}
|
|
7280
7450
|
if (DEBUG_MODE) {
|
|
7281
7451
|
console.log(path, "doesn't appear to be a valid container image.");
|
|
7282
7452
|
}
|
|
@@ -7368,15 +7538,21 @@ export async function createBom(path, options) {
|
|
|
7368
7538
|
}
|
|
7369
7539
|
if (projectType.length > 1) {
|
|
7370
7540
|
thoughtLog(
|
|
7371
|
-
`The user has specified multiple project types: projectType.join(", "). Let's focus on the types one at a time.`,
|
|
7541
|
+
`The user has specified multiple project types: ${projectType.join(", ")}. Let's focus on the types one at a time.`,
|
|
7372
7542
|
);
|
|
7373
7543
|
console.log("Generate BOM for project types:", projectType.join(", "));
|
|
7374
7544
|
return await createMultiXBom(path, options);
|
|
7375
7545
|
}
|
|
7376
7546
|
if (projectType.length === 1) {
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7547
|
+
if (hasAnyProjectType(["oci"], options, false)) {
|
|
7548
|
+
thoughtLog(
|
|
7549
|
+
"Okay, we're generating an SBOM for the OCI type. We'll need a compatible tool like Docker, Podman, or Nerdctl, along with the binary plugins.",
|
|
7550
|
+
);
|
|
7551
|
+
} else {
|
|
7552
|
+
thoughtLog(
|
|
7553
|
+
`The user wants me to focus on a single type, '${projectType}'. Could there be an issue with auto-detection, or might they use another tool like cyclonedx-cli to merge all the generated BOMs later?`,
|
|
7554
|
+
);
|
|
7555
|
+
}
|
|
7380
7556
|
}
|
|
7381
7557
|
// Use the project type alias to return any singular BOM
|
|
7382
7558
|
if (PROJECT_TYPE_ALIASES["java"].includes(projectType[0])) {
|
package/lib/evinser/evinser.js
CHANGED
|
@@ -228,7 +228,7 @@ export async function createSlice(
|
|
|
228
228
|
// Handle language with version types
|
|
229
229
|
if (language.startsWith("ruby")) {
|
|
230
230
|
language = "ruby";
|
|
231
|
-
} else if (language.startsWith("java")) {
|
|
231
|
+
} else if (language.startsWith("java") && language !== "javascript") {
|
|
232
232
|
language = "java";
|
|
233
233
|
} else if (language.startsWith("node")) {
|
|
234
234
|
language = "js";
|
package/lib/helpers/display.js
CHANGED
|
@@ -465,8 +465,10 @@ export function printSummary(bomJson) {
|
|
|
465
465
|
let bomPkgNamespaces = [];
|
|
466
466
|
// Print any annotations found
|
|
467
467
|
const annotations = bomJson?.annotations || [];
|
|
468
|
-
|
|
469
|
-
|
|
468
|
+
if (annotations.length) {
|
|
469
|
+
for (const annot of annotations) {
|
|
470
|
+
message = `${message}\n${annot.text}`;
|
|
471
|
+
}
|
|
470
472
|
}
|
|
471
473
|
const tools = bomJson?.metadata?.tools?.components;
|
|
472
474
|
if (tools) {
|
package/lib/helpers/logger.js
CHANGED
|
@@ -6,7 +6,8 @@ import colors from "yoctocolors";
|
|
|
6
6
|
// Enable think mode
|
|
7
7
|
export const THINK_MODE =
|
|
8
8
|
process.env.CDXGEN_THOUGHT_LOG ||
|
|
9
|
-
["true", "1"].includes(process.env.CDXGEN_THINK_MODE)
|
|
9
|
+
["true", "1"].includes(process.env.CDXGEN_THINK_MODE) ||
|
|
10
|
+
process.env.CDXGEN_DEBUG_MODE === "verbose";
|
|
10
11
|
|
|
11
12
|
const output = process.env.CDXGEN_THOUGHT_LOG
|
|
12
13
|
? fs.createWriteStream(process.env.CDXGEN_THOUGHT_LOG)
|