@cyclonedx/cdxgen 9.4.0 → 9.5.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,5 @@
1
1
  import { globSync } from "glob";
2
- import { tmpdir, platform, freemem } from "node:os";
2
+ import { homedir, tmpdir, platform, freemem } from "node:os";
3
3
  import {
4
4
  dirname,
5
5
  sep as _sep,
@@ -11,6 +11,7 @@ import {
11
11
  import {
12
12
  existsSync,
13
13
  readFileSync,
14
+ lstatSync,
14
15
  mkdtempSync,
15
16
  rmSync,
16
17
  copyFileSync,
@@ -26,19 +27,19 @@ let url = import.meta.url;
26
27
  if (!url.startsWith("file://")) {
27
28
  url = new URL(`file://${import.meta.url}`).toString();
28
29
  }
29
- const dirName = import.meta ? dirname(fileURLToPath(url)) : __dirname;
30
+ const dirNameStr = import.meta ? dirname(fileURLToPath(url)) : __dirname;
30
31
 
31
32
  const licenseMapping = JSON.parse(
32
- readFileSync(join(dirName, "data", "lic-mapping.json"))
33
+ readFileSync(join(dirNameStr, "data", "lic-mapping.json"))
33
34
  );
34
35
  const vendorAliases = JSON.parse(
35
- readFileSync(join(dirName, "data", "vendor-alias.json"))
36
+ readFileSync(join(dirNameStr, "data", "vendor-alias.json"))
36
37
  );
37
38
  const spdxLicenses = JSON.parse(
38
- readFileSync(join(dirName, "data", "spdx-licenses.json"))
39
+ readFileSync(join(dirNameStr, "data", "spdx-licenses.json"))
39
40
  );
40
41
  const knownLicenses = JSON.parse(
41
- readFileSync(join(dirName, "data", "known-licenses.json"))
42
+ readFileSync(join(dirNameStr, "data", "known-licenses.json"))
42
43
  );
43
44
  import { load } from "cheerio";
44
45
  import { load as _load } from "js-yaml";
@@ -49,18 +50,19 @@ import StreamZip from "node-stream-zip";
49
50
  import { parseEDNString } from "edn-data";
50
51
  import { PackageURL } from "packageurl-js";
51
52
  import { getTreeWithPlugin } from "./piptree.js";
53
+ import iconv from "iconv-lite";
52
54
 
53
- const selfPJson = JSON.parse(readFileSync(join(dirName, "package.json")));
55
+ const selfPJson = JSON.parse(readFileSync(join(dirNameStr, "package.json")));
54
56
  const _version = selfPJson.version;
55
57
 
56
58
  // Refer to contrib/py-modules.py for a script to generate this list
57
59
  // The script needs to be used once every few months to update this list
58
60
  const PYTHON_STD_MODULES = JSON.parse(
59
- readFileSync(join(dirName, "data", "python-stdlib.json"))
61
+ readFileSync(join(dirNameStr, "data", "python-stdlib.json"))
60
62
  );
61
63
  // Mapping between modules and package names
62
64
  const PYPI_MODULE_PACKAGE_MAPPING = JSON.parse(
63
- readFileSync(join(dirName, "data", "pypi-pkg-aliases.json"))
65
+ readFileSync(join(dirNameStr, "data", "pypi-pkg-aliases.json"))
64
66
  );
65
67
 
66
68
  // Debug mode flag
@@ -168,10 +170,24 @@ export function getLicenses(pkg, format = "xml") {
168
170
  } else if (l.startsWith("http")) {
169
171
  if (!l.includes("opensource.org")) {
170
172
  licenseContent.name = "CUSTOM";
173
+ } else {
174
+ const possibleId = l
175
+ .replace("http://www.opensource.org/licenses/", "")
176
+ .toUpperCase();
177
+ spdxLicenses.forEach((v) => {
178
+ if (v.toUpperCase() === possibleId) {
179
+ licenseContent.id = v;
180
+ }
181
+ });
171
182
  }
172
183
  if (l.includes("mit-license")) {
173
184
  licenseContent.id = "MIT";
174
185
  }
186
+ // We always need a name to avoid validation errors
187
+ // Issue: #469
188
+ if (!licenseContent.name && !licenseContent.id) {
189
+ licenseContent.name = "CUSTOM";
190
+ }
175
191
  licenseContent.url = l;
176
192
  } else {
177
193
  licenseContent.name = l;
@@ -1449,7 +1465,7 @@ export const parseGradleDep = function (
1449
1465
  }
1450
1466
  let stack = [last_purl];
1451
1467
  const depRegex =
1452
- /^.*?--- +(?<group>[^\s:]+):(?<name>[^\s:]+)(?::(?:{strictly [[]?)?(?<versionspecified>[^,\s:}]+))?(?:})?(?:[^->]* +-> +(?<versionoverride>[^\s:]+))?/gm;
1468
+ /^.*?--- +(?<groupspecified>[^\s:]+) ?:(?<namespecified>[^\s:]+)(?::(?:{strictly [[]?)?(?<versionspecified>[^,\s:}]+))?(?:})?(?:[^->]* +-> +(?:(?<groupoverride>[^\s:]+):(?<nameoverride>[^\s:]+):)?(?<versionoverride>[^\s:]+))?/gm;
1453
1469
  for (const rline of rawOutput.split("\n")) {
1454
1470
  if (!rline) {
1455
1471
  continue;
@@ -1467,16 +1483,9 @@ export const parseGradleDep = function (
1467
1483
  rline.startsWith("\\--- ")
1468
1484
  ) {
1469
1485
  last_level = 1;
1470
- if (rline.startsWith("+--- project :")) {
1471
- const tmpProj = rline.split("+--- project :");
1472
- last_project_purl = `pkg:maven/${tmpProj[1].trim()}@${rootProjectVersion}?type=jar`;
1473
- stack = [last_project_purl];
1474
- last_purl = last_project_purl;
1475
- } else {
1476
- last_project_purl = first_purl;
1477
- last_purl = last_project_purl;
1478
- stack = [first_purl];
1479
- }
1486
+ last_project_purl = first_purl;
1487
+ last_purl = last_project_purl;
1488
+ stack = [first_purl];
1480
1489
  }
1481
1490
  if (rline.includes(" - ")) {
1482
1491
  profileName = rline.split(" - ")[0];
@@ -1489,76 +1498,86 @@ export const parseGradleDep = function (
1489
1498
  }
1490
1499
  }
1491
1500
  while ((match = depRegex.exec(rline))) {
1492
- const [line, group, name, versionspecified, versionoverride] = match;
1501
+ const [
1502
+ line,
1503
+ groupspecified,
1504
+ namespecified,
1505
+ versionspecified,
1506
+ groupoverride,
1507
+ nameoverride,
1508
+ versionoverride
1509
+ ] = match;
1510
+ const group = groupoverride || groupspecified;
1511
+ const name = nameoverride || namespecified;
1493
1512
  const version = versionoverride || versionspecified;
1494
- const level = line.split(group)[0].length / 5;
1495
- if (version !== undefined) {
1513
+ const level = line.split(groupspecified)[0].length / 5;
1514
+ if (version !== undefined || group === "project") {
1496
1515
  let purlString = new PackageURL(
1497
1516
  "maven",
1498
- group,
1517
+ group !== "project" ? group : rootProjectGroup,
1499
1518
  name,
1500
- version,
1519
+ version !== undefined ? version : rootProjectVersion,
1501
1520
  { type: "jar" },
1502
1521
  null
1503
1522
  ).toString();
1504
1523
  purlString = decodeURIComponent(purlString);
1505
1524
  keys_cache[purlString + "_" + last_purl] = true;
1506
- if (group !== "project") {
1507
- // Filter duplicates
1508
- if (!deps_keys_cache[purlString]) {
1509
- deps_keys_cache[purlString] = true;
1510
- const adep = {
1511
- group,
1512
- name: name,
1513
- version: version,
1514
- qualifiers: { type: "jar" }
1515
- };
1516
- if (scope) {
1517
- adep["scope"] = scope;
1518
- }
1519
- if (profileName) {
1520
- adep.properties = [
1521
- {
1522
- name: "GradleProfileName",
1523
- value: profileName
1524
- }
1525
- ];
1526
- }
1527
- deps.push(adep);
1525
+ // Filter duplicates
1526
+ if (!deps_keys_cache[purlString]) {
1527
+ deps_keys_cache[purlString] = true;
1528
+ const adep = {
1529
+ group: group !== "project" ? group : rootProjectGroup,
1530
+ name: name,
1531
+ version: version !== undefined ? version : rootProjectVersion,
1532
+ qualifiers: { type: "jar" }
1533
+ };
1534
+ adep["purl"] = purlString;
1535
+ adep["bom-ref"] = purlString;
1536
+ if (scope) {
1537
+ adep["scope"] = scope;
1528
1538
  }
1529
- if (!level_trees[purlString]) {
1530
- level_trees[purlString] = [];
1539
+ if (profileName) {
1540
+ adep.properties = [
1541
+ {
1542
+ name: "GradleProfileName",
1543
+ value: profileName
1544
+ }
1545
+ ];
1531
1546
  }
1532
- if (level == 0) {
1533
- stack = [first_purl];
1534
- stack.push(purlString);
1535
- } else if (last_purl === "") {
1536
- stack.push(purlString);
1537
- } else if (level > last_level) {
1538
- const cnodes = level_trees[last_purl] || [];
1539
- if (!cnodes.includes(purlString)) {
1540
- cnodes.push(purlString);
1541
- }
1542
- level_trees[last_purl] = cnodes;
1543
- if (stack[stack.length - 1] !== purlString) {
1544
- stack.push(purlString);
1545
- }
1546
- } else {
1547
- for (let i = level; i <= last_level; i++) {
1548
- stack.pop();
1549
- }
1550
- const last_stack =
1551
- stack.length > 0 ? stack[stack.length - 1] : last_project_purl;
1552
- const cnodes = level_trees[last_stack] || [];
1553
- if (!cnodes.includes(purlString)) {
1554
- cnodes.push(purlString);
1555
- }
1556
- level_trees[last_stack] = cnodes;
1547
+ deps.push(adep);
1548
+ }
1549
+ if (!level_trees[purlString]) {
1550
+ level_trees[purlString] = [];
1551
+ }
1552
+ if (level == 0) {
1553
+ stack = [first_purl];
1554
+ stack.push(purlString);
1555
+ } else if (last_purl === "") {
1556
+ stack.push(purlString);
1557
+ } else if (level > last_level) {
1558
+ const cnodes = level_trees[last_purl] || [];
1559
+ if (!cnodes.includes(purlString)) {
1560
+ cnodes.push(purlString);
1561
+ }
1562
+ level_trees[last_purl] = cnodes;
1563
+ if (stack[stack.length - 1] !== purlString) {
1557
1564
  stack.push(purlString);
1558
1565
  }
1559
- last_level = level;
1560
- last_purl = purlString;
1566
+ } else {
1567
+ for (let i = level; i <= last_level; i++) {
1568
+ stack.pop();
1569
+ }
1570
+ const last_stack =
1571
+ stack.length > 0 ? stack[stack.length - 1] : last_project_purl;
1572
+ const cnodes = level_trees[last_stack] || [];
1573
+ if (!cnodes.includes(purlString)) {
1574
+ cnodes.push(purlString);
1575
+ }
1576
+ level_trees[last_stack] = cnodes;
1577
+ stack.push(purlString);
1561
1578
  }
1579
+ last_level = level;
1580
+ last_purl = purlString;
1562
1581
  }
1563
1582
  }
1564
1583
  }
@@ -4118,13 +4137,19 @@ export const parseEdnData = function (rawEdnData) {
4118
4137
  };
4119
4138
 
4120
4139
  export const parseNupkg = async function (nupkgFile) {
4121
- const pkgList = [];
4122
- const pkg = { group: "" };
4123
4140
  let nuspecData = await readZipEntry(nupkgFile, ".nuspec");
4124
- // Remove byte order mark
4125
- if (nuspecData.charCodeAt(0) === 0xfeff) {
4126
- nuspecData = nuspecData.slice(1);
4141
+ if (!nuspecData) {
4142
+ return [];
4127
4143
  }
4144
+ if (nuspecData.charCodeAt(0) === 65533) {
4145
+ nuspecData = await readZipEntry(nupkgFile, ".nuspec", "ucs2");
4146
+ }
4147
+ return await parseNuspecData(nupkgFile, nuspecData);
4148
+ };
4149
+
4150
+ export const parseNuspecData = async function (nupkgFile, nuspecData) {
4151
+ const pkgList = [];
4152
+ const pkg = { group: "" };
4128
4153
  let npkg = undefined;
4129
4154
  try {
4130
4155
  npkg = xml2js(nuspecData, {
@@ -4136,9 +4161,13 @@ export const parseNupkg = async function (nupkgFile) {
4136
4161
  commentKey: "value"
4137
4162
  }).package;
4138
4163
  } catch (e) {
4139
- // If we are parsing with invalid encoding unicode replacement character is used
4164
+ // If we are parsing with invalid encoding, unicode replacement character is used
4140
4165
  if (nuspecData.charCodeAt(0) === 65533) {
4141
4166
  console.log(`Unable to parse ${nupkgFile} in utf-8 mode`);
4167
+ } else {
4168
+ console.log(
4169
+ "Unable to parse this package. Tried utf-8 and ucs2 encoding."
4170
+ );
4142
4171
  }
4143
4172
  }
4144
4173
  if (!npkg) {
@@ -4851,48 +4880,100 @@ export const parseSwiftResolved = (resolvedFile) => {
4851
4880
  *
4852
4881
  * @param {string} mavenCmd Maven command to use
4853
4882
  * @param {string} basePath Path to the maven project
4883
+ * @param {boolean} cleanup Remove temporary directories
4884
+ * @param {boolean} includeCacheDir Include maven and gradle cache directories
4854
4885
  */
4855
- export const collectMvnDependencies = function (mavenCmd, basePath) {
4856
- const tempDir = mkdtempSync(join(tmpdir(), "mvn-deps-"));
4857
- console.log(
4858
- `Executing 'mvn dependency:copy-dependencies -DoutputDirectory=${tempDir} -DexcludeTransitive=true -DincludeScope=runtime' in ${basePath}`
4859
- );
4860
- const result = spawnSync(
4861
- mavenCmd,
4862
- [
4863
- "dependency:copy-dependencies",
4864
- `-DoutputDirectory=${tempDir}`,
4865
- "-DexcludeTransitive=true",
4866
- "-DincludeScope=runtime",
4867
- "-U",
4868
- "-Dmdep.prependGroupId=" + (process.env.MAVEN_PREPEND_GROUP || "false"),
4869
- "-Dmdep.stripVersion=" + (process.env.MAVEN_STRIP_VERSION || "false")
4870
- ],
4871
- { cwd: basePath, encoding: "utf-8" }
4872
- );
4886
+ export const collectMvnDependencies = function (
4887
+ mavenCmd,
4888
+ basePath,
4889
+ cleanup = true,
4890
+ includeCacheDir = false
4891
+ ) {
4873
4892
  let jarNSMapping = {};
4874
- if (result.status !== 0 || result.error) {
4875
- console.error(result.stdout, result.stderr);
4876
- console.log(
4877
- "Resolve the above maven error. You can try the following remediation tips:\n"
4878
- );
4893
+ const MAVEN_CACHE_DIR =
4894
+ process.env.MAVEN_CACHE_DIR || join(homedir(), ".m2", "repository");
4895
+ const tempDir = mkdtempSync(join(tmpdir(), "mvn-deps-"));
4896
+ const copyArgs = [
4897
+ "dependency:copy-dependencies",
4898
+ `-DoutputDirectory=${tempDir}`,
4899
+ "-U",
4900
+ "-Dmdep.copyPom=true",
4901
+ "-Dmdep.useRepositoryLayout=true",
4902
+ "-Dmdep.includeScope=compile",
4903
+ "-Dmdep.prependGroupId=" + (process.env.MAVEN_PREPEND_GROUP || "false"),
4904
+ "-Dmdep.stripVersion=" + (process.env.MAVEN_STRIP_VERSION || "false")
4905
+ ];
4906
+ if (basePath && basePath !== MAVEN_CACHE_DIR) {
4879
4907
  console.log(
4880
- "1. Check if the correct version of maven is installed and available in the PATH."
4908
+ `Executing '${mavenCmd} dependency:copy-dependencies ${copyArgs.join(
4909
+ " "
4910
+ )}' in ${basePath}`
4881
4911
  );
4882
- console.log(
4883
- "2. Perform 'mvn compile package' before invoking this command. Fix any errors found during this invocation."
4912
+ const result = spawnSync(mavenCmd, copyArgs, {
4913
+ cwd: basePath,
4914
+ encoding: "utf-8"
4915
+ });
4916
+ if (result.status !== 0 || result.error) {
4917
+ console.error(result.stdout, result.stderr);
4918
+ console.log(
4919
+ "Resolve the above maven error. You can try the following remediation tips:\n"
4920
+ );
4921
+ console.log(
4922
+ "1. Check if the correct version of maven is installed and available in the PATH."
4923
+ );
4924
+ console.log(
4925
+ "2. Perform 'mvn compile package' before invoking this command. Fix any errors found during this invocation."
4926
+ );
4927
+ console.log(
4928
+ "3. Ensure the temporary directory is available and has sufficient disk space to copy all the artifacts."
4929
+ );
4930
+ } else {
4931
+ jarNSMapping = collectJarNS(tempDir);
4932
+ }
4933
+ }
4934
+ if (includeCacheDir || basePath === MAVEN_CACHE_DIR) {
4935
+ // slow operation
4936
+ jarNSMapping = collectJarNS(MAVEN_CACHE_DIR);
4937
+ }
4938
+
4939
+ // Clean up
4940
+ if (cleanup && tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
4941
+ rmSync(tempDir, { recursive: true, force: true });
4942
+ }
4943
+ return jarNSMapping;
4944
+ };
4945
+
4946
+ export const collectGradleDependencies = (
4947
+ gradleCmd,
4948
+ basePath,
4949
+ cleanup = true, // eslint-disable-line no-unused-vars
4950
+ includeCacheDir = false // eslint-disable-line no-unused-vars
4951
+ ) => {
4952
+ // HELP WANTED: We need an init script that mimics maven copy-dependencies that only collects the project specific jars and poms
4953
+ // Construct gradle cache directory
4954
+ let GRADLE_CACHE_DIR =
4955
+ process.env.GRADLE_CACHE_DIR ||
4956
+ join(homedir(), ".gradle", "caches", "modules-2", "files-2.1");
4957
+ if (process.env.GRADLE_USER_HOME) {
4958
+ GRADLE_CACHE_DIR = join(
4959
+ process.env.GRADLE_USER_HOME,
4960
+ "caches",
4961
+ "modules-2",
4962
+ "files-2.1"
4884
4963
  );
4964
+ }
4965
+ if (DEBUG_MODE) {
4966
+ console.log("Collecting jars from", GRADLE_CACHE_DIR);
4885
4967
  console.log(
4886
- "3. Ensure the temporary directory is available and has sufficient disk space to copy all the artifacts."
4968
+ "To improve performance, ensure only the project dependencies are present in this cache location."
4887
4969
  );
4888
- } else {
4889
- jarNSMapping = collectJarNS(tempDir);
4890
4970
  }
4891
- // Clean up
4892
- if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
4893
- console.log(`Cleaning up ${tempDir}`);
4894
- rmSync(tempDir, { recursive: true, force: true });
4971
+ const pomPathMap = {};
4972
+ const pomFiles = getAllFiles(GRADLE_CACHE_DIR, "**/*.pom");
4973
+ for (const apom of pomFiles) {
4974
+ pomPathMap[basename(apom)] = apom;
4895
4975
  }
4976
+ const jarNSMapping = collectJarNS(GRADLE_CACHE_DIR, pomPathMap);
4896
4977
  return jarNSMapping;
4897
4978
  };
4898
4979
 
@@ -4900,10 +4981,11 @@ export const collectMvnDependencies = function (mavenCmd, basePath) {
4900
4981
  * Method to collect class names from all jars in a directory
4901
4982
  *
4902
4983
  * @param {string} jarPath Path containing jars
4984
+ * @param {object} pomPathMap Map containing jar to pom names. Required to successful parse gradle cache.
4903
4985
  *
4904
4986
  * @return object containing jar name and class list
4905
4987
  */
4906
- export const collectJarNS = function (jarPath) {
4988
+ export const collectJarNS = function (jarPath, pomPathMap = {}) {
4907
4989
  const jarNSMapping = {};
4908
4990
  console.log(
4909
4991
  `About to identify class names for all jars in the path ${jarPath}`
@@ -4912,31 +4994,72 @@ export const collectJarNS = function (jarPath) {
4912
4994
  const jarFiles = getAllFiles(jarPath, "**/*.jar");
4913
4995
  if (jarFiles && jarFiles.length) {
4914
4996
  for (const jf of jarFiles) {
4915
- const jarname = basename(jf);
4997
+ const jarname = jf;
4998
+ const pomname =
4999
+ pomPathMap[basename(jf).replace(".jar", ".pom")] ||
5000
+ jarname.replace(".jar", ".pom");
5001
+ let pomData = undefined;
5002
+ let purl = undefined;
5003
+ if (existsSync(pomname)) {
5004
+ pomData = parsePomXml(readFileSync(pomname, "utf-8"));
5005
+ if (pomData) {
5006
+ const purlObj = new PackageURL(
5007
+ "maven",
5008
+ pomData.groupId || "",
5009
+ pomData.artifactId,
5010
+ pomData.version,
5011
+ { type: "jar" },
5012
+ null
5013
+ );
5014
+ purl = purlObj.toString();
5015
+ }
5016
+ } else if (jf.includes(join(".gradle", "caches"))) {
5017
+ // Let's try our best to construct a purl for gradle cache entries of the form
5018
+ // .gradle/caches/modules-2/files-2.1/org.xmlresolver/xmlresolver/4.2.0/f4dbdaa83d636dcac91c9003ffa7fb173173fe8d/xmlresolver-4.2.0-data.jar
5019
+ const tmpA = jf.split(join("files-2.1", ""));
5020
+ if (tmpA && tmpA.length) {
5021
+ let tmpJarPath = tmpA[tmpA.length - 1];
5022
+ // This would yield xmlresolver-4.2.0-data.jar
5023
+ const jarFileName = basename(tmpJarPath);
5024
+ let tmpDirParts = dirname(tmpJarPath).split(_sep);
5025
+ // This would remove the hash from the end of the directory name
5026
+ tmpDirParts.pop();
5027
+ // Retrieve the version
5028
+ const jarVersion = tmpDirParts.pop();
5029
+ // The result would form the group name
5030
+ const jarGroupName = tmpDirParts.join(".").replace(/^\./, "");
5031
+ const purlObj = new PackageURL(
5032
+ "maven",
5033
+ jarGroupName,
5034
+ jarFileName.replace(`-${jarVersion}`, ""),
5035
+ jarVersion,
5036
+ { type: "jar" },
5037
+ null
5038
+ );
5039
+ purl = purlObj.toString();
5040
+ }
5041
+ }
4916
5042
  if (DEBUG_MODE) {
4917
5043
  console.log(`Executing 'jar tf ${jf}'`);
4918
5044
  }
4919
5045
  const jarResult = spawnSync("jar", ["-tf", jf], { encoding: "utf-8" });
4920
- if (jarResult.status !== 0) {
4921
- console.error(jarResult.stdout, jarResult.stderr);
4922
- console.log(
4923
- "Check if JRE is installed and the jar command is available in the PATH."
4924
- );
4925
- break;
4926
- } else {
4927
- const consolelines = (jarResult.stdout || "").split("\n");
4928
- const nsList = consolelines
4929
- .filter((l) => {
4930
- return l.includes(".class") && !l.includes("-INF");
4931
- })
4932
- .map((e) => {
4933
- return e
4934
- .replace(/\/$/, "")
4935
- .replace(/\//g, ".")
4936
- .replace(".class", "");
4937
- });
4938
- jarNSMapping[jarname] = nsList;
4939
- }
5046
+ const consolelines = (jarResult.stdout || "").split("\n");
5047
+ const nsList = consolelines
5048
+ .filter((l) => {
5049
+ return (
5050
+ l.includes(".class") &&
5051
+ !l.includes("-INF") &&
5052
+ !l.includes("module-info")
5053
+ );
5054
+ })
5055
+ .map((e) => {
5056
+ return e.replace(".class", "").replace(/\/$/, "").replace(/\//g, ".");
5057
+ });
5058
+ jarNSMapping[purl || jf] = {
5059
+ jarFile: jf,
5060
+ pom: pomData,
5061
+ namespaces: nsList
5062
+ };
4940
5063
  }
4941
5064
  if (!jarNSMapping) {
4942
5065
  console.log(`Unable to determine class names for the jars in ${jarPath}`);
@@ -4944,12 +5067,69 @@ export const collectJarNS = function (jarPath) {
4944
5067
  } else {
4945
5068
  console.log(`${jarPath} did not contain any jars.`);
4946
5069
  }
4947
- if (DEBUG_MODE) {
4948
- console.log("JAR Namespace mapping", jarNSMapping);
4949
- }
4950
5070
  return jarNSMapping;
4951
5071
  };
4952
5072
 
5073
+ export const convertJarNSToPackages = (jarNSMapping) => {
5074
+ let pkgList = [];
5075
+ for (const purl of Object.keys(jarNSMapping)) {
5076
+ let { jarFile, pom, namespaces } = jarNSMapping[purl];
5077
+ if (!pom) {
5078
+ pom = {};
5079
+ }
5080
+ let purlObj = undefined;
5081
+ try {
5082
+ purlObj = PackageURL.fromString(purl);
5083
+ } catch (e) {
5084
+ // ignore
5085
+ purlObj = {};
5086
+ }
5087
+ const name = pom.artifactId || purlObj.name;
5088
+ if (!name) {
5089
+ continue;
5090
+ }
5091
+ const apackage = {
5092
+ name,
5093
+ group: pom.groupId || purlObj.namespace || "",
5094
+ version: pom.version || purlObj.version,
5095
+ description: (pom.description || "").trim(),
5096
+ purl,
5097
+ "bom-ref": purl,
5098
+ evidence: {
5099
+ identity: {
5100
+ field: "purl",
5101
+ confidence: 0.5,
5102
+ methods: [
5103
+ {
5104
+ technique: "filename",
5105
+ confidence: 1,
5106
+ value: jarFile
5107
+ }
5108
+ ]
5109
+ }
5110
+ },
5111
+ properties: [
5112
+ {
5113
+ name: "SrcFile",
5114
+ value: jarFile
5115
+ },
5116
+ {
5117
+ name: "Namespaces",
5118
+ value: namespaces.join("\n")
5119
+ }
5120
+ ]
5121
+ };
5122
+ if (pom.url) {
5123
+ apackage["homepage"] = { url: pom.url };
5124
+ }
5125
+ if (pom.scm) {
5126
+ apackage["repository"] = { url: pom.scm };
5127
+ }
5128
+ pkgList.push(apackage);
5129
+ }
5130
+ return pkgList;
5131
+ };
5132
+
4953
5133
  export const parsePomXml = function (pomXmlData) {
4954
5134
  if (!pomXmlData) {
4955
5135
  return undefined;
@@ -5293,10 +5473,15 @@ export const sbtPluginsPath = function (projectPath) {
5293
5473
  *
5294
5474
  * @param {string} zipFile Zip file to read
5295
5475
  * @param {string} filePattern File pattern
5476
+ * @param {string} contentEncoding Encoding. Defaults to utf-8
5296
5477
  *
5297
5478
  * @returns File contents
5298
5479
  */
5299
- export const readZipEntry = async function (zipFile, filePattern) {
5480
+ export const readZipEntry = async function (
5481
+ zipFile,
5482
+ filePattern,
5483
+ contentEncoding = "utf-8"
5484
+ ) {
5300
5485
  let retData = undefined;
5301
5486
  try {
5302
5487
  const zip = new StreamZip.async({ file: zipFile });
@@ -5311,7 +5496,7 @@ export const readZipEntry = async function (zipFile, filePattern) {
5311
5496
  }
5312
5497
  if (entry.name.endsWith(filePattern)) {
5313
5498
  const fileData = await zip.entryData(entry.name);
5314
- retData = Buffer.from(fileData).toString();
5499
+ retData = iconv.decode(Buffer.from(fileData), contentEncoding);
5315
5500
  break;
5316
5501
  }
5317
5502
  }
@@ -5418,7 +5603,7 @@ export const getAtomCommand = () => {
5418
5603
  }
5419
5604
  const NODE_CMD = process.env.NODE_CMD || "node";
5420
5605
  const localAtom = join(
5421
- dirName,
5606
+ dirNameStr,
5422
5607
  "node_modules",
5423
5608
  "@appthreat",
5424
5609
  "atom",
@@ -5431,6 +5616,8 @@ export const getAtomCommand = () => {
5431
5616
  };
5432
5617
 
5433
5618
  export const executeAtom = (src, args) => {
5619
+ let cwd =
5620
+ existsSync(src) && lstatSync(src).isDirectory() ? src : dirname(src);
5434
5621
  let ATOM_BIN = getAtomCommand();
5435
5622
  if (ATOM_BIN.includes(" ")) {
5436
5623
  const tmpA = ATOM_BIN.split(" ");
@@ -5448,7 +5635,7 @@ export const executeAtom = (src, args) => {
5448
5635
  JAVA_OPTS: `-Xms${freeMemoryGB}G -Xmx${freeMemoryGB}G`
5449
5636
  };
5450
5637
  const result = spawnSync(ATOM_BIN, args, {
5451
- cwd: src,
5638
+ cwd,
5452
5639
  encoding: "utf-8",
5453
5640
  timeout: TIMEOUT_MS,
5454
5641
  env