@cyclonedx/cdxgen 9.11.5 → 10.0.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/utils.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { globSync } from "glob";
2
2
  import { homedir, platform, tmpdir } from "node:os";
3
+ import process from "node:process";
4
+ import { Buffer } from "node:buffer";
3
5
  import {
4
6
  basename,
5
7
  delimiter as _delimiter,
@@ -27,7 +29,7 @@ import got from "got";
27
29
  import Arborist from "@npmcli/arborist";
28
30
  import path from "node:path";
29
31
  import { xml2js } from "xml-js";
30
- import { fileURLToPath } from "node:url";
32
+ import { fileURLToPath, URL } from "node:url";
31
33
  import { load } from "cheerio";
32
34
  import { load as _load } from "js-yaml";
33
35
  import { spawnSync } from "node:child_process";
@@ -51,9 +53,9 @@ let url = import.meta.url;
51
53
  if (!url.startsWith("file://")) {
52
54
  url = new URL(`file://${import.meta.url}`).toString();
53
55
  }
54
- const dirNameStr = import.meta ? dirname(fileURLToPath(url)) : __dirname;
55
- const isWin = platform() === "win32";
56
- const isMac = platform() === "darwin";
56
+ export const dirNameStr = import.meta ? dirname(fileURLToPath(url)) : __dirname;
57
+ export const isWin = platform() === "win32";
58
+ export const isMac = platform() === "darwin";
57
59
  export let ATOM_DB = join(homedir(), ".local", "share", ".atomdb");
58
60
  if (isWin) {
59
61
  ATOM_DB = join(homedir(), "AppData", "Local", ".atomdb");
@@ -62,34 +64,36 @@ if (isWin) {
62
64
  }
63
65
 
64
66
  const licenseMapping = JSON.parse(
65
- readFileSync(join(dirNameStr, "data", "lic-mapping.json"))
67
+ readFileSync(join(dirNameStr, "data", "lic-mapping.json"), "utf-8")
66
68
  );
67
69
  const vendorAliases = JSON.parse(
68
- readFileSync(join(dirNameStr, "data", "vendor-alias.json"))
70
+ readFileSync(join(dirNameStr, "data", "vendor-alias.json"), "utf-8")
69
71
  );
70
72
  const spdxLicenses = JSON.parse(
71
- readFileSync(join(dirNameStr, "data", "spdx-licenses.json"))
73
+ readFileSync(join(dirNameStr, "data", "spdx-licenses.json"), "utf-8")
72
74
  );
73
75
  const knownLicenses = JSON.parse(
74
- readFileSync(join(dirNameStr, "data", "known-licenses.json"))
76
+ readFileSync(join(dirNameStr, "data", "known-licenses.json"), "utf-8")
75
77
  );
76
78
  const mesonWrapDB = JSON.parse(
77
- readFileSync(join(dirNameStr, "data", "wrapdb-releases.json"))
79
+ readFileSync(join(dirNameStr, "data", "wrapdb-releases.json"), "utf-8")
78
80
  );
79
81
  export const frameworksList = JSON.parse(
80
- readFileSync(join(dirNameStr, "data", "frameworks-list.json"))
82
+ readFileSync(join(dirNameStr, "data", "frameworks-list.json"), "utf-8")
83
+ );
84
+ const selfPJson = JSON.parse(
85
+ readFileSync(join(dirNameStr, "package.json"), "utf-8")
81
86
  );
82
- const selfPJson = JSON.parse(readFileSync(join(dirNameStr, "package.json")));
83
87
  const _version = selfPJson.version;
84
88
 
85
89
  // Refer to contrib/py-modules.py for a script to generate this list
86
90
  // The script needs to be used once every few months to update this list
87
91
  const PYTHON_STD_MODULES = JSON.parse(
88
- readFileSync(join(dirNameStr, "data", "python-stdlib.json"))
92
+ readFileSync(join(dirNameStr, "data", "python-stdlib.json"), "utf-8")
89
93
  );
90
94
  // Mapping between modules and package names
91
95
  const PYPI_MODULE_PACKAGE_MAPPING = JSON.parse(
92
- readFileSync(join(dirNameStr, "data", "pypi-pkg-aliases.json"))
96
+ readFileSync(join(dirNameStr, "data", "pypi-pkg-aliases.json"), "utf-8")
93
97
  );
94
98
 
95
99
  // Debug mode flag
@@ -122,14 +126,14 @@ export const FETCH_LICENSE =
122
126
  process.env.FETCH_LICENSE &&
123
127
  ["true", "1"].includes(process.env.FETCH_LICENSE);
124
128
 
125
- // Wether search.maven.org will be used to identify jars without maven metadata; default, if unset shall be 'true'
129
+ // Whether search.maven.org will be used to identify jars without maven metadata; default, if unset shall be 'true'
126
130
  export const SEARCH_MAVEN_ORG =
127
131
  !process.env.SEARCH_MAVEN_ORG ||
128
132
  ["true", "1"].includes(process.env.SEARCH_MAVEN_ORG);
129
133
 
130
134
  // circuit breaker for search maven.org
131
135
  let search_maven_org_errors = 0;
132
- const MAX_SEARCH_MAVEN_ORG_ERRORS = 5;
136
+ const MAX_SEARCH_MAVEN_ORG_ERRORS = 1;
133
137
 
134
138
  // circuit breaker for get repo license
135
139
  let get_repo_license_errors = 0;
@@ -137,12 +141,70 @@ const MAX_GET_REPO_LICENSE_ERRORS = 5;
137
141
 
138
142
  const MAX_LICENSE_ID_LENGTH = 100;
139
143
 
140
- let PYTHON_CMD = "python";
144
+ export let JAVA_CMD = "java";
145
+ if (process.env.JAVA_CMD) {
146
+ JAVA_CMD = process.env.JAVA_CMD;
147
+ } else if (
148
+ process.env.JAVA_HOME &&
149
+ existsSync(process.env.JAVA_HOME) &&
150
+ existsSync(join(process.env.JAVA_HOME, "bin", "java"))
151
+ ) {
152
+ JAVA_CMD = join(process.env.JAVA_HOME, "bin", "java");
153
+ }
154
+ export let PYTHON_CMD = "python";
141
155
  if (process.env.PYTHON_CMD) {
142
156
  PYTHON_CMD = process.env.PYTHON_CMD;
143
157
  } else if (process.env.CONDA_PYTHON_EXE) {
144
158
  PYTHON_CMD = process.env.CONDA_PYTHON_EXE;
145
159
  }
160
+ export let DOTNET_CMD = "dotnet";
161
+ if (process.env.DOTNET_CMD) {
162
+ DOTNET_CMD = process.env.DOTNET_CMD;
163
+ }
164
+ export let NODE_CMD = "node";
165
+ if (process.env.NODE_CMD) {
166
+ NODE_CMD = process.env.NODE_CMD;
167
+ }
168
+ export let NPM_CMD = "npm";
169
+ if (process.env.NPM_CMD) {
170
+ NPM_CMD = process.env.NPM_CMD;
171
+ }
172
+ export let YARN_CMD = "yarn";
173
+ if (process.env.YARN_CMD) {
174
+ YARN_CMD = process.env.YARN_CMD;
175
+ }
176
+ export let GCC_CMD = "gcc";
177
+ if (process.env.GCC_CMD) {
178
+ GCC_CMD = process.env.GCC_CMD;
179
+ }
180
+ export let RUSTC_CMD = "rustc";
181
+ if (process.env.RUSTC_CMD) {
182
+ RUSTC_CMD = process.env.RUSTC_CMD;
183
+ }
184
+ export let GO_CMD = "go";
185
+ if (process.env.GO_CMD) {
186
+ GO_CMD = process.env.GO_CMD;
187
+ }
188
+ export let CARGO_CMD = "cargo";
189
+ if (process.env.CARGO_CMD) {
190
+ CARGO_CMD = process.env.CARGO_CMD;
191
+ }
192
+
193
+ // Clojure CLI
194
+ export let CLJ_CMD = "clj";
195
+ if (process.env.CLJ_CMD) {
196
+ CLJ_CMD = process.env.CLJ_CMD;
197
+ }
198
+
199
+ export let LEIN_CMD = "lein";
200
+ if (process.env.LEIN_CMD) {
201
+ LEIN_CMD = process.env.LEIN_CMD;
202
+ }
203
+
204
+ export let SWIFT_CMD = "swift";
205
+ if (process.env.SWIFT_CMD) {
206
+ SWIFT_CMD = process.env.SWIFT_CMD;
207
+ }
146
208
 
147
209
  // Custom user-agent for cdxgen
148
210
  export const cdxgenAgent = got.extend({
@@ -181,7 +243,7 @@ export const getAllFiles = function (dirPath, pattern, options = {}) {
181
243
  *
182
244
  * @param {string} dirPath Root directory for search
183
245
  * @param {string} pattern Glob pattern (eg: *.gradle)
184
- * @param {array} ignoreList Directory patterns to ignore
246
+ * @param {Array} ignoreList Directory patterns to ignore
185
247
  */
186
248
  export const getAllFilesWithIgnore = function (dirPath, pattern, ignoreList) {
187
249
  try {
@@ -190,7 +252,6 @@ export const getAllFilesWithIgnore = function (dirPath, pattern, ignoreList) {
190
252
  absolute: true,
191
253
  nocase: true,
192
254
  nodir: true,
193
- strict: true,
194
255
  dot: pattern.startsWith(".") ? true : false,
195
256
  follow: false,
196
257
  ignore: ignoreList
@@ -213,7 +274,7 @@ const toBase64 = (hexString) => {
213
274
  * and url of the license object, otherwise, set the 'name' of the license
214
275
  * object.
215
276
  */
216
- export function getLicenses(pkg, format = "xml") {
277
+ export function getLicenses(pkg) {
217
278
  let license = pkg.license && (pkg.license.type || pkg.license);
218
279
  if (license) {
219
280
  if (!Array.isArray(license)) {
@@ -231,7 +292,7 @@ export function getLicenses(pkg, format = "xml") {
231
292
  licenseContent.id = l;
232
293
  licenseContent.url = "https://opensource.org/licenses/" + l;
233
294
  } else if (l.startsWith("http")) {
234
- let knownLicense = getKnownLicense(l, pkg);
295
+ const knownLicense = getKnownLicense(l, pkg);
235
296
  if (knownLicense) {
236
297
  licenseContent.id = knownLicense.id;
237
298
  licenseContent.name = knownLicense.name;
@@ -251,13 +312,13 @@ export function getLicenses(pkg, format = "xml") {
251
312
  return undefined;
252
313
  }
253
314
  if (!licenseContent.id) {
254
- addLicenseText(pkg, l, licenseContent, format);
315
+ addLicenseText(pkg, l, licenseContent);
255
316
  }
256
317
  return licenseContent;
257
318
  })
258
319
  .map((l) => ({ license: l }));
259
320
  } else {
260
- let knownLicense = getKnownLicense(undefined, pkg);
321
+ const knownLicense = getKnownLicense(undefined, pkg);
261
322
  if (knownLicense) {
262
323
  return [{ license: knownLicense }];
263
324
  }
@@ -268,9 +329,9 @@ export function getLicenses(pkg, format = "xml") {
268
329
  /**
269
330
  * Method to retrieve known license by known-licenses.json
270
331
  *
271
- * @param {String} repoUrl Repository url
332
+ * @param {String} licenseUrl Repository url
272
333
  * @param {String} pkg Bom ref
273
- * @return {Object>} Objetct with SPDX license id or license name
334
+ * @return {Object} Objetct with SPDX license id or license name
274
335
  */
275
336
  export const getKnownLicense = function (licenseUrl, pkg) {
276
337
  if (licenseUrl && licenseUrl.includes("opensource.org")) {
@@ -337,7 +398,7 @@ export const getKnownLicense = function (licenseUrl, pkg) {
337
398
  * used naming and content types. If a candidate file is found, add
338
399
  * the text to the license text object and stop.
339
400
  */
340
- export function addLicenseText(pkg, l, licenseContent, format = "xml") {
401
+ export function addLicenseText(pkg, l, licenseContent) {
341
402
  const licenseFilenames = [
342
403
  "LICENSE",
343
404
  "License",
@@ -366,8 +427,7 @@ export function addLicenseText(pkg, l, licenseContent, format = "xml") {
366
427
  if (existsSync(licenseFilepath)) {
367
428
  licenseContent.text = readLicenseText(
368
429
  licenseFilepath,
369
- licenseContentType,
370
- format
430
+ licenseContentType
371
431
  );
372
432
  return;
373
433
  }
@@ -380,26 +440,14 @@ export function addLicenseText(pkg, l, licenseContent, format = "xml") {
380
440
  * Read the file from the given path to the license text object and includes
381
441
  * content-type attribute, if not default. Returns the license text object.
382
442
  */
383
- export function readLicenseText(
384
- licenseFilepath,
385
- licenseContentType,
386
- format = "xml"
387
- ) {
443
+ export function readLicenseText(licenseFilepath, licenseContentType) {
388
444
  const licenseText = readFileSync(licenseFilepath, "utf8");
389
445
  if (licenseText) {
390
- if (format === "xml") {
391
- const licenseContentText = { "#cdata": licenseText };
392
- if (licenseContentType !== "text/plain") {
393
- licenseContentText["@content-type"] = licenseContentType;
394
- }
395
- return licenseContentText;
396
- } else {
397
- const licenseContentText = { content: licenseText };
398
- if (licenseContentType !== "text/plain") {
399
- licenseContentText["contentType"] = licenseContentType;
400
- }
401
- return licenseContentText;
446
+ const licenseContentText = { content: licenseText };
447
+ if (licenseContentType !== "text/plain") {
448
+ licenseContentText["contentType"] = licenseContentType;
402
449
  }
450
+ return licenseContentText;
403
451
  }
404
452
  return null;
405
453
  }
@@ -681,9 +729,9 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
681
729
  pkgList.push(pkg);
682
730
 
683
731
  // retrieve workspace node pkglists
684
- let workspaceDependsOn = [];
732
+ const workspaceDependsOn = [];
685
733
  if (node.fsChildren && node.fsChildren.size > 0) {
686
- for (let workspaceNode of node.fsChildren) {
734
+ for (const workspaceNode of node.fsChildren) {
687
735
  const {
688
736
  pkgList: childPkgList,
689
737
  dependenciesList: childDependenciesList
@@ -708,10 +756,10 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
708
756
 
709
757
  // this handles the case when a node has ["dependencies"] key in a package-lock.json
710
758
  // for a node. We exclude the root node because it's already been handled
711
- let childrenDependsOn = [];
759
+ const childrenDependsOn = [];
712
760
  if (node != rootNode) {
713
761
  for (const child of node.children) {
714
- let childNode = child[1];
762
+ const childNode = child[1];
715
763
  const {
716
764
  pkgList: childPkgList,
717
765
  dependenciesList: childDependenciesList
@@ -749,7 +797,7 @@ export const parsePkgLock = async (pkgLockFile, options = {}) => {
749
797
  // which isn't installed
750
798
  // Bug #795. At times, npm loses the integrity node completely and such packages are getting missed out
751
799
  // To keep things safe, we include these packages.
752
- let edgeToIntegrity = edge.to ? edge.to.integrity : undefined;
800
+ const edgeToIntegrity = edge.to ? edge.to.integrity : undefined;
753
801
  if (!edgeToIntegrity) {
754
802
  // This hack is required to fix the package name
755
803
  targetName = node.name.replace(/-cjs$/, "");
@@ -1562,7 +1610,7 @@ export const parseMinJs = async (minJsFile) => {
1562
1610
  */
1563
1611
  export const parsePom = function (pomFile) {
1564
1612
  const deps = [];
1565
- const xmlData = readFileSync(pomFile);
1613
+ const xmlData = readFileSync(pomFile, "utf-8");
1566
1614
  const project = xml2js(xmlData, {
1567
1615
  compact: true,
1568
1616
  spaces: 4,
@@ -2428,7 +2476,7 @@ export const getMvnMetadata = async function (pkgList, jarNSMapping = {}) {
2428
2476
  });
2429
2477
  }
2430
2478
  }
2431
- let group = p.group || "";
2479
+ const group = p.group || "";
2432
2480
  // If the package already has key metadata skip querying maven
2433
2481
  if (group && p.name && p.version && !FETCH_LICENSE) {
2434
2482
  cdepList.push(p);
@@ -2565,7 +2613,7 @@ export const fetchPomXml = async function ({
2565
2613
  name,
2566
2614
  version
2567
2615
  }) {
2568
- let fullUrl = composePomXmlUrl({ urlPrefix, group, name, version });
2616
+ const fullUrl = composePomXmlUrl({ urlPrefix, group, name, version });
2569
2617
  const res = await cdxgenAgent.get(fullUrl);
2570
2618
  return res.body;
2571
2619
  };
@@ -2654,7 +2702,7 @@ export const guessPypiMatchingVersion = (versionsList, versionSpecifiers) => {
2654
2702
  return -c;
2655
2703
  };
2656
2704
  // Iterate in the "reverse" order
2657
- for (let rv of versionsList.sort(comparator)) {
2705
+ for (const rv of versionsList.sort(comparator)) {
2658
2706
  if (satisfies(coerce(rv), versionSpecifiers, true)) {
2659
2707
  return rv;
2660
2708
  }
@@ -2720,8 +2768,8 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
2720
2768
  if (body.info.classifiers) {
2721
2769
  for (const c of body.info.classifiers) {
2722
2770
  if (c.startsWith("License :: ")) {
2723
- let licenseName = c.split("::").slice(-1)[0].trim();
2724
- let licenseId = findLicenseId(licenseName);
2771
+ const licenseName = c.split("::").slice(-1)[0].trim();
2772
+ const licenseId = findLicenseId(licenseName);
2725
2773
  if (licenseId && !p.license.includes(licenseId)) {
2726
2774
  p.license.push(licenseId);
2727
2775
  }
@@ -2729,7 +2777,7 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
2729
2777
  }
2730
2778
  }
2731
2779
  if (body.info.license) {
2732
- let licenseId = findLicenseId(body.info.license);
2780
+ const licenseId = findLicenseId(body.info.license);
2733
2781
  if (licenseId && !p.license.includes(licenseId)) {
2734
2782
  p.license.push(licenseId);
2735
2783
  }
@@ -2939,7 +2987,7 @@ export const parsePiplockData = async function (lockData) {
2939
2987
  export const parsePyProjectToml = (tomlFile) => {
2940
2988
  // Do we need a toml npm package at some point?
2941
2989
  const tomlData = readFileSync(tomlFile, { encoding: "utf-8" });
2942
- let pkg = {};
2990
+ const pkg = {};
2943
2991
  if (!tomlData) {
2944
2992
  return pkg;
2945
2993
  }
@@ -2947,7 +2995,7 @@ export const parsePyProjectToml = (tomlFile) => {
2947
2995
  l = l.replace("\r", "");
2948
2996
  if (l.indexOf("=") > -1) {
2949
2997
  const tmpA = l.split("=");
2950
- let key = tmpA[0].trim();
2998
+ const key = tmpA[0].trim();
2951
2999
  let value = tmpA[1].trim().replace(/["']/g, "");
2952
3000
  switch (key) {
2953
3001
  case "description":
@@ -3080,7 +3128,7 @@ export const parsePoetrylockData = async function (lockData, lockFile) {
3080
3128
  });
3081
3129
  pkgList = await getPyMetadata(pkgList, false);
3082
3130
  for (const key of Object.keys(depsMap)) {
3083
- let dependsOnList = [];
3131
+ const dependsOnList = [];
3084
3132
  for (const adep of Array.from(depsMap[key])) {
3085
3133
  if (adep.startsWith("pkg:")) {
3086
3134
  dependsOnList.push(adep);
@@ -3426,10 +3474,10 @@ export const toGitHubApiUrl = function (repoUrl, repoMetadata) {
3426
3474
  * @return {Promise<String>} SPDX license id
3427
3475
  */
3428
3476
  export const getRepoLicense = async function (repoUrl, repoMetadata) {
3429
- let apiUrl = toGitHubApiUrl(repoUrl, repoMetadata);
3477
+ const apiUrl = toGitHubApiUrl(repoUrl, repoMetadata);
3430
3478
  // Perform github lookups
3431
3479
  if (apiUrl && get_repo_license_errors < MAX_GET_REPO_LICENSE_ERRORS) {
3432
- let licenseUrl = apiUrl + "/license";
3480
+ const licenseUrl = apiUrl + "/license";
3433
3481
  const headers = {};
3434
3482
  if (process.env.GITHUB_TOKEN) {
3435
3483
  headers["Authorization"] = "Bearer " + process.env.GITHUB_TOKEN;
@@ -3849,7 +3897,7 @@ export const parseGosumData = async function (gosumData) {
3849
3897
  }
3850
3898
  const pkgs = gosumData.split("\n");
3851
3899
  for (const l of pkgs) {
3852
- let m = l.replace("\r", "");
3900
+ const m = l.replace("\r", "");
3853
3901
  // look for lines containing go.mod
3854
3902
  if (m.indexOf("go.mod") > -1) {
3855
3903
  const tmpA = m.split(" ");
@@ -4534,7 +4582,7 @@ export const recurseImageNameLookup = (keyValueObj, pkgList, imgList) => {
4534
4582
  export const parseContainerFile = function (fileContents) {
4535
4583
  const imgList = [];
4536
4584
 
4537
- let buildStageNames = [];
4585
+ const buildStageNames = [];
4538
4586
  for (let line of fileContents.split("\n")) {
4539
4587
  line = line.trim();
4540
4588
 
@@ -5190,7 +5238,7 @@ export const parseNupkg = async function (nupkgFile) {
5190
5238
  return await parseNuspecData(nupkgFile, nuspecData);
5191
5239
  };
5192
5240
 
5193
- export const parseNuspecData = async function (nupkgFile, nuspecData) {
5241
+ export const parseNuspecData = function (nupkgFile, nuspecData) {
5194
5242
  const pkgList = [];
5195
5243
  const pkg = { group: "" };
5196
5244
  let npkg = undefined;
@@ -5243,7 +5291,7 @@ export const parseNuspecData = async function (nupkgFile, nuspecData) {
5243
5291
  return pkgList;
5244
5292
  };
5245
5293
 
5246
- export const parseCsPkgData = async function (pkgData) {
5294
+ export const parseCsPkgData = function (pkgData) {
5247
5295
  const pkgList = [];
5248
5296
  if (!pkgData) {
5249
5297
  return pkgList;
@@ -5270,7 +5318,7 @@ export const parseCsPkgData = async function (pkgData) {
5270
5318
  return pkgList;
5271
5319
  };
5272
5320
 
5273
- export const parseCsProjData = async function (csProjData, projFile) {
5321
+ export const parseCsProjData = function (csProjData, projFile) {
5274
5322
  const pkgList = [];
5275
5323
  if (!csProjData) {
5276
5324
  return pkgList;
@@ -5362,10 +5410,7 @@ export const parseCsProjData = async function (csProjData, projFile) {
5362
5410
  return pkgList;
5363
5411
  };
5364
5412
 
5365
- export const parseCsProjAssetsData = async function (
5366
- csProjData,
5367
- assetsJsonFile
5368
- ) {
5413
+ export const parseCsProjAssetsData = function (csProjData, assetsJsonFile) {
5369
5414
  // extract name, operator, version from .NET package representation
5370
5415
  // like "NLog >= 4.5.0"
5371
5416
  function extractNameOperatorVersion(inputStr) {
@@ -5384,7 +5429,7 @@ export const parseCsProjAssetsData = async function (
5384
5429
  }
5385
5430
 
5386
5431
  const pkgList = [];
5387
- let dependenciesList = [];
5432
+ const dependenciesList = [];
5388
5433
  let rootPkg = {};
5389
5434
  // This tracks the resolved version
5390
5435
  const pkgNameVersionMap = {};
@@ -5411,7 +5456,7 @@ export const parseCsProjAssetsData = async function (
5411
5456
  "bom-ref": decodeURIComponent(purlString)
5412
5457
  };
5413
5458
  pkgList.push(rootPkg);
5414
- let rootPkgDeps = new Set();
5459
+ const rootPkgDeps = new Set();
5415
5460
 
5416
5461
  // create root pkg deps
5417
5462
  if (csProjData.targets && csProjData.projectFileDependencyGroups) {
@@ -5467,7 +5512,7 @@ export const parseCsProjAssetsData = async function (
5467
5512
  null,
5468
5513
  null
5469
5514
  ).toString();
5470
- let pkg = {
5515
+ const pkg = {
5471
5516
  group: "",
5472
5517
  name: name,
5473
5518
  version: version,
@@ -5541,7 +5586,7 @@ export const parseCsProjAssetsData = async function (
5541
5586
  if (!pkgNameVersionMap[p + framework]) {
5542
5587
  continue;
5543
5588
  }
5544
- let dversion = pkgNameVersionMap[p + framework];
5589
+ const dversion = pkgNameVersionMap[p + framework];
5545
5590
  const ipurl = new PackageURL(
5546
5591
  "nuget",
5547
5592
  "",
@@ -5577,7 +5622,7 @@ export const parseCsProjAssetsData = async function (
5577
5622
  };
5578
5623
  };
5579
5624
 
5580
- export const parseCsPkgLockData = async function (csLockData, pkgLockFile) {
5625
+ export const parseCsPkgLockData = function (csLockData, pkgLockFile) {
5581
5626
  const pkgList = [];
5582
5627
  const dependenciesList = [];
5583
5628
  const rootList = [];
@@ -5668,7 +5713,7 @@ export const parseCsPkgLockData = async function (csLockData, pkgLockFile) {
5668
5713
  };
5669
5714
  };
5670
5715
 
5671
- export const parsePaketLockData = async function (paketLockData, pkgLockFile) {
5716
+ export const parsePaketLockData = function (paketLockData, pkgLockFile) {
5672
5717
  const pkgList = [];
5673
5718
  const dependenciesList = [];
5674
5719
  const dependenciesMap = {};
@@ -6528,12 +6573,12 @@ export const parseSwiftResolved = (resolvedFile) => {
6528
6573
  * @param {boolean} cleanup Remove temporary directories
6529
6574
  * @param {boolean} includeCacheDir Include maven and gradle cache directories
6530
6575
  */
6531
- export const collectMvnDependencies = function (
6576
+ export const collectMvnDependencies = async (
6532
6577
  mavenCmd,
6533
6578
  basePath,
6534
6579
  cleanup = true,
6535
6580
  includeCacheDir = false
6536
- ) {
6581
+ ) => {
6537
6582
  let jarNSMapping = {};
6538
6583
  const MAVEN_CACHE_DIR =
6539
6584
  process.env.MAVEN_CACHE_DIR || join(homedir(), ".m2", "repository");
@@ -6574,12 +6619,12 @@ export const collectMvnDependencies = function (
6574
6619
  "3. Ensure the temporary directory is available and has sufficient disk space to copy all the artifacts."
6575
6620
  );
6576
6621
  } else {
6577
- jarNSMapping = collectJarNS(tempDir);
6622
+ jarNSMapping = await collectJarNS(tempDir);
6578
6623
  }
6579
6624
  }
6580
6625
  if (includeCacheDir || basePath === MAVEN_CACHE_DIR) {
6581
6626
  // slow operation
6582
- jarNSMapping = collectJarNS(MAVEN_CACHE_DIR);
6627
+ jarNSMapping = await collectJarNS(MAVEN_CACHE_DIR);
6583
6628
  }
6584
6629
 
6585
6630
  // Clean up
@@ -6589,7 +6634,7 @@ export const collectMvnDependencies = function (
6589
6634
  return jarNSMapping;
6590
6635
  };
6591
6636
 
6592
- export const collectGradleDependencies = (
6637
+ export const collectGradleDependencies = async (
6593
6638
  gradleCmd,
6594
6639
  basePath,
6595
6640
  cleanup = true, // eslint-disable-line no-unused-vars
@@ -6619,7 +6664,7 @@ export const collectGradleDependencies = (
6619
6664
  for (const apom of pomFiles) {
6620
6665
  pomPathMap[basename(apom)] = apom;
6621
6666
  }
6622
- const jarNSMapping = collectJarNS(GRADLE_CACHE_DIR, pomPathMap);
6667
+ const jarNSMapping = await collectJarNS(GRADLE_CACHE_DIR, pomPathMap);
6623
6668
  return jarNSMapping;
6624
6669
  };
6625
6670
 
@@ -6631,7 +6676,7 @@ export const collectGradleDependencies = (
6631
6676
  *
6632
6677
  * @return object containing jar name and class list
6633
6678
  */
6634
- export const collectJarNS = function (jarPath, pomPathMap = {}) {
6679
+ export const collectJarNS = async (jarPath, pomPathMap = {}) => {
6635
6680
  const jarNSMapping = {};
6636
6681
  console.log(
6637
6682
  `About to identify class names for all jars in the path ${jarPath}`
@@ -6646,14 +6691,10 @@ export const collectJarNS = function (jarPath, pomPathMap = {}) {
6646
6691
  "bin"
6647
6692
  )}`;
6648
6693
  }
6649
- let jarCommandAvailable = true;
6650
- // Execute jar tvf to get class names
6694
+ // Parse jar files to get class names
6651
6695
  const jarFiles = getAllFiles(jarPath, "**/*.jar");
6652
6696
  if (jarFiles && jarFiles.length) {
6653
6697
  for (const jf of jarFiles) {
6654
- if (!jarCommandAvailable) {
6655
- break;
6656
- }
6657
6698
  const jarname = jf;
6658
6699
  let pomname =
6659
6700
  pomPathMap[basename(jf).replace(".jar", ".pom")] ||
@@ -6685,10 +6726,10 @@ export const collectJarNS = function (jarPath, pomPathMap = {}) {
6685
6726
  // .m2/repository/org/apache/logging/log4j/log4j-web/3.0.0-SNAPSHOT/log4j-web-3.0.0-SNAPSHOT.jar
6686
6727
  const tmpA = jf.split(join(".m2", "repository", ""));
6687
6728
  if (tmpA && tmpA.length) {
6688
- let tmpJarPath = tmpA[tmpA.length - 1];
6729
+ const tmpJarPath = tmpA[tmpA.length - 1];
6689
6730
  // This would yield log4j-web-3.0.0-SNAPSHOT.jar
6690
6731
  const jarFileName = basename(tmpJarPath).replace(".jar", "");
6691
- let tmpDirParts = dirname(tmpJarPath).split(_sep);
6732
+ const tmpDirParts = dirname(tmpJarPath).split(_sep);
6692
6733
  // Retrieve the version
6693
6734
  let jarVersion = tmpDirParts.pop();
6694
6735
  if (jarVersion === "plugins") {
@@ -6731,10 +6772,10 @@ export const collectJarNS = function (jarPath, pomPathMap = {}) {
6731
6772
  // .gradle/caches/modules-2/files-2.1/org.xmlresolver/xmlresolver/4.2.0/f4dbdaa83d636dcac91c9003ffa7fb173173fe8d/xmlresolver-4.2.0-data.jar
6732
6773
  const tmpA = jf.split(join("files-2.1", ""));
6733
6774
  if (tmpA && tmpA.length) {
6734
- let tmpJarPath = tmpA[tmpA.length - 1];
6775
+ const tmpJarPath = tmpA[tmpA.length - 1];
6735
6776
  // This would yield xmlresolver-4.2.0-data.jar
6736
6777
  const jarFileName = basename(tmpJarPath).replace(".jar", "");
6737
- let tmpDirParts = dirname(tmpJarPath).split(_sep);
6778
+ const tmpDirParts = dirname(tmpJarPath).split(_sep);
6738
6779
  // This would remove the hash from the end of the directory name
6739
6780
  tmpDirParts.pop();
6740
6781
  // Retrieve the version
@@ -6757,44 +6798,9 @@ export const collectJarNS = function (jarPath, pomPathMap = {}) {
6757
6798
  jarNSMapping[purl] = jarNSMapping_cache[purl];
6758
6799
  } else {
6759
6800
  if (DEBUG_MODE) {
6760
- console.log(`Executing 'jar tf ${jf}'`);
6761
- }
6762
- const jarResult = spawnSync("jar", ["-tf", jf], {
6763
- encoding: "utf-8",
6764
- shell: isWin,
6765
- maxBuffer: 50 * 1024 * 1024,
6766
- env
6767
- });
6768
- if (
6769
- jarResult &&
6770
- jarResult.stderr &&
6771
- jarResult.stderr.includes(
6772
- "is not recognized as an internal or external command"
6773
- )
6774
- ) {
6775
- jarCommandAvailable = false;
6776
- console.log(
6777
- "jar command is not available in PATH. Ensure JDK >= 21 is installed and set the environment variables JAVA_HOME and PATH to the bin directory inside JAVA_HOME."
6778
- );
6801
+ console.log(`Parsing ${jf}`);
6779
6802
  }
6780
- const consolelines = (jarResult.stdout || "").split("\n");
6781
- const nsList = consolelines
6782
- .filter((l) => {
6783
- return (
6784
- (l.includes(".class") ||
6785
- l.includes(".java") ||
6786
- l.includes(".kt")) &&
6787
- !l.includes("-INF") &&
6788
- !l.includes("module-info")
6789
- );
6790
- })
6791
- .map((e) => {
6792
- return e
6793
- .replace("\r", "")
6794
- .replace(/.(class|java|kt)/, "")
6795
- .replace(/\/$/, "")
6796
- .replace(/\//g, ".");
6797
- });
6803
+ const nsList = await getJarClasses(jf);
6798
6804
  jarNSMapping[purl || jf] = {
6799
6805
  jarFile: jf,
6800
6806
  pom: pomData,
@@ -6816,7 +6822,7 @@ export const collectJarNS = function (jarPath, pomPathMap = {}) {
6816
6822
  };
6817
6823
 
6818
6824
  export const convertJarNSToPackages = (jarNSMapping) => {
6819
- let pkgList = [];
6825
+ const pkgList = [];
6820
6826
  for (const purl of Object.keys(jarNSMapping)) {
6821
6827
  let { jarFile, pom, namespaces } = jarNSMapping[purl];
6822
6828
  if (!pom) {
@@ -6977,7 +6983,7 @@ export const getPomPropertiesFromMavenDir = function (mavenDir) {
6977
6983
  * @param {string} path path to file
6978
6984
  * @returns {Promise<String>} hex value of hash
6979
6985
  */
6980
- async function checksumFile(hashName, path) {
6986
+ function checksumFile(hashName, path) {
6981
6987
  return new Promise((resolve, reject) => {
6982
6988
  const hash = createHash(hashName);
6983
6989
  const stream = createReadStream(path);
@@ -7006,7 +7012,7 @@ export const extractJarArchive = async function (
7006
7012
  const fname = basename(jarFile);
7007
7013
  let pomname = undefined;
7008
7014
  // If there is a pom file in the same directory, try to use it
7009
- let manifestname = join(dirname(jarFile), "META-INF", "MANIFEST.MF");
7015
+ const manifestname = join(dirname(jarFile), "META-INF", "MANIFEST.MF");
7010
7016
  // Issue 439: Current implementation checks for existance of a .pom file, but .pom file is not used.
7011
7017
  // Instead code expects to find META-INF/MANIFEST.MF in the same folder as a .jar file.
7012
7018
  // For now check for presence of both .pom and MANIFEST.MF files.
@@ -7106,7 +7112,15 @@ export const extractJarArchive = async function (
7106
7112
  sha +
7107
7113
  "%22&rows=20&wt=json";
7108
7114
  const res = await cdxgenAgent.get(searchurl, {
7109
- responseType: "json"
7115
+ responseType: "json",
7116
+ timeout: {
7117
+ lookup: 200,
7118
+ connect: 5000,
7119
+ secureConnect: 5000,
7120
+ socket: 1000,
7121
+ send: 10000,
7122
+ response: 1000
7123
+ }
7110
7124
  });
7111
7125
  const data = res && res.body ? res.body["response"] : undefined;
7112
7126
  if (data && data["numFound"] == 1) {
@@ -7118,7 +7132,9 @@ export const extractJarArchive = async function (
7118
7132
  }
7119
7133
  } catch (err) {
7120
7134
  if (err && err.message && !err.message.includes("404")) {
7121
- console.log(err);
7135
+ if (DEBUG_MODE) {
7136
+ console.log(err);
7137
+ }
7122
7138
  search_maven_org_errors++;
7123
7139
  }
7124
7140
  }
@@ -7202,7 +7218,7 @@ export const extractJarArchive = async function (
7202
7218
  group = group === "." ? name : group || name;
7203
7219
  }
7204
7220
  if (name && version) {
7205
- let apkg = {
7221
+ const apkg = {
7206
7222
  group: group ? encodeForPurl(group) : "",
7207
7223
  name: name ? encodeForPurl(name) : "",
7208
7224
  version,
@@ -7391,6 +7407,53 @@ export const readZipEntry = async function (
7391
7407
  return retData;
7392
7408
  };
7393
7409
 
7410
+ /**
7411
+ * Method to get the classes and relevant sources in a jar file
7412
+ *
7413
+ * @param {string} jarFile Jar file to read
7414
+ *
7415
+ * @returns List of classes and sources matching certain known patterns
7416
+ */
7417
+ export const getJarClasses = async function (jarFile) {
7418
+ const retList = [];
7419
+ try {
7420
+ const zip = new StreamZip.async({ file: jarFile });
7421
+ const entriesCount = await zip.entriesCount;
7422
+ if (!entriesCount) {
7423
+ return [];
7424
+ }
7425
+ const entries = await zip.entries();
7426
+ for (const entry of Object.values(entries)) {
7427
+ if (entry.isDirectory) {
7428
+ continue;
7429
+ }
7430
+ if (
7431
+ (entry.name.includes(".class") ||
7432
+ entry.name.includes(".java") ||
7433
+ entry.name.includes(".scala") ||
7434
+ entry.name.includes(".groovy") ||
7435
+ entry.name.includes(".kt")) &&
7436
+ !entry.name.includes("-INF") &&
7437
+ !entry.name.includes("module-info")
7438
+ ) {
7439
+ retList.push(
7440
+ entry.name
7441
+ .replace("\r", "")
7442
+ .replace(/.(class|java|kt|scala|groovy)/g, "")
7443
+ .replace(/\/$/, "")
7444
+ .replace(/\//g, ".")
7445
+ );
7446
+ }
7447
+ }
7448
+ zip.close();
7449
+ } catch (e) {
7450
+ if (DEBUG_MODE) {
7451
+ console.log(`Unable to parse ${jarFile}. Skipping.`);
7452
+ }
7453
+ }
7454
+ return retList;
7455
+ };
7456
+
7394
7457
  /**
7395
7458
  * Method to return the gradle command to use.
7396
7459
  *
@@ -7531,7 +7594,7 @@ export const getAtomCommand = () => {
7531
7594
  };
7532
7595
 
7533
7596
  export const executeAtom = (src, args) => {
7534
- let cwd =
7597
+ const cwd =
7535
7598
  existsSync(src) && lstatSync(src).isDirectory() ? src : dirname(src);
7536
7599
  let ATOM_BIN = getAtomCommand();
7537
7600
  let isSupported = true;
@@ -7579,7 +7642,7 @@ export const executeAtom = (src, args) => {
7579
7642
  result.stderr.includes("Error: Could not create the Java Virtual Machine")
7580
7643
  ) {
7581
7644
  console.log(
7582
- "Atom requires Java 17 or above. To improve the SBOM accuracy, please install a suitable version, set the JAVA_HOME environment variable, and re-run cdxgen.\nAlternatively, use the cdxgen container image."
7645
+ "Atom requires Java 21 or above. To improve the SBOM accuracy, please install a suitable version, set the JAVA_HOME environment variable, and re-run cdxgen.\nAlternatively, use the cdxgen container image."
7583
7646
  );
7584
7647
  console.log(`Current JAVA_HOME: ${env["JAVA_HOME"] || ""}`);
7585
7648
  } else if (result.stderr.includes("astgen")) {
@@ -7638,7 +7701,7 @@ export const findAppModules = function (
7638
7701
  ];
7639
7702
  executeAtom(src, args);
7640
7703
  if (existsSync(slicesFile)) {
7641
- const slicesData = JSON.parse(readFileSync(slicesFile), {
7704
+ const slicesData = JSON.parse(readFileSync(slicesFile, "utf-8"), {
7642
7705
  encoding: "utf-8"
7643
7706
  });
7644
7707
  if (slicesData && Object.keys(slicesData) && slicesData.modules) {
@@ -7787,7 +7850,7 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
7787
7850
  if (reqOrSetupFile) {
7788
7851
  // We have a poetry.lock file
7789
7852
  if (reqOrSetupFile.endsWith("poetry.lock")) {
7790
- let poetryConfigArgs = [
7853
+ const poetryConfigArgs = [
7791
7854
  "-m",
7792
7855
  "poetry",
7793
7856
  "config",
@@ -7849,7 +7912,7 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
7849
7912
  }
7850
7913
  }
7851
7914
  } else {
7852
- let poetryEnvArgs = ["env info", "--path"];
7915
+ const poetryEnvArgs = ["env info", "--path"];
7853
7916
  result = spawnSync("poetry", poetryEnvArgs, {
7854
7917
  cwd: basePath,
7855
7918
  encoding: "utf-8",
@@ -7963,7 +8026,7 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
7963
8026
  }
7964
8027
  const name = t.name.replace(/_/g, "-").toLowerCase();
7965
8028
  const version = t.version;
7966
- let exclude = ["pip", "setuptools", "wheel"];
8029
+ const exclude = ["pip", "setuptools", "wheel"];
7967
8030
  if (!exclude.includes(name)) {
7968
8031
  const purlString = new PackageURL(
7969
8032
  "pypi",
@@ -8050,7 +8113,7 @@ export const addEvidenceForImports = (pkgList, allImports) => {
8050
8113
  if (group === "@types") {
8051
8114
  continue;
8052
8115
  }
8053
- let aliases =
8116
+ const aliases =
8054
8117
  group && group.length
8055
8118
  ? [name, `${group}/${name}`, `@${group}/${name}`]
8056
8119
  : [name];
@@ -8124,8 +8187,8 @@ export const parseCmakeDotFile = (dotFile, pkgType, options = {}) => {
8124
8187
  return;
8125
8188
  }
8126
8189
  let name = "";
8127
- let group = "";
8128
- let version = "";
8190
+ const group = "";
8191
+ const version = "";
8129
8192
  let path = undefined;
8130
8193
  if (l.startsWith("digraph")) {
8131
8194
  const tmpA = l.split(" ");
@@ -8151,7 +8214,7 @@ export const parseCmakeDotFile = (dotFile, pkgType, options = {}) => {
8151
8214
  if (tmpA && tmpA.length) {
8152
8215
  const relationship = tmpA[1];
8153
8216
  if (relationship.includes("->")) {
8154
- let tmpB = relationship.split(" -> ");
8217
+ const tmpB = relationship.split(" -> ");
8155
8218
  if (tmpB && tmpB.length === 2) {
8156
8219
  if (tmpB[0].includes(_sep)) {
8157
8220
  tmpB[0] = basename(tmpB[0]);
@@ -8240,9 +8303,9 @@ export const parseCmakeLikeFile = (cmakeListFile, pkgType, options = {}) => {
8240
8303
  if (l === "\n" || l.startsWith("#")) {
8241
8304
  return;
8242
8305
  }
8243
- let group = "";
8244
- let path = undefined;
8245
- let name_list = [];
8306
+ const group = "";
8307
+ const path = undefined;
8308
+ const name_list = [];
8246
8309
  if (l.startsWith("set")) {
8247
8310
  const tmpA = l.replace("set(", "").replace(")", "").trim().split(" ");
8248
8311
  if (tmpA && tmpA.length === 2) {
@@ -8398,7 +8461,7 @@ export const parseCmakeLikeFile = (cmakeListFile, pkgType, options = {}) => {
8398
8461
  }
8399
8462
  }
8400
8463
  for (let n of name_list) {
8401
- let props = [];
8464
+ const props = [];
8402
8465
  let confidence = 0;
8403
8466
  if (
8404
8467
  n &&
@@ -8517,7 +8580,7 @@ export const getOSPackageForFile = (afile, osPkgsList) => {
8517
8580
  */
8518
8581
  export const getCppModules = (src, options, osPkgsList, epkgList) => {
8519
8582
  // Generic is the type to use where the package registry could not be located
8520
- let pkgType = "generic";
8583
+ const pkgType = "generic";
8521
8584
  const pkgList = [];
8522
8585
  const pkgAddedMap = {};
8523
8586
  let sliceData = {};
@@ -8655,11 +8718,11 @@ export const getCppModules = (src, options, osPkgsList, epkgList) => {
8655
8718
  for (let afile of Object.keys(usageData)) {
8656
8719
  // Normalize windows separator
8657
8720
  afile = afile.replace("..\\", "").replace(/\\/g, "/");
8658
- let fileName = basename(afile);
8721
+ const fileName = basename(afile);
8659
8722
  if (!fileName || !fileName.length) {
8660
8723
  continue;
8661
8724
  }
8662
- let extn = extname(fileName);
8725
+ const extn = extname(fileName);
8663
8726
  let group = dirname(afile);
8664
8727
  if (
8665
8728
  group.startsWith(".") ||
@@ -8669,9 +8732,9 @@ export const getCppModules = (src, options, osPkgsList, epkgList) => {
8669
8732
  ) {
8670
8733
  group = "";
8671
8734
  }
8672
- let version = "";
8735
+ const version = "";
8673
8736
  // We need to resolve the name to an os package here
8674
- let name = fileName.replace(extn, "");
8737
+ const name = fileName.replace(extn, "");
8675
8738
  let apkg = getOSPackageForFile(afile, osPkgsList) ||
8676
8739
  epkgMap[group + "/" + name] || {
8677
8740
  name,
@@ -8844,7 +8907,7 @@ async function queryNuget(p, NUGET_URL) {
8844
8907
  function setLatestVersion(upper) {
8845
8908
  // Handle special case for versions with more than 3 parts
8846
8909
  if (upper.split(".").length > 3) {
8847
- let tmpVersionArray = upper.split("-")[0].split(".");
8910
+ const tmpVersionArray = upper.split("-")[0].split(".");
8848
8911
  // Compromise for versions such as 1.2.3.0-alpha
8849
8912
  // How to find latest proper release version?
8850
8913
  if (
@@ -8889,19 +8952,19 @@ async function queryNuget(p, NUGET_URL) {
8889
8952
  NUGET_URL + np.name.toLowerCase() + "/index.json",
8890
8953
  { responseType: "json" }
8891
8954
  );
8892
- let items = res.body.items;
8955
+ const items = res.body.items;
8893
8956
  if (!items || !items[0]) {
8894
8957
  return [np, newBody, body];
8895
8958
  }
8896
8959
  if (items[0] && !items[0].items) {
8897
8960
  if (!p.version || p.version === "0.0.0" || p.version === "latest") {
8898
- let upper = items[items.length - 1].upper;
8961
+ const upper = items[items.length - 1].upper;
8899
8962
  np.version = setLatestVersion(upper);
8900
8963
  }
8901
8964
  for (const item of items) {
8902
8965
  if (np.version) {
8903
- let lower = compare(coerce(item.lower), coerce(np.version));
8904
- let upper = compare(coerce(item.upper), coerce(np.version));
8966
+ const lower = compare(coerce(item.lower), coerce(np.version));
8967
+ const upper = compare(coerce(item.upper), coerce(np.version));
8905
8968
  if (lower !== 1 && upper !== -1) {
8906
8969
  res = await cdxgenAgent.get(item["@id"], { responseType: "json" });
8907
8970
  for (const i of res.body.items.reverse()) {
@@ -8918,13 +8981,13 @@ async function queryNuget(p, NUGET_URL) {
8918
8981
  }
8919
8982
  } else {
8920
8983
  if (!p.version || p.version === "0.0.0" || p.version === "latest") {
8921
- let upper = items[items.length - 1].upper;
8984
+ const upper = items[items.length - 1].upper;
8922
8985
  np.version = setLatestVersion(upper);
8923
8986
  }
8924
8987
  if (np.version) {
8925
8988
  for (const item of items) {
8926
- let lower = compare(coerce(item.lower), coerce(np.version));
8927
- let upper = compare(coerce(item.upper), coerce(np.version));
8989
+ const lower = compare(coerce(item.lower), coerce(np.version));
8990
+ const upper = compare(coerce(item.upper), coerce(np.version));
8928
8991
  if (lower !== 1 && upper !== -1) {
8929
8992
  for (const i of item.items.reverse()) {
8930
8993
  if (