@cyclonedx/cdxgen 12.1.4 → 12.1.5

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 (35) hide show
  1. package/bin/cdxgen.js +12 -0
  2. package/bin/repl.js +2 -2
  3. package/lib/cli/index.js +158 -69
  4. package/lib/evinser/evinser.js +3 -4
  5. package/lib/evinser/swiftsem.js +1 -1
  6. package/lib/helpers/caxa.js +1 -1
  7. package/lib/helpers/display.js +6 -10
  8. package/lib/helpers/envcontext.js +5 -5
  9. package/lib/helpers/pythonutils.js +296 -0
  10. package/lib/helpers/pythonutils.poku.js +469 -0
  11. package/lib/helpers/utils.js +263 -96
  12. package/lib/helpers/utils.poku.js +84 -1
  13. package/lib/managers/piptree.js +1 -1
  14. package/lib/parsers/npmrc.js +88 -0
  15. package/lib/parsers/npmrc.poku.js +492 -0
  16. package/lib/server/openapi.yaml +0 -9
  17. package/lib/server/server.js +18 -5
  18. package/lib/stages/pregen/env-audit.js +34 -0
  19. package/lib/stages/pregen/env-audit.poku.js +290 -0
  20. package/lib/third-party/arborist/lib/deepest-nesting-target.js +1 -1
  21. package/lib/third-party/arborist/lib/node.js +3 -3
  22. package/lib/third-party/arborist/lib/shrinkwrap.js +1 -1
  23. package/lib/third-party/arborist/lib/tree-check.js +1 -1
  24. package/package.json +3 -3
  25. package/types/lib/cli/index.d.ts.map +1 -1
  26. package/types/lib/evinser/evinser.d.ts.map +1 -1
  27. package/types/lib/helpers/display.d.ts.map +1 -1
  28. package/types/lib/helpers/pythonutils.d.ts +9 -0
  29. package/types/lib/helpers/pythonutils.d.ts.map +1 -0
  30. package/types/lib/helpers/utils.d.ts.map +1 -1
  31. package/types/lib/parsers/npmrc.d.ts +23 -0
  32. package/types/lib/parsers/npmrc.d.ts.map +1 -0
  33. package/types/lib/server/server.d.ts.map +1 -1
  34. package/types/lib/stages/pregen/env-audit.d.ts +2 -0
  35. package/types/lib/stages/pregen/env-audit.d.ts.map +1 -0
package/bin/cdxgen.js CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  } from "../lib/helpers/utils.js";
43
43
  import { validateBom } from "../lib/helpers/validator.js";
44
44
  import { postProcess } from "../lib/stages/postgen/postgen.js";
45
+ import { auditEnvironment } from "../lib/stages/pregen/env-audit.js";
45
46
  import { prepareEnv } from "../lib/stages/pregen/pregen.js";
46
47
 
47
48
  // Support for config files
@@ -840,6 +841,17 @@ const needsBomSigning = ({ generateKeyAndSign }) =>
840
841
  (async () => {
841
842
  // Display the sponsor banner
842
843
  printSponsorBanner(options);
844
+ // Our quest to audit and check the SBOM generation environment to prevent our users from getting exploited
845
+ // during SBOM generation.
846
+ const envWarnings = auditEnvironment();
847
+ if (envWarnings?.length) {
848
+ for (const w of envWarnings) {
849
+ console.log(`SECURE MODE: ${w}`);
850
+ }
851
+ if (isSecureMode) {
852
+ process.exit(1);
853
+ }
854
+ }
843
855
  // Start SBOM server
844
856
  if (options.server) {
845
857
  const serverModule = await import("../lib/server/server.js");
package/bin/repl.js CHANGED
@@ -594,7 +594,7 @@ cdxgenRepl.defineCommand("osinfocategories", {
594
594
  cdxgenRepl.defineCommand("licenses", {
595
595
  help: "visualize license distribution",
596
596
  async action() {
597
- if (!sbom || !sbom.components) {
597
+ if (!sbom?.components) {
598
598
  console.log("⚠ No SBOM loaded.");
599
599
  this.displayPrompt();
600
600
  return;
@@ -649,7 +649,7 @@ cdxgenRepl.defineCommand("inspect", {
649
649
  cdxgenRepl.defineCommand("tagcloud", {
650
650
  help: "generate a text/tag cloud based on component descriptions and tags",
651
651
  action() {
652
- if (!sbom || !sbom.components) {
652
+ if (!sbom?.components) {
653
653
  console.log("⚠ No SBOM loaded.");
654
654
  this.displayPrompt();
655
655
  return;
package/lib/cli/index.js CHANGED
@@ -28,6 +28,7 @@ import { parseCaxaMetadata } from "../helpers/caxa.js";
28
28
  import { collectOSCryptoLibs } from "../helpers/cbomutils.js";
29
29
  import {
30
30
  collectEnvInfo,
31
+ GIT_COMMAND,
31
32
  getBranch,
32
33
  getOriginUrl,
33
34
  gitTreeHashes,
@@ -188,6 +189,7 @@ import {
188
189
  getPkgPathList,
189
190
  parseImageName,
190
191
  } from "../managers/docker.js";
192
+ import { DEFAULT_NPMRC_BLOCKLIST, parseNpmrc } from "../parsers/npmrc.js";
191
193
 
192
194
  const dirName = dirNameStr;
193
195
 
@@ -2879,6 +2881,7 @@ export async function createNodejsBom(path, options) {
2879
2881
  );
2880
2882
  const npmInstallCount =
2881
2883
  Number.parseInt(process.env.NPM_INSTALL_COUNT, 10) || 2;
2884
+ let anyInstallSuccess = false;
2882
2885
  // Automatic npm install logic.
2883
2886
  // Only perform npm install for smaller projects (< 2 package.json) without the correct number of lock files
2884
2887
  if (
@@ -2889,7 +2892,6 @@ export async function createNodejsBom(path, options) {
2889
2892
  pkgJsonFiles?.length <= npmInstallCount &&
2890
2893
  options.installDeps
2891
2894
  ) {
2892
- let anyInstallSuccess = false;
2893
2895
  for (const apkgJson of pkgJsonFiles) {
2894
2896
  let pkgMgr = "npm";
2895
2897
  const supPkgMgrs = ["npm", "yarn", "yarnpkg", "pnpm", "pnpx"];
@@ -2938,17 +2940,50 @@ export async function createNodejsBom(path, options) {
2938
2940
  if (pkgMgr === "pnpm") {
2939
2941
  installArgs.push("--ignore-pnpmfile");
2940
2942
  }
2941
- if (pkgMgr === "npm" && !installArgs.includes("--no-audit")) {
2942
- installArgs.push("--no-audit");
2943
+ if (pkgMgr === "npm") {
2944
+ for (const c of ["--no-audit", "--no-bin-links"]) {
2945
+ if (!installArgs.includes(c)) {
2946
+ installArgs.push(c);
2947
+ }
2948
+ }
2949
+ installArgs.push(`--git=${GIT_COMMAND}`);
2943
2950
  }
2944
2951
  }
2952
+ if (
2953
+ pkgMgr === "npm" &&
2954
+ isSecureMode &&
2955
+ !installArgs.join(" ").includes("--allow-git")
2956
+ ) {
2957
+ console.log(
2958
+ "Consider passing '--allow-git=none' via the environment variable NPM_INSTALL_ARGS to prevent any git dependencies from being fetched and installed via npm.",
2959
+ );
2960
+ }
2945
2961
  const basePath = dirname(apkgJson);
2962
+ let npmrcData;
2963
+ if (safeExistsSync(join(basePath, ".npmrc"))) {
2964
+ thoughtLog(
2965
+ "Wait, there is a .npmrc file here! I'm going to check if it has anything malicious.",
2966
+ );
2967
+ npmrcData = readFileSync(join(basePath, ".npmrc"), "utf-8");
2968
+ const npmrcObj = parseNpmrc(npmrcData);
2969
+ for (const [key, value] of Object.entries(npmrcObj)) {
2970
+ const baseKey = key.replace(/^(?:\/\/[^/]+\/|@[^:]+:)/, "");
2971
+ if (
2972
+ DEFAULT_NPMRC_BLOCKLIST.has(baseKey) ||
2973
+ DEFAULT_NPMRC_BLOCKLIST.has(key)
2974
+ ) {
2975
+ console.warn(
2976
+ `\x1b[1;35mSECURE MODE: Dangerous configuration ${key}=${value} detected in .npmrc! Verify if this is a trusted project. Remove this setting or any other problematic configurations to proceed.\x1b[0m`,
2977
+ );
2978
+ process.exit(1);
2979
+ }
2980
+ }
2981
+ }
2946
2982
  // juice-shop mode
2947
2983
  // Projects such as juice-shop prevent lockfile creations using .npmrc files
2948
2984
  // Plus, they might require specific npm install args such as --legacy-peer-deps that could lead to strange node_modules structure
2949
2985
  // To keep life simple, let's look for any .npmrc file that has package-lock=false to toggle before npm install
2950
- if (pkgMgr === "npm" && safeExistsSync(join(basePath, ".npmrc"))) {
2951
- const npmrcData = readFileSync(join(basePath, ".npmrc"));
2986
+ if (pkgMgr === "npm") {
2952
2987
  if (
2953
2988
  npmrcData?.includes("package-lock=false") &&
2954
2989
  !installArgs.includes("--package-lock")
@@ -2966,6 +3001,9 @@ export async function createNodejsBom(path, options) {
2966
3001
  `**PACKAGE MANAGER**: Let's run the '${pkgMgr}' command with the arguments '${installArgs.join(" ")}' to generate the needed lock files.`,
2967
3002
  );
2968
3003
  }
3004
+ console.warn(
3005
+ "\x1b[1;35mNotice: Generating an SBOM without a lockfile is risky and non-deterministic. Consider generating and committing the lockfile to your repository to ensure reproducible builds and SBOMs.\x1b[0m",
3006
+ );
2969
3007
  console.log(
2970
3008
  `Executing '${pkgMgr} ${installArgs.join(" ")}' in`,
2971
3009
  basePath,
@@ -3239,6 +3277,11 @@ export async function createNodejsBom(path, options) {
3239
3277
  pkgLockFiles?.length &&
3240
3278
  isPackageManagerAllowed("npm", ["pnpm", "yarn"], options)
3241
3279
  ) {
3280
+ if (anyInstallSuccess) {
3281
+ thoughtLog(
3282
+ `I have ${pkgLockFiles.length} package-lock.json file(s) now after a successful npm install.`,
3283
+ );
3284
+ }
3242
3285
  manifestFiles = manifestFiles.concat(pkgLockFiles);
3243
3286
  for (const f of pkgLockFiles) {
3244
3287
  if (DEBUG_MODE) {
@@ -3848,7 +3891,6 @@ export async function createPythonBom(path, options) {
3848
3891
  console.log(`Parsing ${f}`);
3849
3892
  }
3850
3893
  let retMap = await parsePyLockData(lockData, f);
3851
- // Should we exit for workspace errors
3852
3894
  if (retMap?.workspaceWarningShown) {
3853
3895
  options.failOnError && process.exit(1);
3854
3896
  }
@@ -3856,7 +3898,6 @@ export async function createPythonBom(path, options) {
3856
3898
  pkgList = pkgList.concat(retMap.pkgList);
3857
3899
  pkgList = trimComponents(pkgList);
3858
3900
  }
3859
- // Retain the parent hierarchy
3860
3901
  if (retMap?.parentComponent?.components?.length) {
3861
3902
  if (!parentComponent.components) {
3862
3903
  parentComponent.components = [];
@@ -3872,36 +3913,81 @@ export async function createPythonBom(path, options) {
3872
3913
  parentComponent,
3873
3914
  );
3874
3915
  }
3875
- // Retrieve the tree using virtualenv in deep mode and as a fallback
3876
- // This is a slow operation
3877
3916
  if ((options.deep || !dependencies.length) && !f.endsWith("uv.lock")) {
3878
- retMap = await getPipFrozenTree(basePath, f, tempDir, parentComponent);
3879
- if (retMap.pkgList?.length) {
3880
- pkgList = pkgList.concat(retMap.pkgList);
3881
- }
3882
- if (retMap.formulationList?.length) {
3883
- formulationList = formulationList.concat(retMap.formulationList);
3884
- }
3885
- if (retMap.dependenciesList) {
3886
- dependencies = mergeDependencies(
3887
- dependencies,
3888
- retMap.dependenciesList,
3917
+ if (options.installDeps) {
3918
+ retMap = await getPipFrozenTree(
3919
+ basePath,
3920
+ f,
3921
+ tempDir,
3889
3922
  parentComponent,
3890
3923
  );
3924
+ if (retMap.pkgList?.length) pkgList = pkgList.concat(retMap.pkgList);
3925
+ if (retMap.formulationList?.length)
3926
+ formulationList = formulationList.concat(retMap.formulationList);
3927
+ if (retMap.dependenciesList)
3928
+ dependencies = mergeDependencies(
3929
+ dependencies,
3930
+ retMap.dependenciesList,
3931
+ parentComponent,
3932
+ );
3933
+ if (retMap.rootList) {
3934
+ const parentDependsOn = new Set();
3935
+ for (const p of retMap.rootList)
3936
+ parentDependsOn.add(
3937
+ `pkg:pypi/${p.name.toLowerCase()}@${p.version}`,
3938
+ );
3939
+ dependencies.splice(0, 0, {
3940
+ ref: parentComponent["bom-ref"],
3941
+ dependsOn: [...parentDependsOn].sort(),
3942
+ });
3943
+ }
3944
+ } else {
3945
+ let exportedReqs = "";
3946
+ if (f.endsWith("poetry.lock")) {
3947
+ thoughtLog(
3948
+ "Using poetry export as a safe, static alternative to pip install.",
3949
+ );
3950
+ const expCmd = safeSpawnSync(
3951
+ "poetry",
3952
+ ["export", "-f", "requirements.txt"],
3953
+ { cwd: basePath, shell: false },
3954
+ );
3955
+ if (expCmd.status === 0 && expCmd.stdout)
3956
+ exportedReqs = expCmd.stdout.toString();
3957
+ } else if (f.endsWith("pdm.lock")) {
3958
+ thoughtLog(
3959
+ "Using pdm export as a safe, static alternative to pip install.",
3960
+ );
3961
+ const expCmd = safeSpawnSync(
3962
+ "pdm",
3963
+ ["export", "-f", "requirements"],
3964
+ { cwd: basePath, shell: false },
3965
+ );
3966
+ if (expCmd.status === 0 && expCmd.stdout)
3967
+ exportedReqs = expCmd.stdout.toString();
3968
+ }
3969
+ if (exportedReqs) {
3970
+ const tmpReqFile = join(
3971
+ tempDir,
3972
+ `exported-${basename(basePath)}-reqs.txt`,
3973
+ );
3974
+ writeFileSync(tmpReqFile, exportedReqs);
3975
+ const dlist = await parseReqFile(tmpReqFile, false);
3976
+ if (dlist?.length) {
3977
+ pkgList = pkgList.concat(dlist);
3978
+ const parentDependsOn = new Set();
3979
+ for (const p of dlist)
3980
+ parentDependsOn.add(
3981
+ `pkg:pypi/${p.name.toLowerCase()}@${p.version}`,
3982
+ );
3983
+ dependencies.splice(0, 0, {
3984
+ ref: parentComponent["bom-ref"],
3985
+ dependsOn: [...parentDependsOn].sort(),
3986
+ });
3987
+ }
3988
+ }
3891
3989
  }
3892
3990
  }
3893
- if (retMap.rootList) {
3894
- const parentDependsOn = new Set();
3895
- // Complete the dependency tree by making parent component depend on the first level
3896
- for (const p of retMap.rootList) {
3897
- parentDependsOn.add(`pkg:pypi/${p.name.toLowerCase()}@${p.version}`);
3898
- }
3899
- const pdependencies = {
3900
- ref: parentComponent["bom-ref"],
3901
- dependsOn: [...parentDependsOn].sort(),
3902
- };
3903
- dependencies.splice(0, 0, pdependencies);
3904
- }
3905
3991
  }
3906
3992
  options.parentComponent = parentComponent;
3907
3993
  } // poetryMode
@@ -3919,7 +4005,7 @@ export async function createPythonBom(path, options) {
3919
4005
  for (const wf of whlFiles) {
3920
4006
  const mData = await readZipEntry(wf, "METADATA");
3921
4007
  if (mData) {
3922
- const dlist = parseBdistMetadata(undefined, mData);
4008
+ const dlist = parseBdistMetadata(join(wf, "METADATA"), mData);
3923
4009
  if (dlist?.length) {
3924
4010
  pkgList = pkgList.concat(dlist);
3925
4011
  }
@@ -4001,29 +4087,29 @@ export async function createPythonBom(path, options) {
4001
4087
  for (const f of reqFiles) {
4002
4088
  const basePath = dirname(f);
4003
4089
  if (options.installDeps) {
4004
- const pkgMap = await getPipFrozenTree(
4090
+ const rpkgMap = await getPipFrozenTree(
4005
4091
  basePath,
4006
4092
  f,
4007
4093
  tempDir,
4008
4094
  parentComponent,
4009
4095
  );
4010
- if (pkgMap.pkgList?.length) {
4011
- pkgList = pkgList.concat(pkgMap.pkgList);
4096
+ if (rpkgMap.pkgList?.length) {
4097
+ pkgList = pkgList.concat(rpkgMap.pkgList);
4012
4098
  pkgList = trimComponents(pkgList);
4013
4099
  }
4014
- if (pkgMap.formulationList?.length) {
4015
- formulationList = formulationList.concat(pkgMap.formulationList);
4100
+ if (rpkgMap.formulationList?.length) {
4101
+ formulationList = formulationList.concat(rpkgMap.formulationList);
4016
4102
  formulationList = trimComponents(formulationList);
4017
4103
  }
4018
- if (pkgMap.dependenciesList) {
4104
+ if (rpkgMap.dependenciesList) {
4019
4105
  dependencies = mergeDependencies(
4020
4106
  dependencies,
4021
- pkgMap.dependenciesList,
4107
+ rpkgMap.dependenciesList,
4022
4108
  parentComponent,
4023
4109
  );
4024
4110
  }
4025
4111
  // Add the root packages from this file to the parent's dependencies
4026
- for (const p of pkgMap.rootList) {
4112
+ for (const p of rpkgMap.rootList) {
4027
4113
  if (
4028
4114
  parentComponent &&
4029
4115
  p.name === parentComponent.name &&
@@ -4046,12 +4132,42 @@ export async function createPythonBom(path, options) {
4046
4132
  parentComponent,
4047
4133
  );
4048
4134
  }
4049
-
4135
+ if (pkgMap) {
4136
+ // Complete the dependency tree by making parent component depend on the first level
4137
+ for (const p of pkgMap.rootList) {
4138
+ if (
4139
+ parentComponent &&
4140
+ p.name === parentComponent.name &&
4141
+ (p.version === parentComponent.version || p.version === "latest")
4142
+ ) {
4143
+ continue;
4144
+ }
4145
+ parentDependsOn.add(`pkg:pypi/${p.name.toLowerCase()}@${p.version}`);
4146
+ }
4147
+ if (pkgMap?.pkgList?.length) {
4148
+ pkgList = pkgList.concat(pkgMap.pkgList);
4149
+ }
4150
+ if (pkgMap?.formulationList?.length) {
4151
+ formulationList = formulationList.concat(pkgMap.formulationList);
4152
+ }
4153
+ if (pkgMap?.dependenciesList) {
4154
+ dependencies = mergeDependencies(
4155
+ dependencies,
4156
+ pkgMap.dependenciesList,
4157
+ parentComponent,
4158
+ );
4159
+ }
4160
+ }
4050
4161
  // ATOM parsedeps block
4051
4162
  // Atom parsedeps slices can be used to identify packages that are not declared in manifests
4052
4163
  // Since it is a slow operation, we only use it as a fallback or in deep mode
4053
4164
  // This change was made in 10.9.2 release onwards
4054
4165
  if (options.deep || !pkgList.length) {
4166
+ if (!pkgList.length) {
4167
+ thoughtLog(
4168
+ "I couldn't find any components yet. Let's try static analysis with atom parsedeps command.",
4169
+ );
4170
+ }
4055
4171
  const retMap = await getPyModules(path, pkgList, options);
4056
4172
  // We need to patch the existing package list to add ImportedModules for evinse to work
4057
4173
  if (retMap.modList?.length) {
@@ -4100,32 +4216,6 @@ export async function createPythonBom(path, options) {
4100
4216
  }
4101
4217
  }
4102
4218
  // ATOM parsedeps block
4103
- if (pkgMap) {
4104
- // Complete the dependency tree by making parent component depend on the first level
4105
- for (const p of pkgMap.rootList) {
4106
- if (
4107
- parentComponent &&
4108
- p.name === parentComponent.name &&
4109
- (p.version === parentComponent.version || p.version === "latest")
4110
- ) {
4111
- continue;
4112
- }
4113
- parentDependsOn.add(`pkg:pypi/${p.name.toLowerCase()}@${p.version}`);
4114
- }
4115
- if (pkgMap?.pkgList?.length) {
4116
- pkgList = pkgList.concat(pkgMap.pkgList);
4117
- }
4118
- if (pkgMap?.formulationList?.length) {
4119
- formulationList = formulationList.concat(pkgMap.formulationList);
4120
- }
4121
- if (pkgMap?.dependenciesList) {
4122
- dependencies = mergeDependencies(
4123
- dependencies,
4124
- pkgMap.dependenciesList,
4125
- parentComponent,
4126
- );
4127
- }
4128
- }
4129
4219
  let parentPresent = false;
4130
4220
  for (const d of dependencies) {
4131
4221
  if (d.ref === parentComponent["bom-ref"]) {
@@ -4144,7 +4234,6 @@ export async function createPythonBom(path, options) {
4144
4234
  }
4145
4235
  }
4146
4236
  }
4147
-
4148
4237
  // Final fallback is to manually parse setup.py if we still
4149
4238
  // have an empty list
4150
4239
  if (!pkgList.length && setupPyMode) {
@@ -407,7 +407,7 @@ export function initFromSbom(components, language) {
407
407
  const purlLocationMap = {};
408
408
  const purlImportsMap = {};
409
409
  for (const comp of components) {
410
- if (!comp || !comp.evidence) {
410
+ if (!comp?.evidence) {
411
411
  continue;
412
412
  }
413
413
  if (["php", "ruby"].includes(language)) {
@@ -648,8 +648,7 @@ export async function parseObjectSlices(
648
648
  ]) {
649
649
  // Skip the library code typically without filename
650
650
  if (
651
- !slice.fileName ||
652
- !slice.fileName.trim().length ||
651
+ !slice.fileName?.trim().length ||
653
652
  slice.fileName === "<empty>" ||
654
653
  slice.fileName === "<unknown>"
655
654
  ) {
@@ -1807,7 +1806,7 @@ export function collectReachableFrames(_language, reachablesSlice) {
1807
1806
  * @returns
1808
1807
  */
1809
1808
  export function framePicker(dfFrames) {
1810
- if (!dfFrames || !dfFrames.length) {
1809
+ if (!dfFrames?.length) {
1811
1810
  return undefined;
1812
1811
  }
1813
1812
  let aframe = dfFrames[0];
@@ -530,7 +530,7 @@ export function moduleInfo(moduleName, compilerArgs) {
530
530
  * @returns {Object|undefined} Parsed classes, protocols, enums and their functions
531
531
  */
532
532
  export function parseModuleInfo(moduleInfoJson) {
533
- if (!moduleInfoJson || !moduleInfoJson["key.annotations"]) {
533
+ if (!moduleInfoJson?.["key.annotations"]) {
534
534
  return undefined;
535
535
  }
536
536
  const classes = new Set();
@@ -9,7 +9,7 @@ export async function parseCaxaMetadata(mfile) {
9
9
  } catch (_e) {
10
10
  return {};
11
11
  }
12
- if (!mdata || !mdata.components) {
12
+ if (!mdata?.components) {
13
13
  return {};
14
14
  }
15
15
  const { parentComponent } = mdata;
@@ -24,7 +24,7 @@ export function printTable(
24
24
  filterTypes = undefined,
25
25
  highlight = undefined,
26
26
  ) {
27
- if (!bomJson || !bomJson.components) {
27
+ if (!bomJson?.components) {
28
28
  return;
29
29
  }
30
30
  if (
@@ -120,7 +120,7 @@ export function printOSTable(bomJson) {
120
120
  }
121
121
  export function printServices(bomJson) {
122
122
  const data = [["Name", "Endpoints", "Authenticated", "X Trust Boundary"]];
123
- if (!bomJson || !bomJson.services) {
123
+ if (!bomJson?.services) {
124
124
  return;
125
125
  }
126
126
  for (const aservice of bomJson.services) {
@@ -144,7 +144,7 @@ export function printServices(bomJson) {
144
144
 
145
145
  export function printFormulation(bomJson) {
146
146
  const data = [["Tyoe", "Name", "Version"]];
147
- if (!bomJson || !bomJson.formulation) {
147
+ if (!bomJson?.formulation) {
148
148
  return;
149
149
  }
150
150
  for (const aform of bomJson.formulation) {
@@ -179,7 +179,7 @@ const locationComparator = (a, b) => {
179
179
  };
180
180
 
181
181
  export function printOccurrences(bomJson) {
182
- if (!bomJson || !bomJson.components) {
182
+ if (!bomJson?.components) {
183
183
  return;
184
184
  }
185
185
  const data = ["Group", "Name", "Version", "Occurrences"];
@@ -219,15 +219,11 @@ export function printOccurrences(bomJson) {
219
219
 
220
220
  export function printCallStack(bomJson) {
221
221
  const data = [["Group", "Name", "Version", "Call Stack"]];
222
- if (!bomJson || !bomJson.components) {
222
+ if (!bomJson?.components) {
223
223
  return;
224
224
  }
225
225
  for (const comp of bomJson.components) {
226
- if (
227
- !comp.evidence ||
228
- !comp.evidence.callstack ||
229
- !comp.evidence.callstack.frames
230
- ) {
226
+ if (!comp.evidence?.callstack?.frames) {
231
227
  continue;
232
228
  }
233
229
  const frames = Array.from(
@@ -30,13 +30,13 @@ export const GIT_COMMAND = process.env.GIT_CMD || "git";
30
30
  // sdkman tool aliases
31
31
  export const SDKMAN_JAVA_TOOL_ALIASES = {
32
32
  java8: process.env.JAVA8_TOOL || "8.0.452-amzn", // Temurin no longer offers java8 :(
33
- java11: process.env.JAVA11_TOOL || "11.0.29-tem",
34
- java17: process.env.JAVA17_TOOL || "17.0.17-tem",
35
- java21: process.env.JAVA21_TOOL || "21.0.9-tem",
33
+ java11: process.env.JAVA11_TOOL || "11.0.30-tem",
34
+ java17: process.env.JAVA17_TOOL || "17.0.18-tem",
35
+ java21: process.env.JAVA21_TOOL || "21.0.10-tem",
36
36
  java22: process.env.JAVA22_TOOL || "22.0.2-tem",
37
37
  java23: process.env.JAVA23_TOOL || "23.0.2-tem",
38
38
  java24: process.env.JAVA24_TOOL || "24.0.2-tem",
39
- java25: process.env.JAVA25_TOOL || "25.0.1-tem",
39
+ java25: process.env.JAVA25_TOOL || "25.0.2-tem",
40
40
  };
41
41
 
42
42
  /**
@@ -206,7 +206,7 @@ export function collectPythonInfo(dir) {
206
206
  ]);
207
207
  const moduleDesc =
208
208
  getCommandOutput(getPythonCommand(), dir, [
209
- "-S",
209
+ "-I",
210
210
  "-m",
211
211
  "pip",
212
212
  "--version",