@cyclonedx/cdxgen 9.0.1 → 9.1.1

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
@@ -48,6 +48,7 @@ import { satisfies, coerce, maxSatisfying, clean, valid } from "semver";
48
48
  import StreamZip from "node-stream-zip";
49
49
  import { parseEDNString } from "edn-data";
50
50
  import { PackageURL } from "packageurl-js";
51
+ import { getTreeWithPlugin } from "./piptree.js";
51
52
 
52
53
  const selfPJson = JSON.parse(readFileSync(join(dirName, "package.json")));
53
54
  const _version = selfPJson.version;
@@ -195,7 +196,7 @@ export function getLicenses(pkg, format = "xml") {
195
196
  * the text to the license text object and stop.
196
197
  */
197
198
  export function addLicenseText(pkg, l, licenseContent, format = "xml") {
198
- let licenseFilenames = [
199
+ const licenseFilenames = [
199
200
  "LICENSE",
200
201
  "License",
201
202
  "license",
@@ -206,7 +207,7 @@ export function addLicenseText(pkg, l, licenseContent, format = "xml") {
206
207
  "Notice",
207
208
  "notice"
208
209
  ];
209
- let licenseContentTypes = {
210
+ const licenseContentTypes = {
210
211
  "text/plain": "",
211
212
  "text/txt": ".txt",
212
213
  "text/markdown": ".md",
@@ -219,7 +220,7 @@ export function addLicenseText(pkg, l, licenseContent, format = "xml") {
219
220
  for (const [licenseContentType, fileExtension] of Object.entries(
220
221
  licenseContentTypes
221
222
  )) {
222
- let licenseFilepath = `${pkg.realPath}/${licenseFilename}${licenseName}${fileExtension}`;
223
+ const licenseFilepath = `${pkg.realPath}/${licenseFilename}${licenseName}${fileExtension}`;
223
224
  if (existsSync(licenseFilepath)) {
224
225
  licenseContent.text = readLicenseText(
225
226
  licenseFilepath,
@@ -242,16 +243,16 @@ export function readLicenseText(
242
243
  licenseContentType,
243
244
  format = "xml"
244
245
  ) {
245
- let licenseText = readFileSync(licenseFilepath, "utf8");
246
+ const licenseText = readFileSync(licenseFilepath, "utf8");
246
247
  if (licenseText) {
247
248
  if (format === "xml") {
248
- let licenseContentText = { "#cdata": licenseText };
249
+ const licenseContentText = { "#cdata": licenseText };
249
250
  if (licenseContentType !== "text/plain") {
250
251
  licenseContentText["@content-type"] = licenseContentType;
251
252
  }
252
253
  return licenseContentText;
253
254
  } else {
254
- let licenseContentText = { content: licenseText };
255
+ const licenseContentText = { content: licenseText };
255
256
  if (licenseContentType !== "text/plain") {
256
257
  licenseContentText["contentType"] = licenseContentType;
257
258
  }
@@ -315,7 +316,7 @@ const _getDepPkgList = async function (
315
316
  depKeys,
316
317
  pkg
317
318
  ) {
318
- let pkgDependencies =
319
+ const pkgDependencies =
319
320
  pkg.lockfileVersion && pkg.lockfileVersion >= 3
320
321
  ? pkg.packages
321
322
  : pkg.dependencies;
@@ -326,7 +327,7 @@ const _getDepPkgList = async function (
326
327
  if (k === "") {
327
328
  continue;
328
329
  }
329
- let name = k;
330
+ const name = k;
330
331
  const version = pkgDependencies[name].version;
331
332
  const purl = new PackageURL(
332
333
  "npm",
@@ -337,7 +338,7 @@ const _getDepPkgList = async function (
337
338
  null
338
339
  );
339
340
  const purlString = decodeURIComponent(purl.toString());
340
- let scope = pkgDependencies[name].dev === true ? "optional" : undefined;
341
+ const scope = pkgDependencies[name].dev === true ? "optional" : undefined;
341
342
  const apkg = {
342
343
  name: name.replace("node_modules/", ""),
343
344
  version,
@@ -348,7 +349,20 @@ const _getDepPkgList = async function (
348
349
  name: "SrcFile",
349
350
  value: pkgLockFile
350
351
  }
351
- ]
352
+ ],
353
+ evidence: {
354
+ identity: {
355
+ field: "purl",
356
+ confidence: 1,
357
+ methods: [
358
+ {
359
+ technique: "manifest-analysis",
360
+ confidence: 1,
361
+ value: pkgLockFile
362
+ }
363
+ ]
364
+ }
365
+ }
352
366
  };
353
367
  pkgList.push(apkg);
354
368
  if (pkgDependencies[name].dependencies) {
@@ -422,7 +436,20 @@ export const parsePkgJson = async (pkgJsonFile) => {
422
436
  name: "SrcFile",
423
437
  value: pkgJsonFile
424
438
  }
425
- ]
439
+ ],
440
+ evidence: {
441
+ identity: {
442
+ field: "purl",
443
+ confidence: 1,
444
+ methods: [
445
+ {
446
+ technique: "manifest-analysis",
447
+ confidence: 1,
448
+ value: pkgJsonFile
449
+ }
450
+ ]
451
+ }
452
+ }
426
453
  });
427
454
  } catch (err) {
428
455
  // continue regardless of error
@@ -446,8 +473,8 @@ export const parsePkgJson = async (pkgJsonFile) => {
446
473
  */
447
474
  export const parsePkgLock = async (pkgLockFile) => {
448
475
  let pkgList = [];
449
- let dependenciesList = [];
450
- let depKeys = {};
476
+ const dependenciesList = [];
477
+ const depKeys = {};
451
478
  let rootPkg = {};
452
479
  if (existsSync(pkgLockFile)) {
453
480
  const lockData = JSON.parse(readFileSync(pkgLockFile, "utf8"));
@@ -652,7 +679,20 @@ export const parseYarnLock = async function (yarnLockFile) {
652
679
  name: "SrcFile",
653
680
  value: yarnLockFile
654
681
  }
655
- ]
682
+ ],
683
+ evidence: {
684
+ identity: {
685
+ field: "purl",
686
+ confidence: 1,
687
+ methods: [
688
+ {
689
+ technique: "manifest-analysis",
690
+ confidence: 1,
691
+ value: yarnLockFile
692
+ }
693
+ ]
694
+ }
695
+ }
656
696
  });
657
697
  // Reset all the variables
658
698
  group = "";
@@ -766,7 +806,7 @@ export const parseNodeShrinkwrap = async function (swFile) {
766
806
  if (existsSync(swFile)) {
767
807
  const lockData = JSON.parse(readFileSync(swFile, "utf8"));
768
808
  const pkgKeys = Object.keys(lockData);
769
- for (var k in pkgKeys) {
809
+ for (const k in pkgKeys) {
770
810
  const fullName = pkgKeys[k];
771
811
  const integrity = lockData[fullName];
772
812
  const parts = fullName.split("@");
@@ -779,7 +819,7 @@ export const parseNodeShrinkwrap = async function (swFile) {
779
819
  version = parts[1];
780
820
  } else if (parts.length === 3) {
781
821
  if (parts[0] === "") {
782
- let gnameparts = parts[1].split("/");
822
+ const gnameparts = parts[1].split("/");
783
823
  group = gnameparts[0];
784
824
  name = gnameparts[1];
785
825
  } else {
@@ -798,7 +838,20 @@ export const parseNodeShrinkwrap = async function (swFile) {
798
838
  name: "SrcFile",
799
839
  value: swFile
800
840
  }
801
- ]
841
+ ],
842
+ evidence: {
843
+ identity: {
844
+ field: "purl",
845
+ confidence: 1,
846
+ methods: [
847
+ {
848
+ technique: "manifest-analysis",
849
+ confidence: 1,
850
+ value: swFile
851
+ }
852
+ ]
853
+ }
854
+ }
802
855
  });
803
856
  }
804
857
  }
@@ -874,7 +927,7 @@ export const parsePnpmLock = async function (pnpmLock, parentComponent = null) {
874
927
  }
875
928
  const packages = yamlObj.packages;
876
929
  const pkgKeys = Object.keys(packages);
877
- for (var k in pkgKeys) {
930
+ for (const k in pkgKeys) {
878
931
  // Eg: @babel/code-frame/7.10.1
879
932
  // In lockfileVersion 6, /@babel/code-frame@7.18.6
880
933
  let fullName = pkgKeys[k].replace("/@", "@");
@@ -885,7 +938,7 @@ export const parsePnpmLock = async function (pnpmLock, parentComponent = null) {
885
938
  const parts = fullName.split("/");
886
939
  const integrity = packages[pkgKeys[k]].resolution.integrity;
887
940
  const deps = packages[pkgKeys[k]].dependencies || [];
888
- let scope = packages[pkgKeys[k]].dev === true ? "optional" : undefined;
941
+ const scope = packages[pkgKeys[k]].dev === true ? "optional" : undefined;
889
942
  if (parts && parts.length) {
890
943
  let name = "";
891
944
  let version = "";
@@ -952,7 +1005,20 @@ export const parsePnpmLock = async function (pnpmLock, parentComponent = null) {
952
1005
  name: "SrcFile",
953
1006
  value: pnpmLock
954
1007
  }
955
- ]
1008
+ ],
1009
+ evidence: {
1010
+ identity: {
1011
+ field: "purl",
1012
+ confidence: 1,
1013
+ methods: [
1014
+ {
1015
+ technique: "manifest-analysis",
1016
+ confidence: 1,
1017
+ value: pnpmLock
1018
+ }
1019
+ ]
1020
+ }
1021
+ }
956
1022
  });
957
1023
  }
958
1024
  }
@@ -998,7 +1064,20 @@ export const parseBowerJson = async (bowerJsonFile) => {
998
1064
  name: "SrcFile",
999
1065
  value: bowerJsonFile
1000
1066
  }
1001
- ]
1067
+ ],
1068
+ evidence: {
1069
+ identity: {
1070
+ field: "purl",
1071
+ confidence: 1,
1072
+ methods: [
1073
+ {
1074
+ technique: "manifest-analysis",
1075
+ confidence: 1,
1076
+ value: bowerJsonFile
1077
+ }
1078
+ ]
1079
+ }
1080
+ }
1002
1081
  });
1003
1082
  } catch (err) {
1004
1083
  // continue regardless of error
@@ -1049,7 +1128,7 @@ export const parseMinJs = async (minJsFile) => {
1049
1128
  : pkgNameVer.split(" ");
1050
1129
  if (tmpB && tmpB.length > 1) {
1051
1130
  // Fix #223 - lowercase parsed package name
1052
- let name = tmpB[0].replace(/ /g, "-").trim().toLowerCase();
1131
+ const name = tmpB[0].replace(/ /g, "-").trim().toLowerCase();
1053
1132
  if (
1054
1133
  ["copyright", "author", "licensed"].includes(name.toLowerCase())
1055
1134
  ) {
@@ -1066,7 +1145,20 @@ export const parseMinJs = async (minJsFile) => {
1066
1145
  name: "SrcFile",
1067
1146
  value: minJsFile
1068
1147
  }
1069
- ]
1148
+ ],
1149
+ evidence: {
1150
+ identity: {
1151
+ field: "purl",
1152
+ confidence: 0.25,
1153
+ methods: [
1154
+ {
1155
+ technique: "filename",
1156
+ confidence: 0.25,
1157
+ value: minJsFile
1158
+ }
1159
+ ]
1160
+ }
1161
+ }
1070
1162
  });
1071
1163
  }
1072
1164
  return;
@@ -1111,7 +1203,7 @@ export const parsePom = function (pomFile) {
1111
1203
  } else if (dependencies && !Array.isArray(dependencies)) {
1112
1204
  dependencies = [dependencies];
1113
1205
  }
1114
- for (let adep of dependencies) {
1206
+ for (const adep of dependencies) {
1115
1207
  const version = adep.version;
1116
1208
  let versionStr = undefined;
1117
1209
  if (version && version._ && version._.indexOf("$") == -1) {
@@ -1127,7 +1219,20 @@ export const parsePom = function (pomFile) {
1127
1219
  name: "SrcFile",
1128
1220
  value: pomFile
1129
1221
  }
1130
- ]
1222
+ ],
1223
+ evidence: {
1224
+ identity: {
1225
+ field: "purl",
1226
+ confidence: 1,
1227
+ methods: [
1228
+ {
1229
+ technique: "manifest-analysis",
1230
+ confidence: 1,
1231
+ value: pomFile
1232
+ }
1233
+ ]
1234
+ }
1235
+ }
1131
1236
  });
1132
1237
  }
1133
1238
  }
@@ -1150,7 +1255,7 @@ export const parseMavenTree = function (rawOutput) {
1150
1255
  const tmpA = rawOutput.split("\n");
1151
1256
  let last_level = 0;
1152
1257
  let last_purl = "";
1153
- let stack = [];
1258
+ const stack = [];
1154
1259
  tmpA.forEach((l) => {
1155
1260
  if (!includeMavenTestScope && l.trim().endsWith(":test")) {
1156
1261
  return;
@@ -1314,7 +1419,7 @@ export const parseGradleDep = function (
1314
1419
  ) {
1315
1420
  last_level = 1;
1316
1421
  if (rline.startsWith("+--- project :")) {
1317
- let tmpProj = rline.split("+--- project :");
1422
+ const tmpProj = rline.split("+--- project :");
1318
1423
  last_project_purl = `pkg:maven/${tmpProj[1].trim()}@${rootProjectVersion}?type=jar`;
1319
1424
  stack = [last_project_purl];
1320
1425
  last_purl = last_project_purl;
@@ -1481,17 +1586,17 @@ export const parseLeinDep = function (rawOutput) {
1481
1586
 
1482
1587
  export const parseLeinMap = function (node, keys_cache, deps) {
1483
1588
  if (node["map"]) {
1484
- for (let n of node["map"]) {
1589
+ for (const n of node["map"]) {
1485
1590
  if (n.length === 2) {
1486
1591
  const rootNode = n[0];
1487
- let psym = rootNode[0].sym;
1488
- let version = rootNode[1];
1592
+ const psym = rootNode[0].sym;
1593
+ const version = rootNode[1];
1489
1594
  let group = dirname(psym);
1490
1595
  if (group === ".") {
1491
1596
  group = "";
1492
1597
  }
1493
- let name = basename(psym);
1494
- let cacheKey = group + "-" + name + "-" + version;
1598
+ const name = basename(psym);
1599
+ const cacheKey = group + "-" + name + "-" + version;
1495
1600
  if (!keys_cache[cacheKey]) {
1496
1601
  keys_cache[cacheKey] = true;
1497
1602
  deps.push({ group, name, version });
@@ -1524,7 +1629,7 @@ export const parseGradleProjects = function (rawOutput) {
1524
1629
  } else if (l.includes("--- Project")) {
1525
1630
  const tmpB = l.split("Project ");
1526
1631
  if (tmpB && tmpB.length > 1) {
1527
- let projName = tmpB[1].split(" ")[0].replace(/'/g, "");
1632
+ const projName = tmpB[1].split(" ")[0].replace(/'/g, "");
1528
1633
  // Include all projects including test projects
1529
1634
  if (projName.startsWith(":")) {
1530
1635
  projects.add(projName);
@@ -1533,7 +1638,7 @@ export const parseGradleProjects = function (rawOutput) {
1533
1638
  } else if (l.includes("--- project ")) {
1534
1639
  const tmpB = l.split("--- project ");
1535
1640
  if (tmpB && tmpB.length > 1) {
1536
- let projName = tmpB[1];
1641
+ const projName = tmpB[1];
1537
1642
  if (projName.startsWith(":")) {
1538
1643
  projects.add(projName);
1539
1644
  }
@@ -1554,7 +1659,7 @@ export const parseGradleProjects = function (rawOutput) {
1554
1659
  */
1555
1660
  export const parseGradleProperties = function (rawOutput) {
1556
1661
  let rootProject = "root";
1557
- let projects = new Set();
1662
+ const projects = new Set();
1558
1663
  const metadata = { group: "", version: "latest", properties: [] };
1559
1664
  if (typeof rawOutput === "string") {
1560
1665
  const tmpA = rawOutput.split("\n");
@@ -1616,11 +1721,7 @@ export const executeGradleProperties = function (dir, rootPath, subProject) {
1616
1721
  "plain",
1617
1722
  "--build-cache"
1618
1723
  ];
1619
- let gradleCmd = getGradleCommand(dir, rootPath);
1620
- if (process.env.GRADLE_ARGS) {
1621
- const addArgs = process.env.GRADLE_ARGS.split(" ");
1622
- gradlePropertiesArgs = gradlePropertiesArgs.concat(addArgs);
1623
- }
1724
+ const gradleCmd = getGradleCommand(dir, rootPath);
1624
1725
  console.log(
1625
1726
  "Executing",
1626
1727
  gradleCmd,
@@ -1764,7 +1865,7 @@ export const parseKVDep = function (rawOutput) {
1764
1865
  * @param {string} name License full name
1765
1866
  */
1766
1867
  export const findLicenseId = function (name) {
1767
- for (let l of licenseMapping) {
1868
+ for (const l of licenseMapping) {
1768
1869
  if (l.names.includes(name)) {
1769
1870
  return l.exp;
1770
1871
  }
@@ -1781,8 +1882,8 @@ export const findLicenseId = function (name) {
1781
1882
  */
1782
1883
  export const guessLicenseId = function (content) {
1783
1884
  content = content.replace(/\n/g, " ");
1784
- for (let l of licenseMapping) {
1785
- for (let j in l.names) {
1885
+ for (const l of licenseMapping) {
1886
+ for (const j in l.names) {
1786
1887
  if (content.toUpperCase().indexOf(l.names[j].toUpperCase()) > -1) {
1787
1888
  return l.exp;
1788
1889
  }
@@ -1817,7 +1918,7 @@ export const getMvnMetadata = async function (pkgList) {
1817
1918
  if (p.group.indexOf("android") !== -1) {
1818
1919
  urlPrefix = ANDROID_MAVEN;
1819
1920
  }
1820
- let groupPart = p.group.replace(/\./g, "/");
1921
+ const groupPart = p.group.replace(/\./g, "/");
1821
1922
  // Querying maven requires a valid group name
1822
1923
  if (!groupPart || groupPart === "") {
1823
1924
  cdepList.push(p);
@@ -1900,7 +2001,7 @@ export const parsePyRequiresDist = function (dist_string) {
1900
2001
  name = tmpA[0];
1901
2002
  } else if (tmpA.length > 1) {
1902
2003
  name = tmpA[0];
1903
- let tmpVersion = tmpA[1];
2004
+ const tmpVersion = tmpA[1];
1904
2005
  version = tmpVersion.split(",")[0].replace(/[();=&glt><]/g, "");
1905
2006
  }
1906
2007
  return {
@@ -1919,7 +2020,7 @@ export const guessPypiMatchingVersion = (versionsList, versionSpecifiers) => {
1919
2020
  versionSpecifiers = versionSpecifiers.replace(/,/g, " ").split(";")[0];
1920
2021
  // Iterate in the reverse order
1921
2022
  for (let i = versionsList.length - 1; i > 0; i--) {
1922
- let rv = versionsList[i];
2023
+ const rv = versionsList[i];
1923
2024
  if (satisfies(coerce(rv), versionSpecifiers, true)) {
1924
2025
  return rv;
1925
2026
  }
@@ -1939,7 +2040,7 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
1939
2040
  return pkgList;
1940
2041
  }
1941
2042
  const PYPI_URL = "https://pypi.org/pypi/";
1942
- let cdepList = [];
2043
+ const cdepList = [];
1943
2044
  for (const p of pkgList) {
1944
2045
  if (!p || !p.name) {
1945
2046
  continue;
@@ -1973,7 +2074,7 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
1973
2074
  p.description = body.info.summary;
1974
2075
  p.license = findLicenseId(body.info.license);
1975
2076
  if (body.info.home_page) {
1976
- if (body.info.home_page.indexOf("git") > -1) {
2077
+ if (body.info.home_page.includes("git")) {
1977
2078
  p.repository = { url: body.info.home_page };
1978
2079
  } else {
1979
2080
  p.homepage = { url: body.info.home_page };
@@ -2003,11 +2104,39 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
2003
2104
  Object.keys(body.releases || {}),
2004
2105
  versionSpecifiers
2005
2106
  );
2107
+ // Indicate the confidence with our guess
2108
+ p.evidence = {
2109
+ identity: {
2110
+ field: "version",
2111
+ confidence: 0.75,
2112
+ methods: [
2113
+ {
2114
+ technique: "manifest-analysis",
2115
+ confidence: 0.75,
2116
+ value: `Version specifiers: ${versionSpecifiers}`
2117
+ }
2118
+ ]
2119
+ }
2120
+ };
2006
2121
  }
2007
2122
  // If we have reached here, it means we have not solved the version
2008
2123
  // So assume latest
2009
2124
  if (!p.version) {
2010
2125
  p.version = body.info.version;
2126
+ // Indicate the low confidence
2127
+ p.evidence = {
2128
+ identity: {
2129
+ field: "version",
2130
+ confidence: 0.5,
2131
+ methods: [
2132
+ {
2133
+ technique: "source-code-analysis",
2134
+ confidence: 0.5,
2135
+ value: `PyPI package: ${p.name}`
2136
+ }
2137
+ ]
2138
+ }
2139
+ };
2011
2140
  }
2012
2141
  } else if (p.version !== body.info.version) {
2013
2142
  if (!p.properties) {
@@ -2044,16 +2173,21 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
2044
2173
  );
2045
2174
  }
2046
2175
  p.version = "latest";
2176
+ // Indicate the low confidence
2177
+ p.evidence = {
2178
+ identity: {
2179
+ field: "version",
2180
+ confidence: 0,
2181
+ methods: [
2182
+ {
2183
+ technique: "source-code-analysis",
2184
+ confidence: 0,
2185
+ value: `Module ${p.name}`
2186
+ }
2187
+ ]
2188
+ }
2189
+ };
2047
2190
  }
2048
- // Add a property to let the downstream tools know about this assumption
2049
- // FIXME: Do this correctly with 1.5
2050
- if (!p.properties) {
2051
- p.properties = [];
2052
- }
2053
- p.properties.push({
2054
- name: "cdx:pypi:pedigree",
2055
- value: "unknown"
2056
- });
2057
2191
  cdepList.push(p);
2058
2192
  }
2059
2193
  }
@@ -2099,7 +2233,7 @@ export const parsePiplockData = async function (lockData) {
2099
2233
  Object.keys(depBlock).forEach((p) => {
2100
2234
  const pkg = depBlock[p];
2101
2235
  if (Object.prototype.hasOwnProperty.call(pkg, "version")) {
2102
- let versionStr = pkg.version.replace("==", "");
2236
+ const versionStr = pkg.version.replace("==", "");
2103
2237
  pkgList.push({ name: p, version: versionStr });
2104
2238
  }
2105
2239
  });
@@ -2184,7 +2318,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2184
2318
  versionStr = null;
2185
2319
  }
2186
2320
  if (!tmpA[0].includes("=") && !tmpA[0].trim().includes(" ")) {
2187
- let name = tmpA[0].trim().replace(";", "");
2321
+ const name = tmpA[0].trim().replace(";", "");
2188
2322
  if (!PYTHON_STD_MODULES.includes(name)) {
2189
2323
  pkgList.push({
2190
2324
  name,
@@ -2194,8 +2328,8 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2194
2328
  }
2195
2329
  }
2196
2330
  } else if (l.includes("<") && l.includes(">")) {
2197
- let tmpA = l.split(">");
2198
- let name = tmpA[0].trim().replace(";", "");
2331
+ const tmpA = l.split(">");
2332
+ const name = tmpA[0].trim().replace(";", "");
2199
2333
  const versionSpecifiers = l.replace(name, "");
2200
2334
  if (!PYTHON_STD_MODULES.includes(name)) {
2201
2335
  pkgList.push({
@@ -2216,7 +2350,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2216
2350
  tmpA = tmpA.split("#")[0];
2217
2351
  }
2218
2352
  if (!tmpA[0].trim().includes(" ")) {
2219
- let name = tmpA[0].trim().replace(";", "");
2353
+ const name = tmpA[0].trim().replace(";", "");
2220
2354
  const versionSpecifiers = l.replace(name, "");
2221
2355
  if (!PYTHON_STD_MODULES.includes(name)) {
2222
2356
  pkgList.push({
@@ -2237,9 +2371,9 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2237
2371
  l = l.split("#")[0];
2238
2372
  }
2239
2373
  l = l.trim();
2240
- let tmpA = l.split(/(<|>)/);
2374
+ const tmpA = l.split(/(<|>)/);
2241
2375
  if (tmpA && tmpA.length === 3) {
2242
- let name = tmpA[0].trim().replace(";", "");
2376
+ const name = tmpA[0].trim().replace(";", "");
2243
2377
  const versionSpecifiers = l.replace(name, "");
2244
2378
  if (!PYTHON_STD_MODULES.includes(name)) {
2245
2379
  pkgList.push({
@@ -2255,7 +2389,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2255
2389
  });
2256
2390
  }
2257
2391
  } else if (!l.includes(" ")) {
2258
- let name = l.replace(";", "");
2392
+ const name = l.replace(";", "");
2259
2393
  const versionSpecifiers = l.replace(name, "");
2260
2394
  if (!PYTHON_STD_MODULES.includes(name)) {
2261
2395
  pkgList.push({
@@ -2286,6 +2420,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2286
2420
  */
2287
2421
  export const getPyModules = async (src, epkgList) => {
2288
2422
  const allImports = {};
2423
+ const dependenciesList = [];
2289
2424
  const modList = findAppModules(src, "python");
2290
2425
  const pyDefaultModules = new Set(PYTHON_STD_MODULES);
2291
2426
  const filteredModList = modList.filter(
@@ -2323,7 +2458,13 @@ export const getPyModules = async (src, epkgList) => {
2323
2458
  pkgList = pkgList.filter((p) => !pkgMaps.includes(p.name));
2324
2459
  }
2325
2460
  pkgList = await getPyMetadata(pkgList, true);
2326
- return { allImports, pkgList };
2461
+ for (const p of pkgList) {
2462
+ dependenciesList.push({
2463
+ ref: `pkg:pypi/${p.name}@${p.version}`.toLowerCase(),
2464
+ dependsOn: []
2465
+ });
2466
+ }
2467
+ return { allImports, pkgList, dependenciesList };
2327
2468
  };
2328
2469
 
2329
2470
  /**
@@ -2429,7 +2570,7 @@ export const getRepoLicense = async function (repoUrl, repoMetadata) {
2429
2570
  const group = repoMetadata.group;
2430
2571
  const name = repoMetadata.name;
2431
2572
  if (group && name) {
2432
- for (let akLic of knownLicenses) {
2573
+ for (const akLic of knownLicenses) {
2433
2574
  if (akLic.group === "." && akLic.name === name) {
2434
2575
  return akLic.license;
2435
2576
  } else if (
@@ -2471,7 +2612,7 @@ export const getGoPkgLicense = async function (repoMetadata) {
2471
2612
  }
2472
2613
  const licenseIds = licenses.split(", ");
2473
2614
  const licList = [];
2474
- for (let id of licenseIds) {
2615
+ for (const id of licenseIds) {
2475
2616
  const alicense = {
2476
2617
  id: id
2477
2618
  };
@@ -2554,23 +2695,33 @@ export const parseGoModData = async function (goModData, gosumMap) {
2554
2695
  if (!isModReplacement) {
2555
2696
  // Add group, name and version component properties for required modules
2556
2697
  const version = tmpA[1];
2557
- let gosumHash = gosumMap[`${tmpA[0]}/${version}`];
2698
+ const gosumHash = gosumMap[`${tmpA[0]}/${version}`];
2558
2699
  // The hash for this version was not found in go.sum, so skip as it is most likely being replaced.
2559
2700
  if (gosumHash === undefined) {
2560
2701
  continue;
2561
2702
  }
2562
- let component = await getGoPkgComponent("", tmpA[0], version, gosumHash);
2703
+ const component = await getGoPkgComponent(
2704
+ "",
2705
+ tmpA[0],
2706
+ version,
2707
+ gosumHash
2708
+ );
2563
2709
  pkgComponentsList.push(component);
2564
2710
  } else {
2565
2711
  // Add group, name and version component properties for replacement modules
2566
2712
  const version = tmpA[3];
2567
2713
 
2568
- let gosumHash = gosumMap[`${tmpA[2]}/${version}`];
2714
+ const gosumHash = gosumMap[`${tmpA[2]}/${version}`];
2569
2715
  // The hash for this version was not found in go.sum, so skip.
2570
2716
  if (gosumHash === undefined) {
2571
2717
  continue;
2572
2718
  }
2573
- let component = await getGoPkgComponent("", tmpA[2], version, gosumHash);
2719
+ const component = await getGoPkgComponent(
2720
+ "",
2721
+ tmpA[2],
2722
+ version,
2723
+ gosumHash
2724
+ );
2574
2725
  pkgComponentsList.push(component);
2575
2726
  }
2576
2727
  }
@@ -2590,7 +2741,7 @@ export const parseGoListDep = async function (rawOutput, gosumMap) {
2590
2741
  const deps = [];
2591
2742
  const keys_cache = {};
2592
2743
  const pkgs = rawOutput.split("\n");
2593
- for (let l of pkgs) {
2744
+ for (const l of pkgs) {
2594
2745
  const verArr = l.trim().replace(new RegExp("[\"']", "g"), "").split(" ");
2595
2746
 
2596
2747
  if (verArr && verArr.length === 5) {
@@ -2599,8 +2750,8 @@ export const parseGoListDep = async function (rawOutput, gosumMap) {
2599
2750
  if (!keys_cache[key]) {
2600
2751
  keys_cache[key] = key;
2601
2752
  const version = verArr[1];
2602
- let gosumHash = gosumMap[`${verArr[0]}/${version}`];
2603
- let component = await getGoPkgComponent(
2753
+ const gosumHash = gosumMap[`${verArr[0]}/${version}`];
2754
+ const component = await getGoPkgComponent(
2604
2755
  "",
2605
2756
  verArr[0],
2606
2757
  version,
@@ -2655,7 +2806,7 @@ export const parseGosumData = async function (gosumData) {
2655
2806
  return pkgList;
2656
2807
  }
2657
2808
  const pkgs = gosumData.split("\n");
2658
- for (let l of pkgs) {
2809
+ for (const l of pkgs) {
2659
2810
  // look for lines containing go.mod
2660
2811
  if (l.indexOf("go.mod") > -1) {
2661
2812
  const tmpA = l.split(" ");
@@ -2693,7 +2844,7 @@ export const parseGopkgData = async function (gopkgData) {
2693
2844
  }
2694
2845
  let pkg = null;
2695
2846
  const pkgs = gopkgData.split("\n");
2696
- for (let l of pkgs) {
2847
+ for (const l of pkgs) {
2697
2848
  let key = null;
2698
2849
  let value = null;
2699
2850
  if (l.indexOf("[[projects]]") > -1) {
@@ -2741,7 +2892,7 @@ export const parseGoVersionData = async function (buildInfoData) {
2741
2892
  return pkgList;
2742
2893
  }
2743
2894
  const pkgs = buildInfoData.split("\n");
2744
- for (let i in pkgs) {
2895
+ for (const i in pkgs) {
2745
2896
  const l = pkgs[i].trim().replace(/\t/g, " ");
2746
2897
  if (!l.startsWith("dep")) {
2747
2898
  continue;
@@ -2755,7 +2906,7 @@ export const parseGoVersionData = async function (buildInfoData) {
2755
2906
  if (tmpA.length == 4) {
2756
2907
  hash = tmpA[tmpA.length - 1].replace("h1:", "sha256-");
2757
2908
  }
2758
- let component = await getGoPkgComponent("", name, tmpA[2].trim(), hash);
2909
+ const component = await getGoPkgComponent("", name, tmpA[2].trim(), hash);
2759
2910
  pkgList.push(component);
2760
2911
  }
2761
2912
  return pkgList;
@@ -2958,7 +3109,7 @@ export const getDartMetadata = async function (pkgList) {
2958
3109
  });
2959
3110
  if (res && res.body) {
2960
3111
  const versions = res.body.versions;
2961
- for (let v of versions) {
3112
+ for (const v of versions) {
2962
3113
  if (p.version === v.version) {
2963
3114
  const pubspec = v.pubspec;
2964
3115
  p.description = pubspec.description;
@@ -2982,7 +3133,7 @@ export const getDartMetadata = async function (pkgList) {
2982
3133
  };
2983
3134
 
2984
3135
  export const parseCargoTomlData = async function (cargoData) {
2985
- let pkgList = [];
3136
+ const pkgList = [];
2986
3137
  if (!cargoData) {
2987
3138
  return pkgList;
2988
3139
  }
@@ -3031,7 +3182,7 @@ export const parseCargoTomlData = async function (cargoData) {
3031
3182
  pkgList.push(pkg);
3032
3183
  }
3033
3184
  pkg = undefined;
3034
- let tmpA = l.split(" = ");
3185
+ const tmpA = l.split(" = ");
3035
3186
  let tmpB = undefined;
3036
3187
  let name = tmpA[0];
3037
3188
  let version = undefined;
@@ -3179,7 +3330,7 @@ export const parsePubLockData = async function (pubLockData) {
3179
3330
  }
3180
3331
  };
3181
3332
 
3182
- export const parsePubYamlData = async function (pubYamlData) {
3333
+ export const parsePubYamlData = function (pubYamlData) {
3183
3334
  const pkgList = [];
3184
3335
  let yamlObj = undefined;
3185
3336
  try {
@@ -3199,7 +3350,7 @@ export const parsePubYamlData = async function (pubYamlData) {
3199
3350
  return pkgList;
3200
3351
  };
3201
3352
 
3202
- export const parseHelmYamlData = async function (helmData) {
3353
+ export const parseHelmYamlData = function (helmData) {
3203
3354
  const pkgList = [];
3204
3355
  let yamlObj = undefined;
3205
3356
  try {
@@ -3211,7 +3362,7 @@ export const parseHelmYamlData = async function (helmData) {
3211
3362
  return pkgList;
3212
3363
  }
3213
3364
  if (yamlObj.name && yamlObj.version) {
3214
- let pkg = {
3365
+ const pkg = {
3215
3366
  name: yamlObj.name,
3216
3367
  description: yamlObj.description || "",
3217
3368
  version: yamlObj.version
@@ -3223,7 +3374,7 @@ export const parseHelmYamlData = async function (helmData) {
3223
3374
  }
3224
3375
  if (yamlObj.dependencies) {
3225
3376
  for (const hd of yamlObj.dependencies) {
3226
- let pkg = {
3377
+ const pkg = {
3227
3378
  name: hd.name,
3228
3379
  version: hd.version // This could have * so not precise
3229
3380
  };
@@ -3238,7 +3389,7 @@ export const parseHelmYamlData = async function (helmData) {
3238
3389
  for (const key of Object.keys(yamlObj.entries[he])) {
3239
3390
  const hd = yamlObj.entries[he][key];
3240
3391
  if (hd.name && hd.version) {
3241
- let pkg = {
3392
+ const pkg = {
3242
3393
  name: hd.name,
3243
3394
  version: hd.version,
3244
3395
  description: hd.description || ""
@@ -3327,7 +3478,7 @@ export const recurseImageNameLookup = (keyValueObj, pkgList, imgList) => {
3327
3478
  return imgList;
3328
3479
  };
3329
3480
 
3330
- export const parseContainerSpecData = async function (dcData) {
3481
+ export const parseContainerSpecData = function (dcData) {
3331
3482
  const pkgList = [];
3332
3483
  const imgList = [];
3333
3484
  if (!dcData.includes("image") && !dcData.includes("kind")) {
@@ -3397,7 +3548,7 @@ export const parseContainerSpecData = async function (dcData) {
3397
3548
  export const identifyFlow = function (processingObj) {
3398
3549
  let flow = "unknown";
3399
3550
  if (processingObj.sinkId) {
3400
- let sinkId = processingObj.sinkId.toLowerCase();
3551
+ const sinkId = processingObj.sinkId.toLowerCase();
3401
3552
  if (sinkId.endsWith("write")) {
3402
3553
  flow = "inbound";
3403
3554
  } else if (sinkId.endsWith("read")) {
@@ -3427,7 +3578,7 @@ export const parsePrivadoFile = function (f) {
3427
3578
  return servlist;
3428
3579
  }
3429
3580
  const jsonData = JSON.parse(pData);
3430
- let aservice = {
3581
+ const aservice = {
3431
3582
  "x-trust-boundary": false,
3432
3583
  properties: [],
3433
3584
  data: [],
@@ -3472,9 +3623,9 @@ export const parsePrivadoFile = function (f) {
3472
3623
  // Find endpoints
3473
3624
  if (jsonData.collections) {
3474
3625
  const endpoints = [];
3475
- for (let c of jsonData.collections) {
3476
- for (let occ of c.collections) {
3477
- for (let e of occ.occurrences) {
3626
+ for (const c of jsonData.collections) {
3627
+ for (const occ of c.collections) {
3628
+ for (const e of occ.occurrences) {
3478
3629
  if (e.endPoint) {
3479
3630
  endpoints.push(e.endPoint);
3480
3631
  }
@@ -3485,7 +3636,7 @@ export const parsePrivadoFile = function (f) {
3485
3636
  }
3486
3637
  // Capture violations
3487
3638
  if (jsonData.violations) {
3488
- for (let v of jsonData.violations) {
3639
+ for (const v of jsonData.violations) {
3489
3640
  aservice.properties.push({
3490
3641
  name: "privado_violations",
3491
3642
  value: v.policyId
@@ -3505,7 +3656,7 @@ export const parsePrivadoFile = function (f) {
3505
3656
  return servlist;
3506
3657
  };
3507
3658
 
3508
- export const parseOpenapiSpecData = async function (oaData) {
3659
+ export const parseOpenapiSpecData = function (oaData) {
3509
3660
  const servlist = [];
3510
3661
  if (!oaData) {
3511
3662
  return servlist;
@@ -3555,7 +3706,7 @@ export const parseOpenapiSpecData = async function (oaData) {
3555
3706
  return servlist;
3556
3707
  };
3557
3708
 
3558
- export const parseCabalData = async function (cabalData) {
3709
+ export const parseCabalData = function (cabalData) {
3559
3710
  const pkgList = [];
3560
3711
  if (!cabalData) {
3561
3712
  return pkgList;
@@ -3582,7 +3733,7 @@ export const parseCabalData = async function (cabalData) {
3582
3733
  return pkgList;
3583
3734
  };
3584
3735
 
3585
- export const parseMixLockData = async function (mixData) {
3736
+ export const parseMixLockData = function (mixData) {
3586
3737
  const pkgList = [];
3587
3738
  if (!mixData) {
3588
3739
  return pkgList;
@@ -3608,7 +3759,7 @@ export const parseMixLockData = async function (mixData) {
3608
3759
  return pkgList;
3609
3760
  };
3610
3761
 
3611
- export const parseGitHubWorkflowData = async function (ghwData) {
3762
+ export const parseGitHubWorkflowData = function (ghwData) {
3612
3763
  const pkgList = [];
3613
3764
  const keys_cache = {};
3614
3765
  if (!ghwData) {
@@ -3650,7 +3801,7 @@ export const parseGitHubWorkflowData = async function (ghwData) {
3650
3801
  return pkgList;
3651
3802
  };
3652
3803
 
3653
- export const parseCloudBuildData = async function (cbwData) {
3804
+ export const parseCloudBuildData = function (cbwData) {
3654
3805
  const pkgList = [];
3655
3806
  const keys_cache = {};
3656
3807
  if (!cbwData) {
@@ -3666,7 +3817,7 @@ export const parseCloudBuildData = async function (cbwData) {
3666
3817
  const tmpA = step.name.split(":");
3667
3818
  if (tmpA.length === 2) {
3668
3819
  let group = dirname(tmpA[0]);
3669
- let name = basename(tmpA[0]);
3820
+ const name = basename(tmpA[0]);
3670
3821
  if (group === ".") {
3671
3822
  group = "";
3672
3823
  }
@@ -3687,7 +3838,7 @@ export const parseCloudBuildData = async function (cbwData) {
3687
3838
  return pkgList;
3688
3839
  };
3689
3840
 
3690
- export const parseConanLockData = async function (conanLockData) {
3841
+ export const parseConanLockData = function (conanLockData) {
3691
3842
  const pkgList = [];
3692
3843
  if (!conanLockData) {
3693
3844
  return pkgList;
@@ -3697,7 +3848,7 @@ export const parseConanLockData = async function (conanLockData) {
3697
3848
  return pkgList;
3698
3849
  }
3699
3850
  const nodes = graphLock.graph_lock.nodes;
3700
- for (let nk of Object.keys(nodes)) {
3851
+ for (const nk of Object.keys(nodes)) {
3701
3852
  if (nodes[nk].ref) {
3702
3853
  const tmpA = nodes[nk].ref.split("/");
3703
3854
  if (tmpA.length === 2) {
@@ -3708,7 +3859,7 @@ export const parseConanLockData = async function (conanLockData) {
3708
3859
  return pkgList;
3709
3860
  };
3710
3861
 
3711
- export const parseConanData = async function (conanData) {
3862
+ export const parseConanData = function (conanData) {
3712
3863
  const pkgList = [];
3713
3864
  if (!conanData) {
3714
3865
  return pkgList;
@@ -3737,7 +3888,7 @@ export const parseLeiningenData = function (leinData) {
3737
3888
  leinData = "(defproject" + tmpArr[1];
3738
3889
  }
3739
3890
  const ednData = parseEDNString(leinData);
3740
- for (let k of Object.keys(ednData)) {
3891
+ for (const k of Object.keys(ednData)) {
3741
3892
  if (k === "list") {
3742
3893
  ednData[k].forEach((jk) => {
3743
3894
  if (Array.isArray(jk)) {
@@ -3768,7 +3919,7 @@ export const parseEdnData = function (rawEdnData) {
3768
3919
  }
3769
3920
  const ednData = parseEDNString(rawEdnData);
3770
3921
  const pkgCache = {};
3771
- for (let k of Object.keys(ednData)) {
3922
+ for (const k of Object.keys(ednData)) {
3772
3923
  if (k === "map") {
3773
3924
  ednData[k].forEach((jk) => {
3774
3925
  if (Array.isArray(jk)) {
@@ -3815,7 +3966,7 @@ export const parseEdnData = function (rawEdnData) {
3815
3966
 
3816
3967
  export const parseNupkg = async function (nupkgFile) {
3817
3968
  const pkgList = [];
3818
- let pkg = { group: "" };
3969
+ const pkg = { group: "" };
3819
3970
  let nuspecData = await readZipEntry(nupkgFile, ".nuspec");
3820
3971
  // Remove byte order mark
3821
3972
  if (nuspecData.charCodeAt(0) === 0xfeff) {
@@ -3850,6 +4001,19 @@ export const parseNupkg = async function (nupkgFile) {
3850
4001
  value: nupkgFile
3851
4002
  }
3852
4003
  ];
4004
+ pkg.evidence = {
4005
+ identity: {
4006
+ field: "purl",
4007
+ confidence: 1,
4008
+ methods: [
4009
+ {
4010
+ technique: "binary-analysis",
4011
+ confidence: 1,
4012
+ value: nupkgFile
4013
+ }
4014
+ ]
4015
+ }
4016
+ };
3853
4017
  pkgList.push(pkg);
3854
4018
  if (fetchLicenses) {
3855
4019
  return await getNugetMetadata(pkgList);
@@ -3875,9 +4039,9 @@ export const parseCsPkgData = async function (pkgData) {
3875
4039
  return pkgList;
3876
4040
  }
3877
4041
  packages = packages[0].package;
3878
- for (let i in packages) {
4042
+ for (const i in packages) {
3879
4043
  const p = packages[i].$;
3880
- let pkg = { group: "" };
4044
+ const pkg = { group: "" };
3881
4045
  pkg.name = p.id;
3882
4046
  pkg.version = p.version;
3883
4047
  pkgList.push(pkg);
@@ -3907,12 +4071,12 @@ export const parseCsProjData = async function (csProjData) {
3907
4071
  }
3908
4072
  const project = projects[0];
3909
4073
  if (project.ItemGroup && project.ItemGroup.length) {
3910
- for (let i in project.ItemGroup) {
4074
+ for (const i in project.ItemGroup) {
3911
4075
  const item = project.ItemGroup[i];
3912
4076
  // .net core use PackageReference
3913
- for (let j in item.PackageReference) {
4077
+ for (const j in item.PackageReference) {
3914
4078
  const pref = item.PackageReference[j].$;
3915
- let pkg = { group: "" };
4079
+ const pkg = { group: "" };
3916
4080
  if (!pref.Include || pref.Include.includes(".csproj")) {
3917
4081
  continue;
3918
4082
  }
@@ -3921,9 +4085,9 @@ export const parseCsProjData = async function (csProjData) {
3921
4085
  pkgList.push(pkg);
3922
4086
  }
3923
4087
  // .net framework use Reference
3924
- for (let j in item.Reference) {
4088
+ for (const j in item.Reference) {
3925
4089
  const pref = item.Reference[j].$;
3926
- let pkg = { group: "" };
4090
+ const pkg = { group: "" };
3927
4091
  if (!pref.Include || pref.Include.includes(".csproj")) {
3928
4092
  continue;
3929
4093
  }
@@ -3953,7 +4117,7 @@ export const parseCsProjAssetsData = async function (csProjData) {
3953
4117
  if (!assetData || !assetData.libraries) {
3954
4118
  return pkgList;
3955
4119
  }
3956
- for (let alib of Object.keys(assetData.libraries)) {
4120
+ for (const alib of Object.keys(assetData.libraries)) {
3957
4121
  // Skip os runtime packages
3958
4122
  if (alib.startsWith("runtime")) {
3959
4123
  continue;
@@ -3992,8 +4156,8 @@ export const parseCsPkgLockData = async function (csLockData) {
3992
4156
  if (!assetData || !assetData.dependencies) {
3993
4157
  return pkgList;
3994
4158
  }
3995
- for (let aversion of Object.keys(assetData.dependencies)) {
3996
- for (let alib of Object.keys(assetData.dependencies[aversion])) {
4159
+ for (const aversion of Object.keys(assetData.dependencies)) {
4160
+ for (const alib of Object.keys(assetData.dependencies[aversion])) {
3997
4161
  const libData = assetData.dependencies[aversion][alib];
3998
4162
  pkg = {
3999
4163
  group: "",
@@ -4135,15 +4299,15 @@ export const parseComposerLock = function (pkgLockFile) {
4135
4299
  return [];
4136
4300
  }
4137
4301
  if (lockData) {
4138
- let packages = {};
4302
+ const packages = {};
4139
4303
  if (lockData["packages"]) {
4140
4304
  packages["required"] = lockData["packages"];
4141
4305
  }
4142
4306
  if (lockData["packages-dev"]) {
4143
4307
  packages["optional"] = lockData["packages-dev"];
4144
4308
  }
4145
- for (let compScope in packages) {
4146
- for (let i in packages[compScope]) {
4309
+ for (const compScope in packages) {
4310
+ for (const i in packages[compScope]) {
4147
4311
  const pkg = packages[compScope][i];
4148
4312
  // Be extra cautious. Potential fix for #236
4149
4313
  if (!pkg || !pkg.name || !pkg.version) {
@@ -4153,7 +4317,7 @@ export const parseComposerLock = function (pkgLockFile) {
4153
4317
  if (group === ".") {
4154
4318
  group = "";
4155
4319
  }
4156
- let name = basename(pkg.name);
4320
+ const name = basename(pkg.name);
4157
4321
  pkgList.push({
4158
4322
  group: group,
4159
4323
  name: name,
@@ -4171,7 +4335,20 @@ export const parseComposerLock = function (pkgLockFile) {
4171
4335
  name: "SrcFile",
4172
4336
  value: pkgLockFile
4173
4337
  }
4174
- ]
4338
+ ],
4339
+ evidence: {
4340
+ identity: {
4341
+ field: "purl",
4342
+ confidence: 1,
4343
+ methods: [
4344
+ {
4345
+ technique: "manifest-analysis",
4346
+ confidence: 1,
4347
+ value: pkgLockFile
4348
+ }
4349
+ ]
4350
+ }
4351
+ }
4175
4352
  });
4176
4353
  }
4177
4354
  }
@@ -4190,7 +4367,7 @@ export const parseSbtLock = function (pkgLockFile) {
4190
4367
  if (existsSync(pkgLockFile)) {
4191
4368
  const lockData = JSON.parse(readFileSync(pkgLockFile, "utf8"));
4192
4369
  if (lockData && lockData.dependencies) {
4193
- for (let pkg of lockData.dependencies) {
4370
+ for (const pkg of lockData.dependencies) {
4194
4371
  const artifacts = pkg.artifacts || undefined;
4195
4372
  let integrity = "";
4196
4373
  if (artifacts && artifacts.length) {
@@ -4215,7 +4392,20 @@ export const parseSbtLock = function (pkgLockFile) {
4215
4392
  name: "SrcFile",
4216
4393
  value: pkgLockFile
4217
4394
  }
4218
- ]
4395
+ ],
4396
+ evidence: {
4397
+ identity: {
4398
+ field: "purl",
4399
+ confidence: 1,
4400
+ methods: [
4401
+ {
4402
+ technique: "manifest-analysis",
4403
+ confidence: 1,
4404
+ value: pkgLockFile
4405
+ }
4406
+ ]
4407
+ }
4408
+ }
4219
4409
  });
4220
4410
  }
4221
4411
  }
@@ -4240,15 +4430,15 @@ export const convertOSQueryResults = function (
4240
4430
  if (res.version) {
4241
4431
  const version = res.version;
4242
4432
  let name = res.name || res.device_id;
4243
- let group = "";
4244
- let subpath = res.path || res.admindir || res.source;
4245
- let publisher = res.maintainer || res.creator;
4433
+ const group = "";
4434
+ const subpath = res.path || res.admindir || res.source;
4435
+ const publisher = res.maintainer || res.creator;
4246
4436
  let scope = undefined;
4247
- let compScope = res.priority;
4437
+ const compScope = res.priority;
4248
4438
  if (["required", "optional", "excluded"].includes(compScope)) {
4249
4439
  scope = compScope;
4250
4440
  }
4251
- let description =
4441
+ const description =
4252
4442
  res.description ||
4253
4443
  res.arguments ||
4254
4444
  res.device ||
@@ -4293,7 +4483,7 @@ export const _swiftDepPkgList = (
4293
4483
  jsonData
4294
4484
  ) => {
4295
4485
  if (jsonData && jsonData.dependencies) {
4296
- for (let adep of jsonData.dependencies) {
4486
+ for (const adep of jsonData.dependencies) {
4297
4487
  const urlOrPath = adep.url || adep.path;
4298
4488
  const apkg = {
4299
4489
  group: adep.identity || "",
@@ -4333,7 +4523,7 @@ export const _swiftDepPkgList = (
4333
4523
  // Handle the immediate dependencies before recursing
4334
4524
  if (adep.dependencies && adep.dependencies.length) {
4335
4525
  const deplist = [];
4336
- for (let cdep of adep.dependencies) {
4526
+ for (const cdep of adep.dependencies) {
4337
4527
  const deppurl = new PackageURL(
4338
4528
  "swift",
4339
4529
  cdep.identity || "",
@@ -4380,7 +4570,7 @@ export const parseSwiftJsonTree = (rawOutput, pkgFile) => {
4380
4570
  }
4381
4571
  const pkgList = [];
4382
4572
  const dependenciesList = [];
4383
- let depKeys = {};
4573
+ const depKeys = {};
4384
4574
  let rootPkg = {};
4385
4575
  let jsonData = {};
4386
4576
  try {
@@ -4475,7 +4665,20 @@ export const parseSwiftResolved = (resolvedFile) => {
4475
4665
  name: "SrcFile",
4476
4666
  value: resolvedFile
4477
4667
  }
4478
- ]
4668
+ ],
4669
+ evidence: {
4670
+ identity: {
4671
+ field: "purl",
4672
+ confidence: 1,
4673
+ methods: [
4674
+ {
4675
+ technique: "manifest-analysis",
4676
+ confidence: 1,
4677
+ value: resolvedFile
4678
+ }
4679
+ ]
4680
+ }
4681
+ }
4479
4682
  };
4480
4683
  const repLocation = adep.location || adep.repositoryURL;
4481
4684
  if (repLocation) {
@@ -4497,7 +4700,7 @@ export const parseSwiftResolved = (resolvedFile) => {
4497
4700
  * @param {string} basePath Path to the maven project
4498
4701
  */
4499
4702
  export const collectMvnDependencies = function (mavenCmd, basePath) {
4500
- let tempDir = mkdtempSync(join(tmpdir(), "mvn-deps-"));
4703
+ const tempDir = mkdtempSync(join(tmpdir(), "mvn-deps-"));
4501
4704
  console.log(
4502
4705
  `Executing 'mvn dependency:copy-dependencies -DoutputDirectory=${tempDir} -DexcludeTransitive=true -DincludeScope=runtime' in ${basePath}`
4503
4706
  );
@@ -4555,7 +4758,7 @@ export const collectJarNS = function (jarPath) {
4555
4758
  // Execute jar tvf to get class names
4556
4759
  const jarFiles = getAllFiles(jarPath, "**/*.jar");
4557
4760
  if (jarFiles && jarFiles.length) {
4558
- for (let jf of jarFiles) {
4761
+ for (const jf of jarFiles) {
4559
4762
  const jarname = basename(jf);
4560
4763
  if (DEBUG_MODE) {
4561
4764
  console.log(`Executing 'jar tf ${jf}'`);
@@ -4659,7 +4862,7 @@ export { _encodeForPurl as encodeForPurl };
4659
4862
  * @return pkgList Package list
4660
4863
  */
4661
4864
  export const extractJarArchive = function (jarFile, tempDir) {
4662
- let pkgList = [];
4865
+ const pkgList = [];
4663
4866
  let jarFiles = [];
4664
4867
  const fname = basename(jarFile);
4665
4868
  let pomname = undefined;
@@ -4674,7 +4877,7 @@ export const extractJarArchive = function (jarFile, tempDir) {
4674
4877
  copyFileSync(jarFile, join(tempDir, fname), constants.COPYFILE_FICLONE);
4675
4878
  }
4676
4879
  if (jarFile.endsWith(".war") || jarFile.endsWith(".hpi")) {
4677
- let jarResult = spawnSync("jar", ["-xf", join(tempDir, fname)], {
4880
+ const jarResult = spawnSync("jar", ["-xf", join(tempDir, fname)], {
4678
4881
  encoding: "utf-8",
4679
4882
  cwd: tempDir
4680
4883
  });
@@ -4693,7 +4896,7 @@ export const extractJarArchive = function (jarFile, tempDir) {
4693
4896
  jarFiles = [join(tempDir, fname)];
4694
4897
  }
4695
4898
  if (jarFiles && jarFiles.length) {
4696
- for (let jf of jarFiles) {
4899
+ for (const jf of jarFiles) {
4697
4900
  pomname = jf.replace(".jar", ".pom");
4698
4901
  const jarname = basename(jf);
4699
4902
  // Ignore test jars
@@ -4703,7 +4906,7 @@ export const extractJarArchive = function (jarFile, tempDir) {
4703
4906
  ) {
4704
4907
  continue;
4705
4908
  }
4706
- let manifestDir = join(tempDir, "META-INF");
4909
+ const manifestDir = join(tempDir, "META-INF");
4707
4910
  const manifestFile = join(manifestDir, "MANIFEST.MF");
4708
4911
  let jarResult = {
4709
4912
  status: 1
@@ -4732,32 +4935,42 @@ export const extractJarArchive = function (jarFile, tempDir) {
4732
4935
  jarMetadata["Bundle-Vendor"] ||
4733
4936
  jarMetadata["Automatic-Module-Name"] ||
4734
4937
  "";
4938
+ let version =
4939
+ jarMetadata["Bundle-Version"] ||
4940
+ jarMetadata["Implementation-Version"] ||
4941
+ jarMetadata["Specification-Version"];
4942
+ if (version && version.includes(" ")) {
4943
+ version = version.split(" ")[0];
4944
+ }
4735
4945
  let name = "";
4946
+ // Prefer jar filename to construct name and version
4947
+ if (!name || !version || name === "" || version === "") {
4948
+ const tmpA = jarname.split("-");
4949
+ if (tmpA && tmpA.length > 1) {
4950
+ const lastPart = tmpA[tmpA.length - 1];
4951
+ if (!version || version === "") {
4952
+ version = lastPart.replace(".jar", "");
4953
+ }
4954
+ if (!name || name === "") {
4955
+ name = jarname.replace("-" + lastPart, "") || "";
4956
+ }
4957
+ } else {
4958
+ name = jarname.replace(".jar", "");
4959
+ }
4960
+ }
4736
4961
  if (
4962
+ !name.length &&
4737
4963
  jarMetadata["Bundle-Name"] &&
4738
4964
  !jarMetadata["Bundle-Name"].includes(" ")
4739
4965
  ) {
4740
4966
  name = jarMetadata["Bundle-Name"];
4741
4967
  } else if (
4968
+ !name.length &&
4742
4969
  jarMetadata["Implementation-Title"] &&
4743
4970
  !jarMetadata["Implementation-Title"].includes(" ")
4744
4971
  ) {
4745
4972
  name = jarMetadata["Implementation-Title"];
4746
4973
  }
4747
- let version =
4748
- jarMetadata["Bundle-Version"] ||
4749
- jarMetadata["Implementation-Version"] ||
4750
- jarMetadata["Specification-Version"];
4751
- if (version && version.includes(" ")) {
4752
- version = version.split(" ")[0];
4753
- }
4754
- if (!name && group) {
4755
- name = basename(group.replace(/\./g, "/"));
4756
- if (!group.startsWith("javax")) {
4757
- group = dirname(group.replace(/\./g, "/"));
4758
- group = group.replace(/\//g, ".");
4759
- }
4760
- }
4761
4974
  // Sometimes the group might already contain the name
4762
4975
  // Eg: group: org.checkerframework.checker.qual name: checker-qual
4763
4976
  if (name && group && !group.startsWith("javax")) {
@@ -4773,21 +4986,6 @@ export const extractJarArchive = function (jarFile, tempDir) {
4773
4986
  );
4774
4987
  }
4775
4988
  }
4776
- // Fallback to parsing jar filename
4777
- if (!name || !version || name === "" || version === "") {
4778
- const tmpA = jarname.split("-");
4779
- if (tmpA && tmpA.length > 1) {
4780
- const lastPart = tmpA[tmpA.length - 1];
4781
- if (!version || version === "") {
4782
- version = lastPart.replace(".jar", "");
4783
- }
4784
- if (!name || name === "") {
4785
- name = jarname.replace("-" + lastPart, "") || "";
4786
- }
4787
- } else {
4788
- name = jarname.replace(".jar", "");
4789
- }
4790
- }
4791
4989
  // Patch the group string
4792
4990
  for (const aprefix in vendorAliases) {
4793
4991
  if (name && name.startsWith(aprefix)) {
@@ -4796,10 +4994,27 @@ export const extractJarArchive = function (jarFile, tempDir) {
4796
4994
  }
4797
4995
  }
4798
4996
  if (name && version) {
4997
+ // If group and name are the same we only need the name
4998
+ if (group == name) {
4999
+ group = "";
5000
+ }
4799
5001
  pkgList.push({
4800
5002
  group: group === "." ? "" : encodeForPurl(group || "") || "",
4801
5003
  name: name ? encodeForPurl(name) : "",
4802
5004
  version,
5005
+ evidence: {
5006
+ identity: {
5007
+ field: "purl",
5008
+ confidence: 0.5,
5009
+ methods: [
5010
+ {
5011
+ technique: "filename",
5012
+ confidence: 0.5,
5013
+ value: jarname
5014
+ }
5015
+ ]
5016
+ }
5017
+ },
4803
5018
  properties: [
4804
5019
  {
4805
5020
  name: "SrcFile",
@@ -4841,8 +5056,8 @@ export const extractJarArchive = function (jarFile, tempDir) {
4841
5056
  export const determineSbtVersion = function (projectPath) {
4842
5057
  const buildPropFile = join(projectPath, "project", "build.properties");
4843
5058
  if (existsSync(buildPropFile)) {
4844
- let properties = propertiesReader(buildPropFile);
4845
- let property = properties.get("sbt.version");
5059
+ const properties = propertiesReader(buildPropFile);
5060
+ const property = properties.get("sbt.version");
4846
5061
  if (property != null && valid(property)) {
4847
5062
  return property;
4848
5063
  }
@@ -4864,7 +5079,7 @@ export const determineSbtVersion = function (projectPath) {
4864
5079
  */
4865
5080
  export const addPlugin = function (projectPath, plugin) {
4866
5081
  const pluginsFile = sbtPluginsPath(projectPath);
4867
- var originalPluginsFile = null;
5082
+ let originalPluginsFile = null;
4868
5083
  if (existsSync(pluginsFile)) {
4869
5084
  originalPluginsFile = pluginsFile + ".cdxgen";
4870
5085
  copyFileSync(pluginsFile, originalPluginsFile, constants.COPYFILE_FICLONE);
@@ -4997,6 +5212,12 @@ export const getMavenCommand = (srcPath, rootPath) => {
4997
5212
  let findMavenFile = "mvnw";
4998
5213
  if (platform() == "win32") {
4999
5214
  findMavenFile = "mvnw.bat";
5215
+ if (
5216
+ !existsSync(join(srcPath, findMavenFile)) &&
5217
+ existsSync(join(srcPath, "mvnw.cmd"))
5218
+ ) {
5219
+ findMavenFile = "mvnw.cmd";
5220
+ }
5000
5221
  }
5001
5222
 
5002
5223
  if (existsSync(join(srcPath, findMavenFile))) {
@@ -5071,6 +5292,16 @@ export const executeAtom = (src, args) => {
5071
5292
  timeout: TIMEOUT_MS,
5072
5293
  env
5073
5294
  });
5295
+ if (
5296
+ result.stderr &&
5297
+ result.stderr.includes(
5298
+ "has been compiled by a more recent version of the Java Runtime"
5299
+ )
5300
+ ) {
5301
+ console.log(
5302
+ "Atom requires Java 17 or above. Please install a suitable version and re-run cdxgen to improve the SBoM accuracy.\nAlternatively, use the cdxgen container image."
5303
+ );
5304
+ }
5074
5305
  if (DEBUG_MODE) {
5075
5306
  if (result.stdout) {
5076
5307
  console.log(result.stdout);
@@ -5118,17 +5349,62 @@ export const findAppModules = function (src, language) {
5118
5349
  return retList;
5119
5350
  };
5120
5351
 
5352
+ const flattenDeps = (
5353
+ dependsOn,
5354
+ dependenciesList,
5355
+ pkgList,
5356
+ reqOrSetupFile,
5357
+ t
5358
+ ) => {
5359
+ for (const d of t.dependencies) {
5360
+ const pkgRef = `pkg:pypi/${d.name}@${d.version}`
5361
+ .replace(/_/g, "-")
5362
+ .toLowerCase();
5363
+ dependsOn.push(pkgRef);
5364
+ dependenciesList.push({ ref: pkgRef, dependsOn: [] });
5365
+ pkgList.push({
5366
+ name: d.name,
5367
+ version: d.version,
5368
+ properties: [
5369
+ {
5370
+ name: "SrcFile",
5371
+ value: reqOrSetupFile
5372
+ }
5373
+ ],
5374
+ evidence: {
5375
+ identity: {
5376
+ field: "purl",
5377
+ confidence: 1,
5378
+ methods: [
5379
+ {
5380
+ technique: "manifest-analysis",
5381
+ confidence: 1,
5382
+ value: reqOrSetupFile
5383
+ }
5384
+ ]
5385
+ }
5386
+ }
5387
+ });
5388
+ // Recurse and flatten
5389
+ if (d.dependencies && d.dependencies) {
5390
+ flattenDeps(dependsOn, dependenciesList, pkgList, reqOrSetupFile, d);
5391
+ }
5392
+ }
5393
+ };
5394
+
5121
5395
  /**
5122
- * Execute pip freeze by creating a virtual env in a temp directory
5396
+ * Execute pip freeze by creating a virtual env in a temp directory and construct the dependency tree
5123
5397
  *
5124
5398
  * @param {string} basePath Base path
5125
5399
  * @param {string} reqOrSetupFile Requirements or setup.py file
5400
+ * @param {string} tempVenvDir Temp venv dir
5126
5401
  * @returns List of packages from the virtual env
5127
5402
  */
5128
- export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5129
- let pkgList = [];
5403
+ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
5404
+ const pkgList = [];
5405
+ const rootList = [];
5406
+ const dependenciesList = [];
5130
5407
  let result = undefined;
5131
- const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-venv-"));
5132
5408
  const env = {
5133
5409
  ...process.env
5134
5410
  };
@@ -5138,16 +5414,22 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5138
5414
  * By checking the environment variable "VIRTUAL_ENV" we decide whether to create an env or not
5139
5415
  */
5140
5416
  if (!process.env.VIRTUAL_ENV) {
5141
- result = spawnSync(PYTHON_CMD, ["-m", "venv", tempDir]);
5417
+ result = spawnSync(PYTHON_CMD, ["-m", "venv", tempVenvDir]);
5142
5418
  if (result.status !== 0 || result.error) {
5143
5419
  if (DEBUG_MODE) {
5144
- console.log(result.stderr);
5420
+ console.log("Virtual env creation has failed");
5421
+ if (!result.stderr) {
5422
+ console.log(
5423
+ "Ensure the virtualenv package is installed using pip. `python -m pip install virtualenv`"
5424
+ );
5425
+ }
5145
5426
  }
5146
5427
  } else {
5147
- env.VIRTUAL_ENV = tempDir;
5148
- env.PATH = `${join(tempDir, "bin")}${_delimiter}${
5149
- process.env.PATH || ""
5150
- }`;
5428
+ env.VIRTUAL_ENV = tempVenvDir;
5429
+ env.PATH = `${join(
5430
+ tempVenvDir,
5431
+ platform() == "win32" ? "Scripts" : "bin"
5432
+ )}${_delimiter}${process.env.PATH || ""}`;
5151
5433
  }
5152
5434
  }
5153
5435
  /**
@@ -5156,8 +5438,8 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5156
5438
  * This step is accurate but not reproducible since the resulting list could differ based on various factors
5157
5439
  * such as the version of python, pip, os, pypi.org availability (and weather?)
5158
5440
  */
5159
- if (tempDir === env.VIRTUAL_ENV) {
5160
- let pipInstallArgs = [
5441
+ if (tempVenvDir === env.VIRTUAL_ENV && reqOrSetupFile) {
5442
+ const pipInstallArgs = [
5161
5443
  "-m",
5162
5444
  "pip",
5163
5445
  "install",
@@ -5197,6 +5479,7 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5197
5479
  }
5198
5480
  if (!versionRelatedError && DEBUG_MODE) {
5199
5481
  console.log("args used:", pipInstallArgs);
5482
+ console.log(result.stdout, result.stderr);
5200
5483
  console.log(
5201
5484
  "Possible build errors detected. The resulting list in the SBoM would therefore be incomplete.\nTry installing any missing build tools or development libraries to improve the accuracy."
5202
5485
  );
@@ -5215,43 +5498,42 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5215
5498
  console.log("- Check if any git submodules have to be initialized.");
5216
5499
  }
5217
5500
  }
5501
+ }
5502
+ // Bug #375. Attempt pip freeze on existing and new virtual environments
5503
+ if (env.VIRTUAL_ENV && env.VIRTUAL_ENV.length) {
5218
5504
  /**
5219
5505
  * At this point, the previous attempt to do a pip install might have failed and we might have an unclean virtual environment with an incomplete list
5220
5506
  * The position taken by cdxgen is "Some SBoM is better than no SBoM", so we proceed to collecting the dependencies that got installed with pip freeze
5221
5507
  */
5222
- let pipFreezeArgs = [
5223
- "-m",
5224
- "pip",
5225
- "freeze",
5226
- "-l",
5227
- "--exclude-editable",
5228
- "--disable-pip-version-check"
5229
- ];
5230
- if (
5231
- !reqOrSetupFile.endsWith("setup.py") &&
5232
- !reqOrSetupFile.endsWith("pyproject.toml")
5233
- ) {
5234
- pipFreezeArgs.push("-r");
5235
- pipFreezeArgs.push(resolve(reqOrSetupFile));
5508
+ if (DEBUG_MODE) {
5509
+ console.log(
5510
+ "About to construct the pip dependency tree. Please wait ..."
5511
+ );
5236
5512
  }
5237
- result = spawnSync(PYTHON_CMD, pipFreezeArgs, {
5238
- cwd: basePath,
5239
- encoding: "utf-8",
5240
- timeout: TIMEOUT_MS,
5241
- env
5242
- });
5243
- if (result.status === 0 && result.stdout) {
5244
- // We now a list from pip freeze that could be parsed to obtain a list
5245
- // We need to iterate that this list is not reproducible and could vary based on the environment
5246
- const reqData = Buffer.from(result.stdout).toString();
5247
- const dlist = await parseReqFile(reqData, false);
5248
- if (dlist && dlist.length) {
5249
- pkgList = pkgList.concat(dlist);
5250
- }
5251
- } else if (result.status !== 0 || result.error) {
5252
- if (DEBUG_MODE) {
5253
- console.log(result.stderr, "args used:", pipFreezeArgs);
5513
+ const tree = getTreeWithPlugin(env, PYTHON_CMD, basePath);
5514
+ if (DEBUG_MODE && !tree.length) {
5515
+ console.log(
5516
+ "Dependency tree generation has failed. Please check for any errors or version incompatibilities reported in the logs."
5517
+ );
5518
+ }
5519
+ for (const t of tree) {
5520
+ if (!t.version.length) {
5521
+ continue;
5254
5522
  }
5523
+ pkgList.push({
5524
+ name: t.name.replace(/_/g, "-"),
5525
+ version: t.version
5526
+ });
5527
+ rootList.push({
5528
+ name: t.name.replace(/_/g, "-"),
5529
+ version: t.version
5530
+ });
5531
+ const dependsOn = [];
5532
+ flattenDeps(dependsOn, dependenciesList, pkgList, reqOrSetupFile, t);
5533
+ dependenciesList.push({
5534
+ ref: `pkg:pypi/${t.name}@${t.version}`.replace(/_/g, "-").toLowerCase(),
5535
+ dependsOn
5536
+ });
5255
5537
  }
5256
5538
  } else {
5257
5539
  if (DEBUG_MODE) {
@@ -5260,11 +5542,11 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5260
5542
  );
5261
5543
  }
5262
5544
  }
5263
- // Clean up
5264
- if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
5265
- rmSync(tempDir, { recursive: true, force: true });
5266
- }
5267
- return pkgList;
5545
+ return {
5546
+ pkgList,
5547
+ rootList,
5548
+ dependenciesList
5549
+ };
5268
5550
  };
5269
5551
 
5270
5552
  // taken from a very old package https://github.com/keithamus/parse-packagejson-name/blob/master/index.js