@cyclonedx/cdxgen 9.0.1 → 9.1.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
@@ -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,7 +1721,7 @@ export const executeGradleProperties = function (dir, rootPath, subProject) {
1616
1721
  "plain",
1617
1722
  "--build-cache"
1618
1723
  ];
1619
- let gradleCmd = getGradleCommand(dir, rootPath);
1724
+ const gradleCmd = getGradleCommand(dir, rootPath);
1620
1725
  if (process.env.GRADLE_ARGS) {
1621
1726
  const addArgs = process.env.GRADLE_ARGS.split(" ");
1622
1727
  gradlePropertiesArgs = gradlePropertiesArgs.concat(addArgs);
@@ -1764,7 +1869,7 @@ export const parseKVDep = function (rawOutput) {
1764
1869
  * @param {string} name License full name
1765
1870
  */
1766
1871
  export const findLicenseId = function (name) {
1767
- for (let l of licenseMapping) {
1872
+ for (const l of licenseMapping) {
1768
1873
  if (l.names.includes(name)) {
1769
1874
  return l.exp;
1770
1875
  }
@@ -1781,8 +1886,8 @@ export const findLicenseId = function (name) {
1781
1886
  */
1782
1887
  export const guessLicenseId = function (content) {
1783
1888
  content = content.replace(/\n/g, " ");
1784
- for (let l of licenseMapping) {
1785
- for (let j in l.names) {
1889
+ for (const l of licenseMapping) {
1890
+ for (const j in l.names) {
1786
1891
  if (content.toUpperCase().indexOf(l.names[j].toUpperCase()) > -1) {
1787
1892
  return l.exp;
1788
1893
  }
@@ -1817,7 +1922,7 @@ export const getMvnMetadata = async function (pkgList) {
1817
1922
  if (p.group.indexOf("android") !== -1) {
1818
1923
  urlPrefix = ANDROID_MAVEN;
1819
1924
  }
1820
- let groupPart = p.group.replace(/\./g, "/");
1925
+ const groupPart = p.group.replace(/\./g, "/");
1821
1926
  // Querying maven requires a valid group name
1822
1927
  if (!groupPart || groupPart === "") {
1823
1928
  cdepList.push(p);
@@ -1900,7 +2005,7 @@ export const parsePyRequiresDist = function (dist_string) {
1900
2005
  name = tmpA[0];
1901
2006
  } else if (tmpA.length > 1) {
1902
2007
  name = tmpA[0];
1903
- let tmpVersion = tmpA[1];
2008
+ const tmpVersion = tmpA[1];
1904
2009
  version = tmpVersion.split(",")[0].replace(/[();=&glt><]/g, "");
1905
2010
  }
1906
2011
  return {
@@ -1919,7 +2024,7 @@ export const guessPypiMatchingVersion = (versionsList, versionSpecifiers) => {
1919
2024
  versionSpecifiers = versionSpecifiers.replace(/,/g, " ").split(";")[0];
1920
2025
  // Iterate in the reverse order
1921
2026
  for (let i = versionsList.length - 1; i > 0; i--) {
1922
- let rv = versionsList[i];
2027
+ const rv = versionsList[i];
1923
2028
  if (satisfies(coerce(rv), versionSpecifiers, true)) {
1924
2029
  return rv;
1925
2030
  }
@@ -1939,7 +2044,7 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
1939
2044
  return pkgList;
1940
2045
  }
1941
2046
  const PYPI_URL = "https://pypi.org/pypi/";
1942
- let cdepList = [];
2047
+ const cdepList = [];
1943
2048
  for (const p of pkgList) {
1944
2049
  if (!p || !p.name) {
1945
2050
  continue;
@@ -1973,7 +2078,7 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
1973
2078
  p.description = body.info.summary;
1974
2079
  p.license = findLicenseId(body.info.license);
1975
2080
  if (body.info.home_page) {
1976
- if (body.info.home_page.indexOf("git") > -1) {
2081
+ if (body.info.home_page.includes("git")) {
1977
2082
  p.repository = { url: body.info.home_page };
1978
2083
  } else {
1979
2084
  p.homepage = { url: body.info.home_page };
@@ -2003,11 +2108,39 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
2003
2108
  Object.keys(body.releases || {}),
2004
2109
  versionSpecifiers
2005
2110
  );
2111
+ // Indicate the confidence with our guess
2112
+ p.evidence = {
2113
+ identity: {
2114
+ field: "version",
2115
+ confidence: 0.75,
2116
+ methods: [
2117
+ {
2118
+ technique: "manifest-analysis",
2119
+ confidence: 0.75,
2120
+ value: `Version specifiers: ${versionSpecifiers}`
2121
+ }
2122
+ ]
2123
+ }
2124
+ };
2006
2125
  }
2007
2126
  // If we have reached here, it means we have not solved the version
2008
2127
  // So assume latest
2009
2128
  if (!p.version) {
2010
2129
  p.version = body.info.version;
2130
+ // Indicate the low confidence
2131
+ p.evidence = {
2132
+ identity: {
2133
+ field: "version",
2134
+ confidence: 0.5,
2135
+ methods: [
2136
+ {
2137
+ technique: "source-code-analysis",
2138
+ confidence: 0.5,
2139
+ value: `PyPI package: ${p.name}`
2140
+ }
2141
+ ]
2142
+ }
2143
+ };
2011
2144
  }
2012
2145
  } else if (p.version !== body.info.version) {
2013
2146
  if (!p.properties) {
@@ -2044,16 +2177,21 @@ export const getPyMetadata = async function (pkgList, fetchDepsInfo) {
2044
2177
  );
2045
2178
  }
2046
2179
  p.version = "latest";
2180
+ // Indicate the low confidence
2181
+ p.evidence = {
2182
+ identity: {
2183
+ field: "version",
2184
+ confidence: 0,
2185
+ methods: [
2186
+ {
2187
+ technique: "source-code-analysis",
2188
+ confidence: 0,
2189
+ value: `Module ${p.name}`
2190
+ }
2191
+ ]
2192
+ }
2193
+ };
2047
2194
  }
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
2195
  cdepList.push(p);
2058
2196
  }
2059
2197
  }
@@ -2099,7 +2237,7 @@ export const parsePiplockData = async function (lockData) {
2099
2237
  Object.keys(depBlock).forEach((p) => {
2100
2238
  const pkg = depBlock[p];
2101
2239
  if (Object.prototype.hasOwnProperty.call(pkg, "version")) {
2102
- let versionStr = pkg.version.replace("==", "");
2240
+ const versionStr = pkg.version.replace("==", "");
2103
2241
  pkgList.push({ name: p, version: versionStr });
2104
2242
  }
2105
2243
  });
@@ -2184,7 +2322,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2184
2322
  versionStr = null;
2185
2323
  }
2186
2324
  if (!tmpA[0].includes("=") && !tmpA[0].trim().includes(" ")) {
2187
- let name = tmpA[0].trim().replace(";", "");
2325
+ const name = tmpA[0].trim().replace(";", "");
2188
2326
  if (!PYTHON_STD_MODULES.includes(name)) {
2189
2327
  pkgList.push({
2190
2328
  name,
@@ -2194,8 +2332,8 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2194
2332
  }
2195
2333
  }
2196
2334
  } else if (l.includes("<") && l.includes(">")) {
2197
- let tmpA = l.split(">");
2198
- let name = tmpA[0].trim().replace(";", "");
2335
+ const tmpA = l.split(">");
2336
+ const name = tmpA[0].trim().replace(";", "");
2199
2337
  const versionSpecifiers = l.replace(name, "");
2200
2338
  if (!PYTHON_STD_MODULES.includes(name)) {
2201
2339
  pkgList.push({
@@ -2216,7 +2354,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2216
2354
  tmpA = tmpA.split("#")[0];
2217
2355
  }
2218
2356
  if (!tmpA[0].trim().includes(" ")) {
2219
- let name = tmpA[0].trim().replace(";", "");
2357
+ const name = tmpA[0].trim().replace(";", "");
2220
2358
  const versionSpecifiers = l.replace(name, "");
2221
2359
  if (!PYTHON_STD_MODULES.includes(name)) {
2222
2360
  pkgList.push({
@@ -2237,9 +2375,9 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2237
2375
  l = l.split("#")[0];
2238
2376
  }
2239
2377
  l = l.trim();
2240
- let tmpA = l.split(/(<|>)/);
2378
+ const tmpA = l.split(/(<|>)/);
2241
2379
  if (tmpA && tmpA.length === 3) {
2242
- let name = tmpA[0].trim().replace(";", "");
2380
+ const name = tmpA[0].trim().replace(";", "");
2243
2381
  const versionSpecifiers = l.replace(name, "");
2244
2382
  if (!PYTHON_STD_MODULES.includes(name)) {
2245
2383
  pkgList.push({
@@ -2255,7 +2393,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2255
2393
  });
2256
2394
  }
2257
2395
  } else if (!l.includes(" ")) {
2258
- let name = l.replace(";", "");
2396
+ const name = l.replace(";", "");
2259
2397
  const versionSpecifiers = l.replace(name, "");
2260
2398
  if (!PYTHON_STD_MODULES.includes(name)) {
2261
2399
  pkgList.push({
@@ -2286,6 +2424,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
2286
2424
  */
2287
2425
  export const getPyModules = async (src, epkgList) => {
2288
2426
  const allImports = {};
2427
+ const dependenciesList = [];
2289
2428
  const modList = findAppModules(src, "python");
2290
2429
  const pyDefaultModules = new Set(PYTHON_STD_MODULES);
2291
2430
  const filteredModList = modList.filter(
@@ -2323,7 +2462,13 @@ export const getPyModules = async (src, epkgList) => {
2323
2462
  pkgList = pkgList.filter((p) => !pkgMaps.includes(p.name));
2324
2463
  }
2325
2464
  pkgList = await getPyMetadata(pkgList, true);
2326
- return { allImports, pkgList };
2465
+ for (const p of pkgList) {
2466
+ dependenciesList.push({
2467
+ ref: `pkg:pypi/${p.name}@${p.version}`.toLowerCase(),
2468
+ dependsOn: []
2469
+ });
2470
+ }
2471
+ return { allImports, pkgList, dependenciesList };
2327
2472
  };
2328
2473
 
2329
2474
  /**
@@ -2429,7 +2574,7 @@ export const getRepoLicense = async function (repoUrl, repoMetadata) {
2429
2574
  const group = repoMetadata.group;
2430
2575
  const name = repoMetadata.name;
2431
2576
  if (group && name) {
2432
- for (let akLic of knownLicenses) {
2577
+ for (const akLic of knownLicenses) {
2433
2578
  if (akLic.group === "." && akLic.name === name) {
2434
2579
  return akLic.license;
2435
2580
  } else if (
@@ -2471,7 +2616,7 @@ export const getGoPkgLicense = async function (repoMetadata) {
2471
2616
  }
2472
2617
  const licenseIds = licenses.split(", ");
2473
2618
  const licList = [];
2474
- for (let id of licenseIds) {
2619
+ for (const id of licenseIds) {
2475
2620
  const alicense = {
2476
2621
  id: id
2477
2622
  };
@@ -2554,23 +2699,33 @@ export const parseGoModData = async function (goModData, gosumMap) {
2554
2699
  if (!isModReplacement) {
2555
2700
  // Add group, name and version component properties for required modules
2556
2701
  const version = tmpA[1];
2557
- let gosumHash = gosumMap[`${tmpA[0]}/${version}`];
2702
+ const gosumHash = gosumMap[`${tmpA[0]}/${version}`];
2558
2703
  // The hash for this version was not found in go.sum, so skip as it is most likely being replaced.
2559
2704
  if (gosumHash === undefined) {
2560
2705
  continue;
2561
2706
  }
2562
- let component = await getGoPkgComponent("", tmpA[0], version, gosumHash);
2707
+ const component = await getGoPkgComponent(
2708
+ "",
2709
+ tmpA[0],
2710
+ version,
2711
+ gosumHash
2712
+ );
2563
2713
  pkgComponentsList.push(component);
2564
2714
  } else {
2565
2715
  // Add group, name and version component properties for replacement modules
2566
2716
  const version = tmpA[3];
2567
2717
 
2568
- let gosumHash = gosumMap[`${tmpA[2]}/${version}`];
2718
+ const gosumHash = gosumMap[`${tmpA[2]}/${version}`];
2569
2719
  // The hash for this version was not found in go.sum, so skip.
2570
2720
  if (gosumHash === undefined) {
2571
2721
  continue;
2572
2722
  }
2573
- let component = await getGoPkgComponent("", tmpA[2], version, gosumHash);
2723
+ const component = await getGoPkgComponent(
2724
+ "",
2725
+ tmpA[2],
2726
+ version,
2727
+ gosumHash
2728
+ );
2574
2729
  pkgComponentsList.push(component);
2575
2730
  }
2576
2731
  }
@@ -2590,7 +2745,7 @@ export const parseGoListDep = async function (rawOutput, gosumMap) {
2590
2745
  const deps = [];
2591
2746
  const keys_cache = {};
2592
2747
  const pkgs = rawOutput.split("\n");
2593
- for (let l of pkgs) {
2748
+ for (const l of pkgs) {
2594
2749
  const verArr = l.trim().replace(new RegExp("[\"']", "g"), "").split(" ");
2595
2750
 
2596
2751
  if (verArr && verArr.length === 5) {
@@ -2599,8 +2754,8 @@ export const parseGoListDep = async function (rawOutput, gosumMap) {
2599
2754
  if (!keys_cache[key]) {
2600
2755
  keys_cache[key] = key;
2601
2756
  const version = verArr[1];
2602
- let gosumHash = gosumMap[`${verArr[0]}/${version}`];
2603
- let component = await getGoPkgComponent(
2757
+ const gosumHash = gosumMap[`${verArr[0]}/${version}`];
2758
+ const component = await getGoPkgComponent(
2604
2759
  "",
2605
2760
  verArr[0],
2606
2761
  version,
@@ -2655,7 +2810,7 @@ export const parseGosumData = async function (gosumData) {
2655
2810
  return pkgList;
2656
2811
  }
2657
2812
  const pkgs = gosumData.split("\n");
2658
- for (let l of pkgs) {
2813
+ for (const l of pkgs) {
2659
2814
  // look for lines containing go.mod
2660
2815
  if (l.indexOf("go.mod") > -1) {
2661
2816
  const tmpA = l.split(" ");
@@ -2693,7 +2848,7 @@ export const parseGopkgData = async function (gopkgData) {
2693
2848
  }
2694
2849
  let pkg = null;
2695
2850
  const pkgs = gopkgData.split("\n");
2696
- for (let l of pkgs) {
2851
+ for (const l of pkgs) {
2697
2852
  let key = null;
2698
2853
  let value = null;
2699
2854
  if (l.indexOf("[[projects]]") > -1) {
@@ -2741,7 +2896,7 @@ export const parseGoVersionData = async function (buildInfoData) {
2741
2896
  return pkgList;
2742
2897
  }
2743
2898
  const pkgs = buildInfoData.split("\n");
2744
- for (let i in pkgs) {
2899
+ for (const i in pkgs) {
2745
2900
  const l = pkgs[i].trim().replace(/\t/g, " ");
2746
2901
  if (!l.startsWith("dep")) {
2747
2902
  continue;
@@ -2755,7 +2910,7 @@ export const parseGoVersionData = async function (buildInfoData) {
2755
2910
  if (tmpA.length == 4) {
2756
2911
  hash = tmpA[tmpA.length - 1].replace("h1:", "sha256-");
2757
2912
  }
2758
- let component = await getGoPkgComponent("", name, tmpA[2].trim(), hash);
2913
+ const component = await getGoPkgComponent("", name, tmpA[2].trim(), hash);
2759
2914
  pkgList.push(component);
2760
2915
  }
2761
2916
  return pkgList;
@@ -2958,7 +3113,7 @@ export const getDartMetadata = async function (pkgList) {
2958
3113
  });
2959
3114
  if (res && res.body) {
2960
3115
  const versions = res.body.versions;
2961
- for (let v of versions) {
3116
+ for (const v of versions) {
2962
3117
  if (p.version === v.version) {
2963
3118
  const pubspec = v.pubspec;
2964
3119
  p.description = pubspec.description;
@@ -2982,7 +3137,7 @@ export const getDartMetadata = async function (pkgList) {
2982
3137
  };
2983
3138
 
2984
3139
  export const parseCargoTomlData = async function (cargoData) {
2985
- let pkgList = [];
3140
+ const pkgList = [];
2986
3141
  if (!cargoData) {
2987
3142
  return pkgList;
2988
3143
  }
@@ -3031,7 +3186,7 @@ export const parseCargoTomlData = async function (cargoData) {
3031
3186
  pkgList.push(pkg);
3032
3187
  }
3033
3188
  pkg = undefined;
3034
- let tmpA = l.split(" = ");
3189
+ const tmpA = l.split(" = ");
3035
3190
  let tmpB = undefined;
3036
3191
  let name = tmpA[0];
3037
3192
  let version = undefined;
@@ -3179,7 +3334,7 @@ export const parsePubLockData = async function (pubLockData) {
3179
3334
  }
3180
3335
  };
3181
3336
 
3182
- export const parsePubYamlData = async function (pubYamlData) {
3337
+ export const parsePubYamlData = function (pubYamlData) {
3183
3338
  const pkgList = [];
3184
3339
  let yamlObj = undefined;
3185
3340
  try {
@@ -3199,7 +3354,7 @@ export const parsePubYamlData = async function (pubYamlData) {
3199
3354
  return pkgList;
3200
3355
  };
3201
3356
 
3202
- export const parseHelmYamlData = async function (helmData) {
3357
+ export const parseHelmYamlData = function (helmData) {
3203
3358
  const pkgList = [];
3204
3359
  let yamlObj = undefined;
3205
3360
  try {
@@ -3211,7 +3366,7 @@ export const parseHelmYamlData = async function (helmData) {
3211
3366
  return pkgList;
3212
3367
  }
3213
3368
  if (yamlObj.name && yamlObj.version) {
3214
- let pkg = {
3369
+ const pkg = {
3215
3370
  name: yamlObj.name,
3216
3371
  description: yamlObj.description || "",
3217
3372
  version: yamlObj.version
@@ -3223,7 +3378,7 @@ export const parseHelmYamlData = async function (helmData) {
3223
3378
  }
3224
3379
  if (yamlObj.dependencies) {
3225
3380
  for (const hd of yamlObj.dependencies) {
3226
- let pkg = {
3381
+ const pkg = {
3227
3382
  name: hd.name,
3228
3383
  version: hd.version // This could have * so not precise
3229
3384
  };
@@ -3238,7 +3393,7 @@ export const parseHelmYamlData = async function (helmData) {
3238
3393
  for (const key of Object.keys(yamlObj.entries[he])) {
3239
3394
  const hd = yamlObj.entries[he][key];
3240
3395
  if (hd.name && hd.version) {
3241
- let pkg = {
3396
+ const pkg = {
3242
3397
  name: hd.name,
3243
3398
  version: hd.version,
3244
3399
  description: hd.description || ""
@@ -3327,7 +3482,7 @@ export const recurseImageNameLookup = (keyValueObj, pkgList, imgList) => {
3327
3482
  return imgList;
3328
3483
  };
3329
3484
 
3330
- export const parseContainerSpecData = async function (dcData) {
3485
+ export const parseContainerSpecData = function (dcData) {
3331
3486
  const pkgList = [];
3332
3487
  const imgList = [];
3333
3488
  if (!dcData.includes("image") && !dcData.includes("kind")) {
@@ -3397,7 +3552,7 @@ export const parseContainerSpecData = async function (dcData) {
3397
3552
  export const identifyFlow = function (processingObj) {
3398
3553
  let flow = "unknown";
3399
3554
  if (processingObj.sinkId) {
3400
- let sinkId = processingObj.sinkId.toLowerCase();
3555
+ const sinkId = processingObj.sinkId.toLowerCase();
3401
3556
  if (sinkId.endsWith("write")) {
3402
3557
  flow = "inbound";
3403
3558
  } else if (sinkId.endsWith("read")) {
@@ -3427,7 +3582,7 @@ export const parsePrivadoFile = function (f) {
3427
3582
  return servlist;
3428
3583
  }
3429
3584
  const jsonData = JSON.parse(pData);
3430
- let aservice = {
3585
+ const aservice = {
3431
3586
  "x-trust-boundary": false,
3432
3587
  properties: [],
3433
3588
  data: [],
@@ -3472,9 +3627,9 @@ export const parsePrivadoFile = function (f) {
3472
3627
  // Find endpoints
3473
3628
  if (jsonData.collections) {
3474
3629
  const endpoints = [];
3475
- for (let c of jsonData.collections) {
3476
- for (let occ of c.collections) {
3477
- for (let e of occ.occurrences) {
3630
+ for (const c of jsonData.collections) {
3631
+ for (const occ of c.collections) {
3632
+ for (const e of occ.occurrences) {
3478
3633
  if (e.endPoint) {
3479
3634
  endpoints.push(e.endPoint);
3480
3635
  }
@@ -3485,7 +3640,7 @@ export const parsePrivadoFile = function (f) {
3485
3640
  }
3486
3641
  // Capture violations
3487
3642
  if (jsonData.violations) {
3488
- for (let v of jsonData.violations) {
3643
+ for (const v of jsonData.violations) {
3489
3644
  aservice.properties.push({
3490
3645
  name: "privado_violations",
3491
3646
  value: v.policyId
@@ -3505,7 +3660,7 @@ export const parsePrivadoFile = function (f) {
3505
3660
  return servlist;
3506
3661
  };
3507
3662
 
3508
- export const parseOpenapiSpecData = async function (oaData) {
3663
+ export const parseOpenapiSpecData = function (oaData) {
3509
3664
  const servlist = [];
3510
3665
  if (!oaData) {
3511
3666
  return servlist;
@@ -3555,7 +3710,7 @@ export const parseOpenapiSpecData = async function (oaData) {
3555
3710
  return servlist;
3556
3711
  };
3557
3712
 
3558
- export const parseCabalData = async function (cabalData) {
3713
+ export const parseCabalData = function (cabalData) {
3559
3714
  const pkgList = [];
3560
3715
  if (!cabalData) {
3561
3716
  return pkgList;
@@ -3582,7 +3737,7 @@ export const parseCabalData = async function (cabalData) {
3582
3737
  return pkgList;
3583
3738
  };
3584
3739
 
3585
- export const parseMixLockData = async function (mixData) {
3740
+ export const parseMixLockData = function (mixData) {
3586
3741
  const pkgList = [];
3587
3742
  if (!mixData) {
3588
3743
  return pkgList;
@@ -3608,7 +3763,7 @@ export const parseMixLockData = async function (mixData) {
3608
3763
  return pkgList;
3609
3764
  };
3610
3765
 
3611
- export const parseGitHubWorkflowData = async function (ghwData) {
3766
+ export const parseGitHubWorkflowData = function (ghwData) {
3612
3767
  const pkgList = [];
3613
3768
  const keys_cache = {};
3614
3769
  if (!ghwData) {
@@ -3650,7 +3805,7 @@ export const parseGitHubWorkflowData = async function (ghwData) {
3650
3805
  return pkgList;
3651
3806
  };
3652
3807
 
3653
- export const parseCloudBuildData = async function (cbwData) {
3808
+ export const parseCloudBuildData = function (cbwData) {
3654
3809
  const pkgList = [];
3655
3810
  const keys_cache = {};
3656
3811
  if (!cbwData) {
@@ -3666,7 +3821,7 @@ export const parseCloudBuildData = async function (cbwData) {
3666
3821
  const tmpA = step.name.split(":");
3667
3822
  if (tmpA.length === 2) {
3668
3823
  let group = dirname(tmpA[0]);
3669
- let name = basename(tmpA[0]);
3824
+ const name = basename(tmpA[0]);
3670
3825
  if (group === ".") {
3671
3826
  group = "";
3672
3827
  }
@@ -3687,7 +3842,7 @@ export const parseCloudBuildData = async function (cbwData) {
3687
3842
  return pkgList;
3688
3843
  };
3689
3844
 
3690
- export const parseConanLockData = async function (conanLockData) {
3845
+ export const parseConanLockData = function (conanLockData) {
3691
3846
  const pkgList = [];
3692
3847
  if (!conanLockData) {
3693
3848
  return pkgList;
@@ -3697,7 +3852,7 @@ export const parseConanLockData = async function (conanLockData) {
3697
3852
  return pkgList;
3698
3853
  }
3699
3854
  const nodes = graphLock.graph_lock.nodes;
3700
- for (let nk of Object.keys(nodes)) {
3855
+ for (const nk of Object.keys(nodes)) {
3701
3856
  if (nodes[nk].ref) {
3702
3857
  const tmpA = nodes[nk].ref.split("/");
3703
3858
  if (tmpA.length === 2) {
@@ -3708,7 +3863,7 @@ export const parseConanLockData = async function (conanLockData) {
3708
3863
  return pkgList;
3709
3864
  };
3710
3865
 
3711
- export const parseConanData = async function (conanData) {
3866
+ export const parseConanData = function (conanData) {
3712
3867
  const pkgList = [];
3713
3868
  if (!conanData) {
3714
3869
  return pkgList;
@@ -3737,7 +3892,7 @@ export const parseLeiningenData = function (leinData) {
3737
3892
  leinData = "(defproject" + tmpArr[1];
3738
3893
  }
3739
3894
  const ednData = parseEDNString(leinData);
3740
- for (let k of Object.keys(ednData)) {
3895
+ for (const k of Object.keys(ednData)) {
3741
3896
  if (k === "list") {
3742
3897
  ednData[k].forEach((jk) => {
3743
3898
  if (Array.isArray(jk)) {
@@ -3768,7 +3923,7 @@ export const parseEdnData = function (rawEdnData) {
3768
3923
  }
3769
3924
  const ednData = parseEDNString(rawEdnData);
3770
3925
  const pkgCache = {};
3771
- for (let k of Object.keys(ednData)) {
3926
+ for (const k of Object.keys(ednData)) {
3772
3927
  if (k === "map") {
3773
3928
  ednData[k].forEach((jk) => {
3774
3929
  if (Array.isArray(jk)) {
@@ -3815,7 +3970,7 @@ export const parseEdnData = function (rawEdnData) {
3815
3970
 
3816
3971
  export const parseNupkg = async function (nupkgFile) {
3817
3972
  const pkgList = [];
3818
- let pkg = { group: "" };
3973
+ const pkg = { group: "" };
3819
3974
  let nuspecData = await readZipEntry(nupkgFile, ".nuspec");
3820
3975
  // Remove byte order mark
3821
3976
  if (nuspecData.charCodeAt(0) === 0xfeff) {
@@ -3850,6 +4005,19 @@ export const parseNupkg = async function (nupkgFile) {
3850
4005
  value: nupkgFile
3851
4006
  }
3852
4007
  ];
4008
+ pkg.evidence = {
4009
+ identity: {
4010
+ field: "purl",
4011
+ confidence: 1,
4012
+ methods: [
4013
+ {
4014
+ technique: "binary-analysis",
4015
+ confidence: 1,
4016
+ value: nupkgFile
4017
+ }
4018
+ ]
4019
+ }
4020
+ };
3853
4021
  pkgList.push(pkg);
3854
4022
  if (fetchLicenses) {
3855
4023
  return await getNugetMetadata(pkgList);
@@ -3875,9 +4043,9 @@ export const parseCsPkgData = async function (pkgData) {
3875
4043
  return pkgList;
3876
4044
  }
3877
4045
  packages = packages[0].package;
3878
- for (let i in packages) {
4046
+ for (const i in packages) {
3879
4047
  const p = packages[i].$;
3880
- let pkg = { group: "" };
4048
+ const pkg = { group: "" };
3881
4049
  pkg.name = p.id;
3882
4050
  pkg.version = p.version;
3883
4051
  pkgList.push(pkg);
@@ -3907,12 +4075,12 @@ export const parseCsProjData = async function (csProjData) {
3907
4075
  }
3908
4076
  const project = projects[0];
3909
4077
  if (project.ItemGroup && project.ItemGroup.length) {
3910
- for (let i in project.ItemGroup) {
4078
+ for (const i in project.ItemGroup) {
3911
4079
  const item = project.ItemGroup[i];
3912
4080
  // .net core use PackageReference
3913
- for (let j in item.PackageReference) {
4081
+ for (const j in item.PackageReference) {
3914
4082
  const pref = item.PackageReference[j].$;
3915
- let pkg = { group: "" };
4083
+ const pkg = { group: "" };
3916
4084
  if (!pref.Include || pref.Include.includes(".csproj")) {
3917
4085
  continue;
3918
4086
  }
@@ -3921,9 +4089,9 @@ export const parseCsProjData = async function (csProjData) {
3921
4089
  pkgList.push(pkg);
3922
4090
  }
3923
4091
  // .net framework use Reference
3924
- for (let j in item.Reference) {
4092
+ for (const j in item.Reference) {
3925
4093
  const pref = item.Reference[j].$;
3926
- let pkg = { group: "" };
4094
+ const pkg = { group: "" };
3927
4095
  if (!pref.Include || pref.Include.includes(".csproj")) {
3928
4096
  continue;
3929
4097
  }
@@ -3953,7 +4121,7 @@ export const parseCsProjAssetsData = async function (csProjData) {
3953
4121
  if (!assetData || !assetData.libraries) {
3954
4122
  return pkgList;
3955
4123
  }
3956
- for (let alib of Object.keys(assetData.libraries)) {
4124
+ for (const alib of Object.keys(assetData.libraries)) {
3957
4125
  // Skip os runtime packages
3958
4126
  if (alib.startsWith("runtime")) {
3959
4127
  continue;
@@ -3992,8 +4160,8 @@ export const parseCsPkgLockData = async function (csLockData) {
3992
4160
  if (!assetData || !assetData.dependencies) {
3993
4161
  return pkgList;
3994
4162
  }
3995
- for (let aversion of Object.keys(assetData.dependencies)) {
3996
- for (let alib of Object.keys(assetData.dependencies[aversion])) {
4163
+ for (const aversion of Object.keys(assetData.dependencies)) {
4164
+ for (const alib of Object.keys(assetData.dependencies[aversion])) {
3997
4165
  const libData = assetData.dependencies[aversion][alib];
3998
4166
  pkg = {
3999
4167
  group: "",
@@ -4135,15 +4303,15 @@ export const parseComposerLock = function (pkgLockFile) {
4135
4303
  return [];
4136
4304
  }
4137
4305
  if (lockData) {
4138
- let packages = {};
4306
+ const packages = {};
4139
4307
  if (lockData["packages"]) {
4140
4308
  packages["required"] = lockData["packages"];
4141
4309
  }
4142
4310
  if (lockData["packages-dev"]) {
4143
4311
  packages["optional"] = lockData["packages-dev"];
4144
4312
  }
4145
- for (let compScope in packages) {
4146
- for (let i in packages[compScope]) {
4313
+ for (const compScope in packages) {
4314
+ for (const i in packages[compScope]) {
4147
4315
  const pkg = packages[compScope][i];
4148
4316
  // Be extra cautious. Potential fix for #236
4149
4317
  if (!pkg || !pkg.name || !pkg.version) {
@@ -4153,7 +4321,7 @@ export const parseComposerLock = function (pkgLockFile) {
4153
4321
  if (group === ".") {
4154
4322
  group = "";
4155
4323
  }
4156
- let name = basename(pkg.name);
4324
+ const name = basename(pkg.name);
4157
4325
  pkgList.push({
4158
4326
  group: group,
4159
4327
  name: name,
@@ -4171,7 +4339,20 @@ export const parseComposerLock = function (pkgLockFile) {
4171
4339
  name: "SrcFile",
4172
4340
  value: pkgLockFile
4173
4341
  }
4174
- ]
4342
+ ],
4343
+ evidence: {
4344
+ identity: {
4345
+ field: "purl",
4346
+ confidence: 1,
4347
+ methods: [
4348
+ {
4349
+ technique: "manifest-analysis",
4350
+ confidence: 1,
4351
+ value: pkgLockFile
4352
+ }
4353
+ ]
4354
+ }
4355
+ }
4175
4356
  });
4176
4357
  }
4177
4358
  }
@@ -4190,7 +4371,7 @@ export const parseSbtLock = function (pkgLockFile) {
4190
4371
  if (existsSync(pkgLockFile)) {
4191
4372
  const lockData = JSON.parse(readFileSync(pkgLockFile, "utf8"));
4192
4373
  if (lockData && lockData.dependencies) {
4193
- for (let pkg of lockData.dependencies) {
4374
+ for (const pkg of lockData.dependencies) {
4194
4375
  const artifacts = pkg.artifacts || undefined;
4195
4376
  let integrity = "";
4196
4377
  if (artifacts && artifacts.length) {
@@ -4215,7 +4396,20 @@ export const parseSbtLock = function (pkgLockFile) {
4215
4396
  name: "SrcFile",
4216
4397
  value: pkgLockFile
4217
4398
  }
4218
- ]
4399
+ ],
4400
+ evidence: {
4401
+ identity: {
4402
+ field: "purl",
4403
+ confidence: 1,
4404
+ methods: [
4405
+ {
4406
+ technique: "manifest-analysis",
4407
+ confidence: 1,
4408
+ value: pkgLockFile
4409
+ }
4410
+ ]
4411
+ }
4412
+ }
4219
4413
  });
4220
4414
  }
4221
4415
  }
@@ -4240,15 +4434,15 @@ export const convertOSQueryResults = function (
4240
4434
  if (res.version) {
4241
4435
  const version = res.version;
4242
4436
  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;
4437
+ const group = "";
4438
+ const subpath = res.path || res.admindir || res.source;
4439
+ const publisher = res.maintainer || res.creator;
4246
4440
  let scope = undefined;
4247
- let compScope = res.priority;
4441
+ const compScope = res.priority;
4248
4442
  if (["required", "optional", "excluded"].includes(compScope)) {
4249
4443
  scope = compScope;
4250
4444
  }
4251
- let description =
4445
+ const description =
4252
4446
  res.description ||
4253
4447
  res.arguments ||
4254
4448
  res.device ||
@@ -4293,7 +4487,7 @@ export const _swiftDepPkgList = (
4293
4487
  jsonData
4294
4488
  ) => {
4295
4489
  if (jsonData && jsonData.dependencies) {
4296
- for (let adep of jsonData.dependencies) {
4490
+ for (const adep of jsonData.dependencies) {
4297
4491
  const urlOrPath = adep.url || adep.path;
4298
4492
  const apkg = {
4299
4493
  group: adep.identity || "",
@@ -4333,7 +4527,7 @@ export const _swiftDepPkgList = (
4333
4527
  // Handle the immediate dependencies before recursing
4334
4528
  if (adep.dependencies && adep.dependencies.length) {
4335
4529
  const deplist = [];
4336
- for (let cdep of adep.dependencies) {
4530
+ for (const cdep of adep.dependencies) {
4337
4531
  const deppurl = new PackageURL(
4338
4532
  "swift",
4339
4533
  cdep.identity || "",
@@ -4380,7 +4574,7 @@ export const parseSwiftJsonTree = (rawOutput, pkgFile) => {
4380
4574
  }
4381
4575
  const pkgList = [];
4382
4576
  const dependenciesList = [];
4383
- let depKeys = {};
4577
+ const depKeys = {};
4384
4578
  let rootPkg = {};
4385
4579
  let jsonData = {};
4386
4580
  try {
@@ -4475,7 +4669,20 @@ export const parseSwiftResolved = (resolvedFile) => {
4475
4669
  name: "SrcFile",
4476
4670
  value: resolvedFile
4477
4671
  }
4478
- ]
4672
+ ],
4673
+ evidence: {
4674
+ identity: {
4675
+ field: "purl",
4676
+ confidence: 1,
4677
+ methods: [
4678
+ {
4679
+ technique: "manifest-analysis",
4680
+ confidence: 1,
4681
+ value: resolvedFile
4682
+ }
4683
+ ]
4684
+ }
4685
+ }
4479
4686
  };
4480
4687
  const repLocation = adep.location || adep.repositoryURL;
4481
4688
  if (repLocation) {
@@ -4497,7 +4704,7 @@ export const parseSwiftResolved = (resolvedFile) => {
4497
4704
  * @param {string} basePath Path to the maven project
4498
4705
  */
4499
4706
  export const collectMvnDependencies = function (mavenCmd, basePath) {
4500
- let tempDir = mkdtempSync(join(tmpdir(), "mvn-deps-"));
4707
+ const tempDir = mkdtempSync(join(tmpdir(), "mvn-deps-"));
4501
4708
  console.log(
4502
4709
  `Executing 'mvn dependency:copy-dependencies -DoutputDirectory=${tempDir} -DexcludeTransitive=true -DincludeScope=runtime' in ${basePath}`
4503
4710
  );
@@ -4555,7 +4762,7 @@ export const collectJarNS = function (jarPath) {
4555
4762
  // Execute jar tvf to get class names
4556
4763
  const jarFiles = getAllFiles(jarPath, "**/*.jar");
4557
4764
  if (jarFiles && jarFiles.length) {
4558
- for (let jf of jarFiles) {
4765
+ for (const jf of jarFiles) {
4559
4766
  const jarname = basename(jf);
4560
4767
  if (DEBUG_MODE) {
4561
4768
  console.log(`Executing 'jar tf ${jf}'`);
@@ -4659,7 +4866,7 @@ export { _encodeForPurl as encodeForPurl };
4659
4866
  * @return pkgList Package list
4660
4867
  */
4661
4868
  export const extractJarArchive = function (jarFile, tempDir) {
4662
- let pkgList = [];
4869
+ const pkgList = [];
4663
4870
  let jarFiles = [];
4664
4871
  const fname = basename(jarFile);
4665
4872
  let pomname = undefined;
@@ -4674,7 +4881,7 @@ export const extractJarArchive = function (jarFile, tempDir) {
4674
4881
  copyFileSync(jarFile, join(tempDir, fname), constants.COPYFILE_FICLONE);
4675
4882
  }
4676
4883
  if (jarFile.endsWith(".war") || jarFile.endsWith(".hpi")) {
4677
- let jarResult = spawnSync("jar", ["-xf", join(tempDir, fname)], {
4884
+ const jarResult = spawnSync("jar", ["-xf", join(tempDir, fname)], {
4678
4885
  encoding: "utf-8",
4679
4886
  cwd: tempDir
4680
4887
  });
@@ -4693,7 +4900,7 @@ export const extractJarArchive = function (jarFile, tempDir) {
4693
4900
  jarFiles = [join(tempDir, fname)];
4694
4901
  }
4695
4902
  if (jarFiles && jarFiles.length) {
4696
- for (let jf of jarFiles) {
4903
+ for (const jf of jarFiles) {
4697
4904
  pomname = jf.replace(".jar", ".pom");
4698
4905
  const jarname = basename(jf);
4699
4906
  // Ignore test jars
@@ -4703,7 +4910,7 @@ export const extractJarArchive = function (jarFile, tempDir) {
4703
4910
  ) {
4704
4911
  continue;
4705
4912
  }
4706
- let manifestDir = join(tempDir, "META-INF");
4913
+ const manifestDir = join(tempDir, "META-INF");
4707
4914
  const manifestFile = join(manifestDir, "MANIFEST.MF");
4708
4915
  let jarResult = {
4709
4916
  status: 1
@@ -4732,32 +4939,42 @@ export const extractJarArchive = function (jarFile, tempDir) {
4732
4939
  jarMetadata["Bundle-Vendor"] ||
4733
4940
  jarMetadata["Automatic-Module-Name"] ||
4734
4941
  "";
4942
+ let version =
4943
+ jarMetadata["Bundle-Version"] ||
4944
+ jarMetadata["Implementation-Version"] ||
4945
+ jarMetadata["Specification-Version"];
4946
+ if (version && version.includes(" ")) {
4947
+ version = version.split(" ")[0];
4948
+ }
4735
4949
  let name = "";
4950
+ // Prefer jar filename to construct name and version
4951
+ if (!name || !version || name === "" || version === "") {
4952
+ const tmpA = jarname.split("-");
4953
+ if (tmpA && tmpA.length > 1) {
4954
+ const lastPart = tmpA[tmpA.length - 1];
4955
+ if (!version || version === "") {
4956
+ version = lastPart.replace(".jar", "");
4957
+ }
4958
+ if (!name || name === "") {
4959
+ name = jarname.replace("-" + lastPart, "") || "";
4960
+ }
4961
+ } else {
4962
+ name = jarname.replace(".jar", "");
4963
+ }
4964
+ }
4736
4965
  if (
4966
+ !name.length &&
4737
4967
  jarMetadata["Bundle-Name"] &&
4738
4968
  !jarMetadata["Bundle-Name"].includes(" ")
4739
4969
  ) {
4740
4970
  name = jarMetadata["Bundle-Name"];
4741
4971
  } else if (
4972
+ !name.length &&
4742
4973
  jarMetadata["Implementation-Title"] &&
4743
4974
  !jarMetadata["Implementation-Title"].includes(" ")
4744
4975
  ) {
4745
4976
  name = jarMetadata["Implementation-Title"];
4746
4977
  }
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
4978
  // Sometimes the group might already contain the name
4762
4979
  // Eg: group: org.checkerframework.checker.qual name: checker-qual
4763
4980
  if (name && group && !group.startsWith("javax")) {
@@ -4773,21 +4990,6 @@ export const extractJarArchive = function (jarFile, tempDir) {
4773
4990
  );
4774
4991
  }
4775
4992
  }
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
4993
  // Patch the group string
4792
4994
  for (const aprefix in vendorAliases) {
4793
4995
  if (name && name.startsWith(aprefix)) {
@@ -4796,10 +4998,27 @@ export const extractJarArchive = function (jarFile, tempDir) {
4796
4998
  }
4797
4999
  }
4798
5000
  if (name && version) {
5001
+ // If group and name are the same we only need the name
5002
+ if (group == name) {
5003
+ group = "";
5004
+ }
4799
5005
  pkgList.push({
4800
5006
  group: group === "." ? "" : encodeForPurl(group || "") || "",
4801
5007
  name: name ? encodeForPurl(name) : "",
4802
5008
  version,
5009
+ evidence: {
5010
+ identity: {
5011
+ field: "purl",
5012
+ confidence: 0.5,
5013
+ methods: [
5014
+ {
5015
+ technique: "filename",
5016
+ confidence: 0.5,
5017
+ value: jarname
5018
+ }
5019
+ ]
5020
+ }
5021
+ },
4803
5022
  properties: [
4804
5023
  {
4805
5024
  name: "SrcFile",
@@ -4841,8 +5060,8 @@ export const extractJarArchive = function (jarFile, tempDir) {
4841
5060
  export const determineSbtVersion = function (projectPath) {
4842
5061
  const buildPropFile = join(projectPath, "project", "build.properties");
4843
5062
  if (existsSync(buildPropFile)) {
4844
- let properties = propertiesReader(buildPropFile);
4845
- let property = properties.get("sbt.version");
5063
+ const properties = propertiesReader(buildPropFile);
5064
+ const property = properties.get("sbt.version");
4846
5065
  if (property != null && valid(property)) {
4847
5066
  return property;
4848
5067
  }
@@ -4864,7 +5083,7 @@ export const determineSbtVersion = function (projectPath) {
4864
5083
  */
4865
5084
  export const addPlugin = function (projectPath, plugin) {
4866
5085
  const pluginsFile = sbtPluginsPath(projectPath);
4867
- var originalPluginsFile = null;
5086
+ let originalPluginsFile = null;
4868
5087
  if (existsSync(pluginsFile)) {
4869
5088
  originalPluginsFile = pluginsFile + ".cdxgen";
4870
5089
  copyFileSync(pluginsFile, originalPluginsFile, constants.COPYFILE_FICLONE);
@@ -4997,6 +5216,12 @@ export const getMavenCommand = (srcPath, rootPath) => {
4997
5216
  let findMavenFile = "mvnw";
4998
5217
  if (platform() == "win32") {
4999
5218
  findMavenFile = "mvnw.bat";
5219
+ if (
5220
+ !existsSync(join(srcPath, findMavenFile)) &&
5221
+ existsSync(join(srcPath, "mvnw.cmd"))
5222
+ ) {
5223
+ findMavenFile = "mvnw.cmd";
5224
+ }
5000
5225
  }
5001
5226
 
5002
5227
  if (existsSync(join(srcPath, findMavenFile))) {
@@ -5071,6 +5296,16 @@ export const executeAtom = (src, args) => {
5071
5296
  timeout: TIMEOUT_MS,
5072
5297
  env
5073
5298
  });
5299
+ if (
5300
+ result.stderr &&
5301
+ result.stderr.includes(
5302
+ "has been compiled by a more recent version of the Java Runtime"
5303
+ )
5304
+ ) {
5305
+ console.log(
5306
+ "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."
5307
+ );
5308
+ }
5074
5309
  if (DEBUG_MODE) {
5075
5310
  if (result.stdout) {
5076
5311
  console.log(result.stdout);
@@ -5118,17 +5353,62 @@ export const findAppModules = function (src, language) {
5118
5353
  return retList;
5119
5354
  };
5120
5355
 
5356
+ const flattenDeps = (
5357
+ dependsOn,
5358
+ dependenciesList,
5359
+ pkgList,
5360
+ reqOrSetupFile,
5361
+ t
5362
+ ) => {
5363
+ for (const d of t.dependencies) {
5364
+ const pkgRef = `pkg:pypi/${d.name}@${d.version}`
5365
+ .replace(/_/g, "-")
5366
+ .toLowerCase();
5367
+ dependsOn.push(pkgRef);
5368
+ dependenciesList.push({ ref: pkgRef, dependsOn: [] });
5369
+ pkgList.push({
5370
+ name: d.name,
5371
+ version: d.version,
5372
+ properties: [
5373
+ {
5374
+ name: "SrcFile",
5375
+ value: reqOrSetupFile
5376
+ }
5377
+ ],
5378
+ evidence: {
5379
+ identity: {
5380
+ field: "purl",
5381
+ confidence: 1,
5382
+ methods: [
5383
+ {
5384
+ technique: "manifest-analysis",
5385
+ confidence: 1,
5386
+ value: reqOrSetupFile
5387
+ }
5388
+ ]
5389
+ }
5390
+ }
5391
+ });
5392
+ // Recurse and flatten
5393
+ if (d.dependencies && d.dependencies) {
5394
+ flattenDeps(dependsOn, dependenciesList, pkgList, reqOrSetupFile, d);
5395
+ }
5396
+ }
5397
+ };
5398
+
5121
5399
  /**
5122
- * Execute pip freeze by creating a virtual env in a temp directory
5400
+ * Execute pip freeze by creating a virtual env in a temp directory and construct the dependency tree
5123
5401
  *
5124
5402
  * @param {string} basePath Base path
5125
5403
  * @param {string} reqOrSetupFile Requirements or setup.py file
5404
+ * @param {string} tempVenvDir Temp venv dir
5126
5405
  * @returns List of packages from the virtual env
5127
5406
  */
5128
- export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5129
- let pkgList = [];
5407
+ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
5408
+ const pkgList = [];
5409
+ const rootList = [];
5410
+ const dependenciesList = [];
5130
5411
  let result = undefined;
5131
- const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-venv-"));
5132
5412
  const env = {
5133
5413
  ...process.env
5134
5414
  };
@@ -5138,16 +5418,22 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5138
5418
  * By checking the environment variable "VIRTUAL_ENV" we decide whether to create an env or not
5139
5419
  */
5140
5420
  if (!process.env.VIRTUAL_ENV) {
5141
- result = spawnSync(PYTHON_CMD, ["-m", "venv", tempDir]);
5421
+ result = spawnSync(PYTHON_CMD, ["-m", "venv", tempVenvDir]);
5142
5422
  if (result.status !== 0 || result.error) {
5143
5423
  if (DEBUG_MODE) {
5144
- console.log(result.stderr);
5424
+ console.log("Virtual env creation has failed");
5425
+ if (!result.stderr) {
5426
+ console.log(
5427
+ "Ensure the virtualenv package is installed using pip. `python -m pip install virtualenv`"
5428
+ );
5429
+ }
5145
5430
  }
5146
5431
  } else {
5147
- env.VIRTUAL_ENV = tempDir;
5148
- env.PATH = `${join(tempDir, "bin")}${_delimiter}${
5149
- process.env.PATH || ""
5150
- }`;
5432
+ env.VIRTUAL_ENV = tempVenvDir;
5433
+ env.PATH = `${join(
5434
+ tempVenvDir,
5435
+ platform() == "win32" ? "Scripts" : "bin"
5436
+ )}${_delimiter}${process.env.PATH || ""}`;
5151
5437
  }
5152
5438
  }
5153
5439
  /**
@@ -5156,8 +5442,8 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5156
5442
  * This step is accurate but not reproducible since the resulting list could differ based on various factors
5157
5443
  * such as the version of python, pip, os, pypi.org availability (and weather?)
5158
5444
  */
5159
- if (tempDir === env.VIRTUAL_ENV) {
5160
- let pipInstallArgs = [
5445
+ if (tempVenvDir === env.VIRTUAL_ENV && reqOrSetupFile) {
5446
+ const pipInstallArgs = [
5161
5447
  "-m",
5162
5448
  "pip",
5163
5449
  "install",
@@ -5197,6 +5483,7 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5197
5483
  }
5198
5484
  if (!versionRelatedError && DEBUG_MODE) {
5199
5485
  console.log("args used:", pipInstallArgs);
5486
+ console.log(result.stdout, result.stderr);
5200
5487
  console.log(
5201
5488
  "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
5489
  );
@@ -5215,43 +5502,42 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5215
5502
  console.log("- Check if any git submodules have to be initialized.");
5216
5503
  }
5217
5504
  }
5505
+ }
5506
+ // Bug #375. Attempt pip freeze on existing and new virtual environments
5507
+ if (env.VIRTUAL_ENV && env.VIRTUAL_ENV.length) {
5218
5508
  /**
5219
5509
  * 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
5510
  * 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
5511
  */
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));
5512
+ if (DEBUG_MODE) {
5513
+ console.log(
5514
+ "About to construct the pip dependency tree. Please wait ..."
5515
+ );
5236
5516
  }
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);
5517
+ const tree = getTreeWithPlugin(env, PYTHON_CMD, basePath);
5518
+ if (DEBUG_MODE && !tree.length) {
5519
+ console.log(
5520
+ "Dependency tree generation has failed. Please check for any errors or version incompatibilities reported in the logs."
5521
+ );
5522
+ }
5523
+ for (const t of tree) {
5524
+ if (!t.version.length) {
5525
+ continue;
5254
5526
  }
5527
+ pkgList.push({
5528
+ name: t.name.replace(/_/g, "-"),
5529
+ version: t.version
5530
+ });
5531
+ rootList.push({
5532
+ name: t.name.replace(/_/g, "-"),
5533
+ version: t.version
5534
+ });
5535
+ const dependsOn = [];
5536
+ flattenDeps(dependsOn, dependenciesList, pkgList, reqOrSetupFile, t);
5537
+ dependenciesList.push({
5538
+ ref: `pkg:pypi/${t.name}@${t.version}`.replace(/_/g, "-").toLowerCase(),
5539
+ dependsOn
5540
+ });
5255
5541
  }
5256
5542
  } else {
5257
5543
  if (DEBUG_MODE) {
@@ -5260,11 +5546,11 @@ export const executePipFreezeInVenv = async (basePath, reqOrSetupFile) => {
5260
5546
  );
5261
5547
  }
5262
5548
  }
5263
- // Clean up
5264
- if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
5265
- rmSync(tempDir, { recursive: true, force: true });
5266
- }
5267
- return pkgList;
5549
+ return {
5550
+ pkgList,
5551
+ rootList,
5552
+ dependenciesList
5553
+ };
5268
5554
  };
5269
5555
 
5270
5556
  // taken from a very old package https://github.com/keithamus/parse-packagejson-name/blob/master/index.js