@cyclonedx/cdxgen 12.4.3 → 12.4.4

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.
Files changed (38) hide show
  1. package/README.md +6 -0
  2. package/bin/audit.js +7 -0
  3. package/bin/cdxgen.js +48 -2
  4. package/bin/evinse.js +7 -0
  5. package/lib/audit/index.js +165 -2
  6. package/lib/audit/index.poku.js +462 -0
  7. package/lib/cli/index.js +317 -169
  8. package/lib/evinser/evinser.js +31 -9
  9. package/lib/helpers/analyzer.js +890 -0
  10. package/lib/helpers/analyzer.poku.js +341 -0
  11. package/lib/helpers/atomUtils.js +445 -0
  12. package/lib/helpers/atomUtils.poku.js +137 -0
  13. package/lib/helpers/bomUtils.js +71 -0
  14. package/lib/helpers/bomUtils.poku.js +45 -0
  15. package/lib/helpers/depsUtils.js +146 -0
  16. package/lib/helpers/depsUtils.poku.js +183 -0
  17. package/lib/helpers/utils.js +585 -191
  18. package/lib/helpers/utils.poku.js +357 -4
  19. package/lib/managers/binary.js +18 -9
  20. package/lib/stages/postgen/postgen.js +215 -0
  21. package/lib/stages/postgen/postgen.poku.js +218 -3
  22. package/lib/validator/bomValidator.js +11 -2
  23. package/package.json +8 -8
  24. package/types/lib/audit/index.d.ts.map +1 -1
  25. package/types/lib/cli/index.d.ts.map +1 -1
  26. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  27. package/types/lib/helpers/atomUtils.d.ts +18 -0
  28. package/types/lib/helpers/atomUtils.d.ts.map +1 -0
  29. package/types/lib/helpers/bomUtils.d.ts +10 -0
  30. package/types/lib/helpers/bomUtils.d.ts.map +1 -1
  31. package/types/lib/helpers/depsUtils.d.ts +9 -0
  32. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  33. package/types/lib/helpers/utils.d.ts +19 -0
  34. package/types/lib/helpers/utils.d.ts.map +1 -1
  35. package/types/lib/managers/binary.d.ts +2 -1
  36. package/types/lib/managers/binary.d.ts.map +1 -1
  37. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  38. package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/cli/index.js CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  } from "../helpers/asarutils.js";
43
43
  import { expandBomAuditCategories } from "../helpers/auditCategories.js";
44
44
  import {
45
+ isCycloneDxComponentTypeEnabled,
45
46
  setCycloneDxFormat,
46
47
  toCycloneDxSpecVersionString,
47
48
  } from "../helpers/bomUtils.js";
@@ -57,8 +58,10 @@ import {
57
58
  discoverChromiumExtensionDirs,
58
59
  } from "../helpers/chromextutils.js";
59
60
  import {
61
+ filterInvalidCryptoComponents,
60
62
  mergeDependencies,
61
63
  mergeServices,
64
+ propagateRequiredScopeFromDependencies,
62
65
  trimComponents,
63
66
  } from "../helpers/depsUtils.js";
64
67
  import {
@@ -192,7 +195,9 @@ import {
192
195
  parseLeinDep,
193
196
  parseLeiningenData,
194
197
  parseMakeDFile,
198
+ parseMavenArgs,
195
199
  parseMavenTree,
200
+ parseMavenTreeJson,
196
201
  parseMillDependency,
197
202
  parseMinJs,
198
203
  parseMixLockData,
@@ -1727,7 +1732,7 @@ export async function createJavaBom(path, options) {
1727
1732
  }
1728
1733
  // Support for passing additional settings and profile to maven
1729
1734
  if (process.env.MVN_ARGS) {
1730
- const addArgs = process.env.MVN_ARGS.split(" ");
1735
+ const addArgs = parseMavenArgs(process.env.MVN_ARGS);
1731
1736
  mvnArgs = mvnArgs.concat(addArgs);
1732
1737
  }
1733
1738
  // specVersion 1.4 doesn't support externalReferences.type=distribution-intake
@@ -1804,180 +1809,270 @@ export async function createJavaBom(path, options) {
1804
1809
  result?.status !== 0 ||
1805
1810
  result?.error
1806
1811
  ) {
1807
- const tempMvnTree = join(basePath, "target", "cdxgen-mvn-tree.txt");
1808
- const tempMvnParentTree = join(
1809
- basePath,
1810
- "target",
1812
+ const tempRoot = getTmpDir();
1813
+ const tempDir = safeMkdtempSync(join(tempRoot, "cdxgen-mvn-"));
1814
+ const tempMvnTree = join(tempDir, "cdxgen-mvn-tree.json");
1815
+ const tempMvnTreeText = join(tempDir, "cdxgen-mvn-tree.txt");
1816
+ const tempMvnParentTree = join(tempDir, "cdxgen-mvn-parent-tree.json");
1817
+ const tempMvnParentTreeText = join(
1818
+ tempDir,
1811
1819
  "cdxgen-mvn-parent-tree.txt",
1812
1820
  );
1813
- let mvnTreeArgs = ["dependency:tree", `-DoutputFile=${tempMvnTree}`];
1814
- let addArgs = "";
1815
- if (process.env.MVN_ARGS) {
1816
- addArgs = process.env.MVN_ARGS.split(" ");
1817
- mvnTreeArgs = mvnTreeArgs.concat(addArgs);
1818
- }
1819
- // Automatically use settings.xml to improve the success for fallback
1820
- if (safeExistsSync(settingsXml)) {
1821
- mvnTreeArgs.push("-s");
1822
- mvnTreeArgs.push(settingsXml);
1823
- }
1824
- // For the first pom alone, we need to execute first in non-recursive mode to capture
1825
- // the parent component. Then, we execute all of them in recursive mode
1826
- if (f === firstPom) {
1827
- thoughtLog(
1828
- "What is the parent component here? Let's use maven command to find out.",
1829
- );
1830
- let findParentComponentArgs = [
1821
+ try {
1822
+ let mvnTreeArgs = [
1831
1823
  "dependency:tree",
1832
- "-N",
1833
- `-DoutputFile=${tempMvnParentTree}`,
1824
+ `-DoutputFile=${tempMvnTree}`,
1825
+ "-DoutputType=json",
1834
1826
  ];
1835
- if (addArgs) {
1836
- findParentComponentArgs = findParentComponentArgs.concat(addArgs);
1827
+ let addArgs = [];
1828
+ if (process.env.MVN_ARGS) {
1829
+ addArgs = parseMavenArgs(process.env.MVN_ARGS);
1830
+ mvnTreeArgs = mvnTreeArgs.concat(addArgs);
1837
1831
  }
1838
- result = safeSpawnSync("mvn", findParentComponentArgs, {
1839
- cwd: basePath,
1840
- shell: isWin,
1841
- });
1842
- if (result.status === 0) {
1832
+ // Automatically use settings.xml to improve the success for fallback
1833
+ if (safeExistsSync(settingsXml)) {
1834
+ mvnTreeArgs.push("-s");
1835
+ mvnTreeArgs.push(settingsXml);
1836
+ }
1837
+ // For the first pom alone, we need to execute first in non-recursive mode to capture
1838
+ // the parent component. Then, we execute all of them in recursive mode
1839
+ if (f === firstPom) {
1840
+ thoughtLog(
1841
+ "What is the parent component here? Let's use maven command to find out.",
1842
+ );
1843
+ let findParentComponentArgs = [
1844
+ "dependency:tree",
1845
+ "-N",
1846
+ `-DoutputFile=${tempMvnParentTree}`,
1847
+ "-DoutputType=json",
1848
+ ];
1849
+ if (addArgs.length) {
1850
+ findParentComponentArgs = findParentComponentArgs.concat(addArgs);
1851
+ }
1852
+ result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
1853
+ cwd: basePath,
1854
+ shell: isWin,
1855
+ });
1856
+ // If json is empty or unparseable, fallback to text parsing
1857
+ let emptyJson = !safeExistsSync(tempMvnParentTree);
1843
1858
  if (safeExistsSync(tempMvnParentTree)) {
1844
1859
  const mvnTreeString = readFileSync(tempMvnParentTree, {
1845
1860
  encoding: "utf-8",
1846
1861
  });
1847
- const parsedList = parseMavenTree(mvnTreeString, f);
1848
- const dlist = parsedList.pkgList;
1849
- const tmpParentComponent = dlist.splice(0, 1)[0];
1850
- tmpParentComponent.type = "application";
1851
- parentComponent = tmpParentComponent;
1852
- parentComponent.components = [];
1853
- if (parentComponent.name) {
1854
- thoughtLog(
1855
- `Parent component is called ${parentComponent.name}!`,
1856
- );
1862
+ const parsedList = parseMavenTreeJson(mvnTreeString, f);
1863
+ if (!parsedList?.pkgList?.length) {
1864
+ emptyJson = true;
1865
+ // Remove the invalid json file so text output is preferred below.
1866
+ if (safeExistsSync(tempMvnParentTree)) {
1867
+ safeUnlinkSync(tempMvnParentTree);
1868
+ }
1869
+ }
1870
+ }
1871
+ if (result.status !== 0 || result.error || emptyJson) {
1872
+ findParentComponentArgs = [
1873
+ "dependency:tree",
1874
+ "-N",
1875
+ `-DoutputFile=${tempMvnParentTreeText}`,
1876
+ ];
1877
+ if (addArgs.length) {
1878
+ findParentComponentArgs =
1879
+ findParentComponentArgs.concat(addArgs);
1880
+ }
1881
+ result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
1882
+ cwd: basePath,
1883
+ shell: isWin,
1884
+ });
1885
+ }
1886
+ if (result.status === 0) {
1887
+ const parentTreeFile = safeExistsSync(tempMvnParentTreeText)
1888
+ ? tempMvnParentTreeText
1889
+ : tempMvnParentTree;
1890
+ if (safeExistsSync(parentTreeFile)) {
1891
+ const mvnTreeString = readFileSync(parentTreeFile, {
1892
+ encoding: "utf-8",
1893
+ });
1894
+ const parsedList = parentTreeFile.endsWith(".json")
1895
+ ? parseMavenTreeJson(mvnTreeString, f)
1896
+ : parseMavenTree(mvnTreeString, f);
1897
+ const dlist = parsedList.pkgList || [];
1898
+ if (dlist.length) {
1899
+ const tmpParentComponent = dlist.splice(0, 1)[0];
1900
+ tmpParentComponent.type = "application";
1901
+ parentComponent = tmpParentComponent;
1902
+ parentComponent.components = [];
1903
+ if (parentComponent.name) {
1904
+ thoughtLog(
1905
+ `Parent component is called ${parentComponent.name}!`,
1906
+ );
1907
+ }
1908
+ }
1857
1909
  }
1858
1910
  }
1859
1911
  }
1860
- }
1861
- thoughtLog(
1862
- `**MAVEN**: Let's use Maven to collect packages from ${basePath}.`,
1863
- );
1864
- if (DEBUG_MODE) {
1865
- console.log(`Executing 'mvn dependency:tree ...' in ${basePath}`);
1866
- }
1867
- // Prefer the built-in maven
1868
- result = safeSpawnSync(
1869
- PREFER_MAVEN_DEPS_TREE ? "mvn" : mavenCmd,
1870
- mvnTreeArgs,
1871
- {
1912
+ thoughtLog(
1913
+ `**MAVEN**: Let's use Maven to collect packages from ${basePath}.`,
1914
+ );
1915
+ if (DEBUG_MODE) {
1916
+ console.log(
1917
+ `Executing '${basename(mavenCmd)} dependency:tree ...' in ${basePath}`,
1918
+ );
1919
+ }
1920
+ result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
1872
1921
  cwd: basePath,
1873
1922
  shell: isWin,
1874
- },
1875
- );
1876
- if (result.status !== 0 || result.error) {
1877
- possible_misses = true;
1878
- // Our approach to recursively invoking the maven plugin for each sub-module is bound to result in failures
1879
- // These could be due to a range of reasons that are covered below.
1880
- if (pomFiles.length === 1 || DEBUG_MODE || PREFER_MAVEN_DEPS_TREE) {
1881
- if (result.stdout) {
1882
- console.log(result.stdout);
1923
+ });
1924
+ let emptyJson = !safeExistsSync(tempMvnTree);
1925
+ if (safeExistsSync(tempMvnTree)) {
1926
+ const mvnTreeString = readFileSync(tempMvnTree, {
1927
+ encoding: "utf-8",
1928
+ });
1929
+ const parsedList = parseMavenTreeJson(mvnTreeString, f);
1930
+ if (!parsedList?.pkgList?.length) {
1931
+ emptyJson = true;
1932
+ // Remove the invalid json file so text output is preferred below.
1933
+ if (safeExistsSync(tempMvnTree)) {
1934
+ safeUnlinkSync(tempMvnTree);
1935
+ }
1883
1936
  }
1884
- if (result.stderr) {
1885
- console.log(result.stderr);
1886
- console.log("The above build errors could be due to:\n");
1937
+ }
1938
+ if (result.status !== 0 || result.error || emptyJson) {
1939
+ mvnTreeArgs = [
1940
+ "dependency:tree",
1941
+ `-DoutputFile=${tempMvnTreeText}`,
1942
+ ];
1943
+ if (addArgs.length) {
1944
+ mvnTreeArgs = mvnTreeArgs.concat(addArgs);
1887
1945
  }
1888
- if (
1889
- result.stdout &&
1890
- (result.stdout.includes("Non-resolvable parent POM") ||
1891
- result.stdout.includes("points at wrong local POM"))
1892
- ) {
1893
- console.log(
1894
- "1. Check if the pom.xml contains valid settings for parent and modules. Some projects can be built only from a specific directory.",
1895
- );
1896
- } else if (
1897
- result.stdout &&
1898
- (result.stdout.includes("Could not resolve dependencies") ||
1899
- result.stdout.includes("no dependency information available") ||
1900
- result.stdout.includes(
1901
- "The following artifacts could not be resolved",
1902
- ))
1903
- ) {
1904
- console.log(
1905
- "1. Try building the project with 'mvn package -Dmaven.test.skip=true' using the correct version of Java and maven before invoking cdxgen.",
1906
- );
1907
- } else if (
1908
- result.stdout?.includes(
1909
- "Could not resolve target platform specification",
1910
- )
1911
- ) {
1946
+ if (safeExistsSync(settingsXml)) {
1947
+ mvnTreeArgs.push("-s");
1948
+ mvnTreeArgs.push(settingsXml);
1949
+ }
1950
+ result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
1951
+ cwd: basePath,
1952
+ shell: isWin,
1953
+ });
1954
+ }
1955
+ if (result.status !== 0 || result.error) {
1956
+ possible_misses = true;
1957
+ // Our approach to recursively invoking the maven plugin for each sub-module is bound to result in failures
1958
+ // These could be due to a range of reasons that are covered below.
1959
+ if (pomFiles.length === 1 || DEBUG_MODE || PREFER_MAVEN_DEPS_TREE) {
1960
+ if (result.stdout) {
1961
+ console.log(result.stdout);
1962
+ }
1963
+ if (result.stderr) {
1964
+ console.log(result.stderr);
1965
+ console.log("The above build errors could be due to:\n");
1966
+ }
1967
+ if (
1968
+ result.stdout &&
1969
+ (result.stdout.includes("Non-resolvable parent POM") ||
1970
+ result.stdout.includes("points at wrong local POM"))
1971
+ ) {
1972
+ console.log(
1973
+ "1. Check if the pom.xml contains valid settings for parent and modules. Some projects can be built only from a specific directory.",
1974
+ );
1975
+ } else if (
1976
+ result.stdout &&
1977
+ (result.stdout.includes("Could not resolve dependencies") ||
1978
+ result.stdout.includes(
1979
+ "no dependency information available",
1980
+ ) ||
1981
+ result.stdout.includes(
1982
+ "The following artifacts could not be resolved",
1983
+ ))
1984
+ ) {
1985
+ console.log(
1986
+ "1. Try building the project with 'mvn package -Dmaven.test.skip=true' using the correct version of Java and maven before invoking cdxgen.",
1987
+ );
1988
+ } else if (
1989
+ result.stdout?.includes(
1990
+ "Could not resolve target platform specification",
1991
+ )
1992
+ ) {
1993
+ console.log(
1994
+ "1. Some projects can be built only from the root directory. Invoke cdxgen with --no-recurse option",
1995
+ );
1996
+ } else {
1997
+ console.log(
1998
+ "1. Java version requirement: cdxgen container image bundles Java 24 with maven 3.9 which might be incompatible. Try running cdxgen with the custom JDK11-based image `ghcr.io/cyclonedx/cdxgen-java11:v12`.",
1999
+ );
2000
+ }
1912
2001
  console.log(
1913
- "1. Some projects can be built only from the root directory. Invoke cdxgen with --no-recurse option",
2002
+ "2. Private dependencies cannot be downloaded: Check if any additional arguments must be passed to maven and set them via MVN_ARGS environment variable.",
1914
2003
  );
1915
- } else {
1916
2004
  console.log(
1917
- "1. Java version requirement: cdxgen container image bundles Java 24 with maven 3.9 which might be incompatible. Try running cdxgen with the custom JDK11-based image `ghcr.io/cyclonedx/cdxgen-java11:v12`.",
2005
+ "3. Check if all required environment variables including any maven profile arguments are passed correctly to this tool.",
1918
2006
  );
1919
2007
  }
2008
+ // Do not fall back to methods that can produce incomplete results when failOnError is set
2009
+ options.failOnError && process.exit(1);
1920
2010
  console.log(
1921
- "2. Private dependencies cannot be downloaded: Check if any additional arguments must be passed to maven and set them via MVN_ARGS environment variable.",
2011
+ "\nFalling back to parsing pom.xml files. Only direct dependencies would get included!",
1922
2012
  );
1923
- console.log(
1924
- "3. Check if all required environment variables including any maven profile arguments are passed correctly to this tool.",
2013
+ thoughtLog(
2014
+ "**MAVEN**: There appear to be build errors, so the SBOM will be incomplete.",
1925
2015
  );
1926
- }
1927
- // Do not fall back to methods that can produce incomplete results when failOnError is set
1928
- options.failOnError && process.exit(1);
1929
- console.log(
1930
- "\nFalling back to parsing pom.xml files. Only direct dependencies would get included!",
1931
- );
1932
- thoughtLog(
1933
- "**MAVEN**: There appear to be build errors, so the SBOM will be incomplete.",
1934
- );
1935
- const dlist = parsePom(f);
1936
- if (dlist?.length) {
1937
- pkgList = pkgList.concat(dlist);
1938
- }
1939
- } else {
1940
- if (safeExistsSync(tempMvnTree)) {
1941
- const mvnTreeString = readFileSync(tempMvnTree, {
1942
- encoding: "utf-8",
1943
- });
1944
- const parsedList = parseMavenTree(mvnTreeString, f);
1945
- const dlist = parsedList.pkgList;
1946
- const tmpParentComponent = dlist.splice(0, 1)[0];
1947
- tmpParentComponent.type = "application";
1948
- if (dlist?.length) {
2016
+ const pomMap = parsePom(f);
2017
+ const dlist = pomMap?.dependencies || [];
2018
+ if (dlist.length) {
1949
2019
  pkgList = pkgList.concat(dlist);
1950
- if (dlist.length > 1) {
1951
- thoughtLog(`Obtained ${dlist.length} components from maven.`);
1952
- } else {
2020
+ }
2021
+ } else {
2022
+ const treeFile = safeExistsSync(tempMvnTreeText)
2023
+ ? tempMvnTreeText
2024
+ : tempMvnTree;
2025
+ if (safeExistsSync(treeFile)) {
2026
+ const mvnTreeString = readFileSync(treeFile, {
2027
+ encoding: "utf-8",
2028
+ });
2029
+ const parsedList = treeFile.endsWith(".json")
2030
+ ? parseMavenTreeJson(mvnTreeString, f)
2031
+ : parseMavenTree(mvnTreeString, f);
2032
+ const dlist = parsedList.pkgList || [];
2033
+ const tmpParentComponent = dlist.splice(0, 1)[0];
2034
+ if (tmpParentComponent) {
2035
+ tmpParentComponent.type = "application";
2036
+ }
2037
+ if (dlist.length) {
2038
+ pkgList = pkgList.concat(dlist);
2039
+ if (dlist.length > 1) {
2040
+ thoughtLog(`Obtained ${dlist.length} components from maven.`);
2041
+ } else {
2042
+ thoughtLog(
2043
+ `"Received very few components from the maven dependency tree command for ${basePath}."`,
2044
+ );
2045
+ }
2046
+ }
2047
+ // Retain the parent hierarchy
2048
+ if (!tmpParentComponent) {
1953
2049
  thoughtLog(
1954
- `"Received very few components from the maven dependency tree command for ${basePath}."`,
2050
+ `No parseable components were found after executing '${basename(mavenCmd)}'.`,
1955
2051
  );
2052
+ } else if (!Object.keys(parentComponent).length) {
2053
+ parentComponent = tmpParentComponent;
2054
+ parentComponent.components = [];
2055
+ } else {
2056
+ parentComponent.components.push(tmpParentComponent);
1956
2057
  }
1957
- }
1958
- // Retain the parent hierarchy
1959
- if (!Object.keys(parentComponent).length) {
1960
- parentComponent = tmpParentComponent;
1961
- parentComponent.components = [];
1962
- } else {
1963
- parentComponent.components.push(tmpParentComponent);
1964
- }
1965
- if (parsedList?.dependenciesList?.length) {
1966
- dependencies = mergeDependencies(
1967
- dependencies,
1968
- parsedList.dependenciesList,
1969
- tmpParentComponent,
1970
- );
1971
- } else {
1972
- if (dlist?.length) {
1973
- thoughtLog(
1974
- `Hmm, I didn't find any dependencies after executing '${basename(mavenCmd)}'. However, I did get ${dlist.length} components, which is confusing.`,
2058
+ if (parsedList?.dependenciesList?.length) {
2059
+ dependencies = mergeDependencies(
2060
+ dependencies,
2061
+ parsedList.dependenciesList,
2062
+ tmpParentComponent,
1975
2063
  );
2064
+ } else {
2065
+ if (dlist?.length) {
2066
+ thoughtLog(
2067
+ `Hmm, I didn't find any dependencies after executing '${basename(mavenCmd)}'. However, I did get ${dlist.length} components, which is confusing.`,
2068
+ );
2069
+ }
1976
2070
  }
1977
2071
  }
1978
- if (!DEBUG_MODE) {
1979
- safeUnlinkSync(tempMvnTree);
1980
- }
2072
+ }
2073
+ } finally {
2074
+ if (!DEBUG_MODE && tempDir?.startsWith(tempRoot)) {
2075
+ safeRmSync(tempDir, { recursive: true, force: true });
1981
2076
  }
1982
2077
  }
1983
2078
  }
@@ -3068,7 +3163,7 @@ export async function createNodejsBom(path, options) {
3068
3163
  `Performing babel-based package usage analysis with source code at ${path}`,
3069
3164
  );
3070
3165
  }
3071
- const retData = await findJSImportsExports(path, options.deep);
3166
+ const retData = await findJSImportsExports(path, options);
3072
3167
  allImports = retData.allImports;
3073
3168
  allExports = retData.allExports;
3074
3169
  if (shouldDetectMcpInventory(includedAiInventoryTypes)) {
@@ -3128,11 +3223,23 @@ export async function createNodejsBom(path, options) {
3128
3223
  `${options.multiProject ? "**/" : ""}pnpm-lock.yaml`,
3129
3224
  options,
3130
3225
  );
3131
- const pnpmWorkspaceFiles = getAllFiles(
3226
+ let pnpmWorkspaceFiles = getAllFiles(
3132
3227
  path,
3133
3228
  `${options.multiProject ? "**/" : ""}pnpm-workspace.yaml`,
3134
3229
  options,
3135
3230
  );
3231
+ const rootPnpmLockFile = resolve(path, "pnpm-lock.yaml");
3232
+ const rootPnpmWorkspaceFile = resolve(path, "pnpm-workspace.yaml");
3233
+ if (
3234
+ safeExistsSync(rootPnpmLockFile) &&
3235
+ safeExistsSync(rootPnpmWorkspaceFile) &&
3236
+ pnpmLockFiles.includes(rootPnpmLockFile)
3237
+ ) {
3238
+ pnpmLockFiles = [rootPnpmLockFile];
3239
+ pnpmWorkspaceFiles = [rootPnpmWorkspaceFile];
3240
+ yarnLockFiles = [];
3241
+ pkgLockFiles = [];
3242
+ }
3136
3243
  const minJsFiles = getAllFiles(
3137
3244
  path,
3138
3245
  `${options.multiProject ? "**/" : ""}*min.js`,
@@ -3418,7 +3525,6 @@ export async function createNodejsBom(path, options) {
3418
3525
  const workspaceDirectDeps = {};
3419
3526
  const depsWorkspaceRefs = {};
3420
3527
  let workspaceCatalogs = {};
3421
- let workspaceWarningShown = false;
3422
3528
  const seenPkgJsonFiles = {};
3423
3529
  // Is this a pnpm workspace?
3424
3530
  for (const f of pnpmWorkspaceFiles) {
@@ -3427,16 +3533,26 @@ export async function createNodejsBom(path, options) {
3427
3533
  }
3428
3534
  const workspaceObj = parsePnpmWorkspace(f);
3429
3535
  if (workspaceObj?.packages) {
3536
+ const workspaceBasePath = dirname(f);
3537
+ const workspacePatterns =
3538
+ workspaceObj.packagePatterns || workspaceObj.packages;
3430
3539
  // We need the precise purl for all workspace packages and their direct dependencies
3431
- for (const awp of workspaceObj.packages) {
3432
- const wpkgJsonFiles = getAllFiles(awp, "**/package.json", options);
3540
+ for (const awp of workspacePatterns) {
3541
+ const workspaceExcludes = [...(workspaceObj.excludePackages || [])];
3542
+ const workspaceSearchOptions = {
3543
+ ...options,
3544
+ exclude: [...(options.exclude || []), ...workspaceExcludes],
3545
+ includeNodeModulesDir: false,
3546
+ };
3547
+ const workspacePackagePattern = awp.endsWith("package.json")
3548
+ ? awp
3549
+ : `${awp.replace(/\/+$/, "")}/package.json`;
3550
+ const wpkgJsonFiles = getAllFiles(
3551
+ workspaceBasePath,
3552
+ workspacePackagePattern,
3553
+ workspaceSearchOptions,
3554
+ );
3433
3555
  if (!wpkgJsonFiles?.length) {
3434
- if (!workspaceWarningShown) {
3435
- workspaceWarningShown = true;
3436
- console.warn(
3437
- `Unable to find any package.json files belonging to the workspace '${awp}' referred in ${f}. To improve SBOM precision, run cdxgen from the directory containing the complete source code.`,
3438
- );
3439
- }
3440
3556
  continue;
3441
3557
  }
3442
3558
  for (const apj of wpkgJsonFiles) {
@@ -3444,7 +3560,12 @@ export async function createNodejsBom(path, options) {
3444
3560
  continue;
3445
3561
  }
3446
3562
  seenPkgJsonFiles[apj] = true;
3447
- const pkgData = JSON.parse(readFileSync(apj, "utf-8"));
3563
+ let pkgData;
3564
+ try {
3565
+ pkgData = JSON.parse(readFileSync(apj, "utf-8"));
3566
+ } catch (_err) {
3567
+ continue;
3568
+ }
3448
3569
  if (pkgData?.name) {
3449
3570
  const relativePkgJsonFile = relative(path, apj);
3450
3571
  let workspaceRef = `pkg:npm/${pkgData.name}`;
@@ -3498,16 +3619,11 @@ export async function createNodejsBom(path, options) {
3498
3619
  console.log(
3499
3620
  `${Object.keys(seenPkgJsonFiles).length} package.json files were parsed to identify workspace names. Total number of package.json files: ${pkgJsonFiles.length}`,
3500
3621
  );
3501
- if (Object.keys(seenPkgJsonFiles).length < pkgJsonFiles.length - 1) {
3502
- const seenfilenames = Object.keys(seenPkgJsonFiles);
3503
- console.log(
3504
- "Following files were not parsed:",
3505
- pkgJsonFiles.filter((p) => !seenfilenames.includes(p)),
3506
- );
3507
- console.log(
3508
- "TIP: Check the configuration in pnpm-workspace.yaml to ensure all the required workspaces are included correctly.",
3509
- );
3510
- }
3622
+ }
3623
+ if (!Object.keys(seenPkgJsonFiles).length && pnpmWorkspaceFiles.length) {
3624
+ console.warn(
3625
+ `Unable to find any package.json files belonging to the workspace(s) referred in ${pnpmWorkspaceFiles.join(", ")}. To improve SBOM precision, run cdxgen from the directory containing the complete source code.`,
3626
+ );
3511
3627
  }
3512
3628
  for (const f of pnpmLockFiles) {
3513
3629
  if (DEBUG_MODE) {
@@ -3570,6 +3686,7 @@ export async function createNodejsBom(path, options) {
3570
3686
  workspaceCatalogs,
3571
3687
  workspaceDirectDeps,
3572
3688
  depsWorkspaceRefs,
3689
+ path,
3573
3690
  );
3574
3691
  const dlist = parsedList.pkgList;
3575
3692
  if (dlist?.length) {
@@ -3697,11 +3814,27 @@ export async function createNodejsBom(path, options) {
3697
3814
  });
3698
3815
  }
3699
3816
  if (safeExistsSync(pnpmLock)) {
3700
- const pnpmLockObj = await parsePnpmLock(pnpmLock);
3817
+ const pnpmLockObj = await parsePnpmLock(
3818
+ pnpmLock,
3819
+ null,
3820
+ [],
3821
+ {},
3822
+ {},
3823
+ {},
3824
+ {},
3825
+ path,
3826
+ );
3701
3827
  let pkgList = addWasmComponentsFromImports(
3702
3828
  pnpmLockObj.pkgList,
3703
3829
  allImports,
3704
3830
  );
3831
+ let dependencies = [];
3832
+ if (pnpmLockObj?.dependenciesList?.length) {
3833
+ dependencies = mergeDependencies(
3834
+ dependencies,
3835
+ pnpmLockObj.dependenciesList,
3836
+ );
3837
+ }
3705
3838
  if (allImports && Object.keys(allImports).length) {
3706
3839
  pkgList = await addEvidenceForImports(
3707
3840
  pkgList,
@@ -3710,9 +3843,11 @@ export async function createNodejsBom(path, options) {
3710
3843
  options.deep,
3711
3844
  );
3712
3845
  }
3846
+ pkgList = propagateRequiredScopeFromDependencies(pkgList, dependencies);
3713
3847
  return buildBomNSData(options, pkgList, "npm", {
3714
3848
  allImports,
3715
3849
  allExports,
3850
+ dependencies,
3716
3851
  src: path,
3717
3852
  filename: "pnpm-lock.yaml",
3718
3853
  });
@@ -4013,6 +4148,7 @@ export async function createNodejsBom(path, options) {
4013
4148
  mergeServices([], mcpInventory.services || []),
4014
4149
  aiInventory.services || [],
4015
4150
  );
4151
+ pkgList = propagateRequiredScopeFromDependencies(pkgList, dependencies);
4016
4152
  if (exactAiInventoryType === "mcp") {
4017
4153
  pkgList = trimComponents(filterInventorySubjectsByTypes(pkgList, ["mcp"]));
4018
4154
  dependencies = filterInventoryDependencies(
@@ -8341,6 +8477,13 @@ async function analyzeInstalledExtensionDirs(
8341
8477
  * @returns {Promise<Object>} Promise resolving to BOM object
8342
8478
  */
8343
8479
  export async function createCryptoCertsBom(path, options) {
8480
+ if (!isCycloneDxComponentTypeEnabled("cryptographic-asset", options)) {
8481
+ return {
8482
+ bomJson: {
8483
+ components: [],
8484
+ },
8485
+ };
8486
+ }
8344
8487
  const pkgList = [];
8345
8488
  const certFiles = getAllFiles(
8346
8489
  path,
@@ -8407,7 +8550,7 @@ export async function createCryptoCertsBom(path, options) {
8407
8550
  }
8408
8551
  return {
8409
8552
  bomJson: {
8410
- components: pkgList,
8553
+ components: filterInvalidCryptoComponents(pkgList),
8411
8554
  },
8412
8555
  };
8413
8556
  }
@@ -8511,6 +8654,7 @@ export async function createMultiXBom(pathList, options) {
8511
8654
  } = await getOSPackages(
8512
8655
  options.allLayersExplodedDir,
8513
8656
  options.exportData?.inspectData?.Config,
8657
+ options,
8514
8658
  );
8515
8659
  // TODO: Need to test these with docker-compose type where multiple images could have different values.
8516
8660
  // This is also clearly misusing options, which must become immutable at some point.
@@ -9349,7 +9493,11 @@ export async function createMultiXBom(pathList, options) {
9349
9493
  }
9350
9494
  }
9351
9495
  // Collect any crypto keys
9352
- if (options.specVersion >= 1.6 && options.includeCrypto) {
9496
+ if (
9497
+ options.specVersion >= 1.6 &&
9498
+ options.includeCrypto &&
9499
+ isCycloneDxComponentTypeEnabled("cryptographic-asset", options)
9500
+ ) {
9353
9501
  if (!hasAnyProjectType(["oci"], options, false)) {
9354
9502
  thoughtLog(
9355
9503
  "**CBOM**: Wait, the user wants me to look for cryptographic assets. Let's check thoroughly.",