@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/index.js CHANGED
@@ -1,7 +1,10 @@
1
+ import Ajv from "ajv";
2
+ import addFormats from "ajv-formats";
1
3
  import { platform as _platform, homedir, tmpdir } from "node:os";
2
- import { join, dirname, sep } from "node:path";
4
+ import { basename, join, dirname, sep } from "node:path";
3
5
  import { parse } from "ssri";
4
6
  import {
7
+ lstatSync,
5
8
  mkdtempSync,
6
9
  rmSync,
7
10
  existsSync,
@@ -50,7 +53,7 @@ import {
50
53
  parseBdistMetadata,
51
54
  readZipEntry,
52
55
  parsePiplockData,
53
- executePipFreezeInVenv,
56
+ getPipFrozenTree,
54
57
  parseReqFile,
55
58
  getPyModules,
56
59
  parseSetupPyFile,
@@ -148,7 +151,7 @@ if (process.env.SWIFT_CMD) {
148
151
  }
149
152
 
150
153
  // Construct sbt cache directory
151
- let SBT_CACHE_DIR =
154
+ const SBT_CACHE_DIR =
152
155
  process.env.SBT_CACHE_DIR || join(homedir(), ".ivy2", "cache");
153
156
 
154
157
  // Debug mode flag
@@ -167,7 +170,10 @@ const TIMEOUT_MS = parseInt(process.env.CDXGEN_TIMEOUT_MS) || 10 * 60 * 1000;
167
170
 
168
171
  const createDefaultParentComponent = (path) => {
169
172
  // Create a parent component based on the directory name
170
- let dirName = dirname(path);
173
+ let dirName =
174
+ existsSync(path) && lstatSync(path).isDirectory()
175
+ ? basename(path)
176
+ : dirname(path);
171
177
  const tmpA = dirName.split(sep);
172
178
  dirName = tmpA[tmpA.length - 1];
173
179
  const parentComponent = {
@@ -206,55 +212,14 @@ const determineParentComponent = (options) => {
206
212
  return parentComponent;
207
213
  };
208
214
 
209
- /**
210
- * Method to create global external references
211
- *
212
- * @param pkg
213
- * @returns {Array}
214
- */
215
- function addGlobalReferences(src, filename, format = "xml") {
216
- let externalReferences = [];
217
- if (format === "json") {
218
- externalReferences.push({
219
- type: "other",
220
- url: src,
221
- comment: "Base path"
222
- });
223
- } else {
224
- externalReferences.push({
225
- reference: { "@type": "other", url: src, comment: "Base path" }
226
- });
227
- }
228
- let packageFileMeta = filename;
229
- if (!filename.includes(src)) {
230
- packageFileMeta = join(src, filename);
231
- }
232
- if (format === "json") {
233
- externalReferences.push({
234
- type: "other",
235
- url: packageFileMeta,
236
- comment: "Package file"
237
- });
238
- } else {
239
- externalReferences.push({
240
- reference: {
241
- "@type": "other",
242
- url: packageFileMeta,
243
- comment: "Package file"
244
- }
245
- });
246
- }
247
- return externalReferences;
248
- }
249
-
250
215
  /**
251
216
  * Function to create the services block
252
217
  */
253
218
  function addServices(services, format = "xml") {
254
- let serv_list = [];
219
+ const serv_list = [];
255
220
  for (const aserv of services) {
256
221
  if (format === "xml") {
257
- let service = {
222
+ const service = {
258
223
  "@bom-ref": aserv["bom-ref"],
259
224
  group: aserv.group || "",
260
225
  name: aserv.name,
@@ -276,9 +241,9 @@ function addServices(services, format = "xml") {
276
241
  * Function to create the dependency block
277
242
  */
278
243
  function addDependencies(dependencies) {
279
- let deps_list = [];
244
+ const deps_list = [];
280
245
  for (const adep of dependencies) {
281
- let dependsOnList = adep.dependsOn.map((v) => ({
246
+ const dependsOnList = adep.dependsOn.map((v) => ({
282
247
  "@ref": v
283
248
  }));
284
249
  const aentry = {
@@ -299,7 +264,7 @@ function addDependencies(dependencies) {
299
264
  function addMetadata(parentComponent = {}, format = "xml", options = {}) {
300
265
  // DO NOT fork this project to just change the vendor or author's name
301
266
  // Try to contribute to this project by sending PR or filing issues
302
- let metadata = {
267
+ const metadata = {
303
268
  timestamp: new Date().toISOString(),
304
269
  tools: {
305
270
  components: [
@@ -359,11 +324,17 @@ function addMetadata(parentComponent = {}, format = "xml", options = {}) {
359
324
  if (parentComponent && parentComponent.components) {
360
325
  firstPComp.components = parentComponent.components;
361
326
  }
327
+ if (firstPComp.evidence) {
328
+ delete firstPComp.evidence;
329
+ }
362
330
  metadata.component = firstPComp;
363
331
  }
364
332
  } else {
365
333
  // As a fallback, retain the parent component
366
334
  if (format === "json") {
335
+ if (parentComponent.evidence) {
336
+ delete parentComponent.evidence;
337
+ }
367
338
  metadata.component = parentComponent;
368
339
  }
369
340
  }
@@ -508,7 +479,7 @@ function addMetadata(parentComponent = {}, format = "xml", options = {}) {
508
479
  * @returns {Array}
509
480
  */
510
481
  function addExternalReferences(opkg, format = "xml") {
511
- let externalReferences = [];
482
+ const externalReferences = [];
512
483
  let pkgList = [];
513
484
  if (Array.isArray(opkg)) {
514
485
  pkgList = opkg;
@@ -549,7 +520,7 @@ function addExternalReferences(opkg, format = "xml") {
549
520
  } else {
550
521
  if (pkg.homepage && pkg.homepage.url) {
551
522
  externalReferences.push({
552
- type: "website",
523
+ type: pkg.homepage.url.includes("git") ? "vcs" : "website",
553
524
  url: pkg.homepage.url
554
525
  });
555
526
  }
@@ -575,17 +546,15 @@ function addExternalReferences(opkg, format = "xml") {
575
546
  * For all modules in the specified package, creates a list of
576
547
  * component objects from each one.
577
548
  */
578
- const _listComponents = listComponents;
579
- export { _listComponents as listComponents };
580
- function listComponents(
549
+ export function listComponents(
581
550
  options,
582
551
  allImports,
583
552
  pkg,
584
553
  ptype = "npm",
585
554
  format = "xml"
586
555
  ) {
587
- let compMap = {};
588
- let isRootPkg = ptype === "npm";
556
+ const compMap = {};
557
+ const isRootPkg = ptype === "npm";
589
558
  if (Array.isArray(pkg)) {
590
559
  pkg.forEach((p) => {
591
560
  addComponent(options, allImports, p, ptype, compMap, false, format);
@@ -616,13 +585,13 @@ function addComponent(
616
585
  return;
617
586
  }
618
587
  if (!isRootPkg) {
619
- let pkgIdentifier = parsePackageJsonName(pkg.name);
620
- let author = pkg.author || "";
621
- let publisher = pkg.publisher || "";
588
+ const pkgIdentifier = parsePackageJsonName(pkg.name);
589
+ const author = pkg.author || "";
590
+ const publisher = pkg.publisher || "";
622
591
  let group = pkg.group || pkgIdentifier.scope;
623
592
  // Create empty group
624
593
  group = group || "";
625
- let name = pkgIdentifier.fullName || pkg.name || "";
594
+ const name = pkgIdentifier.fullName || pkg.name || "";
626
595
  // name is mandatory
627
596
  if (!name) {
628
597
  return;
@@ -640,13 +609,13 @@ function addComponent(
640
609
  ) {
641
610
  return;
642
611
  }
643
- let version = pkg.version;
612
+ const version = pkg.version;
644
613
  if (!version || ["dummy", "ignore"].includes(version)) {
645
614
  return;
646
615
  }
647
- let licenses = pkg.licenses || getLicenses(pkg, format);
616
+ const licenses = pkg.licenses || getLicenses(pkg, format);
648
617
 
649
- let purl =
618
+ const purl =
650
619
  pkg.purl ||
651
620
  new PackageURL(
652
621
  ptype,
@@ -680,7 +649,7 @@ function addComponent(
680
649
  if (options.requiredOnly && ["optional", "excluded"].includes(compScope)) {
681
650
  return;
682
651
  }
683
- let component = {
652
+ const component = {
684
653
  author,
685
654
  publisher,
686
655
  group,
@@ -709,8 +678,14 @@ function addComponent(
709
678
 
710
679
  processHashes(pkg, component, format);
711
680
  // Retain any component properties
712
- if (format === "json" && pkg.properties && pkg.properties.length) {
713
- component.properties = pkg.properties;
681
+ if (format === "json") {
682
+ // Retain evidence
683
+ if (pkg.evidence && Object.keys(pkg.evidence).length) {
684
+ component.evidence = pkg.evidence;
685
+ }
686
+ if (pkg.properties && pkg.properties.length) {
687
+ component.properties = pkg.properties;
688
+ }
714
689
  }
715
690
  if (compMap[component.purl]) return; //remove cycles
716
691
  compMap[component.purl] = component;
@@ -735,7 +710,7 @@ function determinePackageType(pkg) {
735
710
  }
736
711
  if (pkg.purl) {
737
712
  try {
738
- let purl = PackageURL.fromString(pkg.purl);
713
+ const purl = PackageURL.fromString(pkg.purl);
739
714
  if (purl.type) {
740
715
  if (["docker", "oci", "container"].includes(purl.type)) {
741
716
  return "container";
@@ -801,7 +776,7 @@ function determinePackageType(pkg) {
801
776
  }
802
777
  }
803
778
  if (Object.prototype.hasOwnProperty.call(pkg, "keywords")) {
804
- for (let keyword of pkg.keywords) {
779
+ for (const keyword of pkg.keywords) {
805
780
  if (keyword.toLowerCase() === "framework") {
806
781
  return "framework";
807
782
  }
@@ -832,7 +807,7 @@ function processHashes(pkg, component, format = "xml") {
832
807
  });
833
808
  }
834
809
  } else if (pkg._integrity) {
835
- let integrity = parse(pkg._integrity) || {};
810
+ const integrity = parse(pkg._integrity) || {};
836
811
  // Components may have multiple hashes with various lengths. Check each one
837
812
  // that is supported by the CycloneDX specification.
838
813
  if (Object.prototype.hasOwnProperty.call(integrity, "sha512")) {
@@ -919,11 +894,6 @@ const buildBomXml = (
919
894
  bom.ele("metadata").ele(metadata);
920
895
  if (components && components.length) {
921
896
  bom.ele("components").ele(components);
922
- if (context && context.src && context.filename) {
923
- bom
924
- .ele("externalReferences")
925
- .ele(addGlobalReferences(context.src, context.filename, "xml"));
926
- }
927
897
  if (context) {
928
898
  if (context.services && context.services.length) {
929
899
  bom.ele("services").ele(addServices(context.services, "xml"));
@@ -987,13 +957,6 @@ const buildBomNSData = (options, pkgInfo, ptype, context) => {
987
957
  components: listComponents(options, allImports, pkgInfo, ptype, "json"),
988
958
  dependencies
989
959
  };
990
- if (context && context.src && context.filename) {
991
- jsonTpl.externalReferences = addGlobalReferences(
992
- context.src,
993
- context.filename,
994
- "json"
995
- );
996
- }
997
960
  bomNSData.bomXml = bomString;
998
961
  bomNSData.bomJson = jsonTpl;
999
962
  bomNSData.nsMapping = nsMapping;
@@ -1009,10 +972,7 @@ const buildBomNSData = (options, pkgInfo, ptype, context) => {
1009
972
  * @param path to the project
1010
973
  * @param options Parse options from the cli
1011
974
  */
1012
- const createJarBom = (path, options) => {
1013
- console.log(
1014
- `About to create SBoM for all jar files under ${path}. This would take a while ...`
1015
- );
975
+ export const createJarBom = (path, options) => {
1016
976
  let pkgList = [];
1017
977
  let jarFiles = getAllFiles(
1018
978
  path,
@@ -1026,8 +986,8 @@ const createJarBom = (path, options) => {
1026
986
  if (hpiFiles.length) {
1027
987
  jarFiles = jarFiles.concat(hpiFiles);
1028
988
  }
1029
- let tempDir = mkdtempSync(join(tmpdir(), "jar-deps-"));
1030
- for (let jar of jarFiles) {
989
+ const tempDir = mkdtempSync(join(tmpdir(), "jar-deps-"));
990
+ for (const jar of jarFiles) {
1031
991
  if (DEBUG_MODE) {
1032
992
  console.log(`Parsing ${jar}`);
1033
993
  }
@@ -1038,7 +998,6 @@ const createJarBom = (path, options) => {
1038
998
  }
1039
999
  // Clean up
1040
1000
  if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
1041
- console.log(`Cleaning up ${tempDir}`);
1042
1001
  rmSync(tempDir, { recursive: true, force: true });
1043
1002
  }
1044
1003
  return buildBomNSData(options, pkgList, "maven", {
@@ -1054,7 +1013,7 @@ const createJarBom = (path, options) => {
1054
1013
  * @param path to the project
1055
1014
  * @param options Parse options from the cli
1056
1015
  */
1057
- const createJavaBom = async (path, options) => {
1016
+ export const createJavaBom = async (path, options) => {
1058
1017
  let jarNSMapping = {};
1059
1018
  let pkgList = [];
1060
1019
  let dependencies = [];
@@ -1068,7 +1027,7 @@ const createJavaBom = async (path, options) => {
1068
1027
  if (DEBUG_MODE) {
1069
1028
  console.log(`Retrieving packages from ${path}`);
1070
1029
  }
1071
- let tempDir = mkdtempSync(join(tmpdir(), "war-deps-"));
1030
+ const tempDir = mkdtempSync(join(tmpdir(), "war-deps-"));
1072
1031
  pkgList = extractJarArchive(path, tempDir);
1073
1032
  if (pkgList.length) {
1074
1033
  pkgList = await getMvnMetadata(pkgList);
@@ -1119,7 +1078,7 @@ const createJavaBom = async (path, options) => {
1119
1078
  const addArgs = process.env.MVN_ARGS.split(" ");
1120
1079
  mvnArgs = mvnArgs.concat(addArgs);
1121
1080
  }
1122
- for (let f of pomFiles) {
1081
+ for (const f of pomFiles) {
1123
1082
  const basePath = dirname(f);
1124
1083
  const settingsXml = join(basePath, "settings.xml");
1125
1084
  if (existsSync(settingsXml)) {
@@ -1127,7 +1086,7 @@ const createJavaBom = async (path, options) => {
1127
1086
  `maven settings.xml found in ${basePath}. Please set the MVN_ARGS environment variable based on the full mvn build command used for this project.\nExample: MVN_ARGS='--settings ${settingsXml}'`
1128
1087
  );
1129
1088
  }
1130
- let mavenCmd = getMavenCommand(basePath, path);
1089
+ const mavenCmd = getMavenCommand(basePath, path);
1131
1090
  // Should we attempt to resolve class names
1132
1091
  if (options.resolveClass) {
1133
1092
  console.log(
@@ -1150,8 +1109,8 @@ const createJavaBom = async (path, options) => {
1150
1109
  const bomJsonFiles = getAllFiles(path, "**/target/*.json");
1151
1110
  const bomGenerated = bomJsonFiles.length;
1152
1111
  if (!bomGenerated || result.status !== 0 || result.error) {
1153
- let tempDir = mkdtempSync(join(tmpdir(), "cdxmvn-"));
1154
- let tempMvnTree = join(tempDir, "mvn-tree.txt");
1112
+ const tempDir = mkdtempSync(join(tmpdir(), "cdxmvn-"));
1113
+ const tempMvnTree = join(tempDir, "mvn-tree.txt");
1155
1114
  let mvnTreeArgs = ["dependency:tree", "-DoutputFile=" + tempMvnTree];
1156
1115
  if (process.env.MVN_ARGS) {
1157
1116
  const addArgs = process.env.MVN_ARGS.split(" ");
@@ -1281,18 +1240,18 @@ const createJavaBom = async (path, options) => {
1281
1240
  }
1282
1241
  }
1283
1242
  // gradle
1284
- let gradleFiles = getAllFiles(
1243
+ const gradleFiles = getAllFiles(
1285
1244
  path,
1286
1245
  (options.multiProject ? "**/" : "") + "build.gradle*"
1287
1246
  );
1288
- let allProjects = [];
1247
+ const allProjects = [];
1289
1248
  const allProjectsAddedPurls = [];
1290
1249
  const rootDependsOn = [];
1291
1250
  // Execute gradle properties
1292
1251
  if (gradleFiles && gradleFiles.length) {
1293
1252
  let retMap = executeGradleProperties(path, null, null);
1294
1253
  const allProjectsStr = retMap.projects || [];
1295
- let rootProject = retMap.rootProject;
1254
+ const rootProject = retMap.rootProject;
1296
1255
  if (rootProject) {
1297
1256
  parentComponent = {
1298
1257
  name: rootProject,
@@ -1314,11 +1273,11 @@ const createJavaBom = async (path, options) => {
1314
1273
  }
1315
1274
  // Get the sub-project properties and set the root dependencies
1316
1275
  if (allProjectsStr && allProjectsStr.length) {
1317
- for (let spstr of allProjectsStr) {
1276
+ for (const spstr of allProjectsStr) {
1318
1277
  retMap = executeGradleProperties(path, null, spstr);
1319
- let rootSubProject = retMap.rootProject;
1278
+ const rootSubProject = retMap.rootProject;
1320
1279
  if (rootSubProject) {
1321
- let rspName = rootSubProject.replace(/^:/, "").replace(/:/, "/");
1280
+ const rspName = rootSubProject.replace(/^:/, "").replace(/:/, "/");
1322
1281
  const rootSubProjectObj = {
1323
1282
  name: rspName,
1324
1283
  type: "application",
@@ -1356,9 +1315,9 @@ const createJavaBom = async (path, options) => {
1356
1315
  }
1357
1316
  }
1358
1317
  if (gradleFiles && gradleFiles.length && options.installDeps) {
1359
- let gradleCmd = getGradleCommand(path, null);
1318
+ const gradleCmd = getGradleCommand(path, null);
1360
1319
  allProjects.push(parentComponent);
1361
- for (let sp of allProjects) {
1320
+ for (const sp of allProjects) {
1362
1321
  let gradleDepArgs = [
1363
1322
  sp.purl === parentComponent.purl
1364
1323
  ? "dependencies"
@@ -1452,13 +1411,13 @@ const createJavaBom = async (path, options) => {
1452
1411
 
1453
1412
  // Bazel
1454
1413
  // Look for the BUILD file only in the root directory
1455
- let bazelFiles = getAllFiles(path, "BUILD");
1414
+ const bazelFiles = getAllFiles(path, "BUILD");
1456
1415
  if (bazelFiles && bazelFiles.length) {
1457
1416
  let BAZEL_CMD = "bazel";
1458
1417
  if (process.env.BAZEL_HOME) {
1459
1418
  BAZEL_CMD = join(process.env.BAZEL_HOME, "bin", "bazel");
1460
1419
  }
1461
- for (let f of bazelFiles) {
1420
+ for (const f of bazelFiles) {
1462
1421
  const basePath = dirname(f);
1463
1422
  // Invoke bazel build first
1464
1423
  const bazelTarget = process.env.BAZEL_TARGET || ":all";
@@ -1500,7 +1459,7 @@ const createJavaBom = async (path, options) => {
1500
1459
  console.error(result.stdout, result.stderr);
1501
1460
  options.failOnError && process.exit(1);
1502
1461
  }
1503
- let stdout = result.stdout;
1462
+ const stdout = result.stdout;
1504
1463
  if (stdout) {
1505
1464
  const cmdOutput = Buffer.from(stdout).toString();
1506
1465
  const dlist = parseBazelSkyframe(cmdOutput);
@@ -1545,7 +1504,7 @@ const createJavaBom = async (path, options) => {
1545
1504
  );
1546
1505
 
1547
1506
  let sbtProjects = [];
1548
- for (let i in sbtProjectFiles) {
1507
+ for (const i in sbtProjectFiles) {
1549
1508
  // parent dir of sbtProjectFile is the `project` directory
1550
1509
  // parent dir of `project` is the sbt root project directory
1551
1510
  const baseDir = dirname(dirname(sbtProjectFiles[i]));
@@ -1558,7 +1517,7 @@ const createJavaBom = async (path, options) => {
1558
1517
  path,
1559
1518
  (options.multiProject ? "**/" : "") + "*.sbt"
1560
1519
  );
1561
- for (let i in sbtProjectFiles) {
1520
+ for (const i in sbtProjectFiles) {
1562
1521
  const baseDir = dirname(sbtProjectFiles[i]);
1563
1522
  sbtProjects = sbtProjects.concat(baseDir);
1564
1523
  }
@@ -1566,7 +1525,7 @@ const createJavaBom = async (path, options) => {
1566
1525
 
1567
1526
  sbtProjects = [...new Set(sbtProjects)]; // eliminate duplicates
1568
1527
 
1569
- let sbtLockFiles = getAllFiles(
1528
+ const sbtLockFiles = getAllFiles(
1570
1529
  path,
1571
1530
  (options.multiProject ? "**/" : "") + "build.sbt.lock"
1572
1531
  );
@@ -1575,15 +1534,15 @@ const createJavaBom = async (path, options) => {
1575
1534
  let pkgList = [];
1576
1535
  // If the project use sbt lock files
1577
1536
  if (sbtLockFiles && sbtLockFiles.length) {
1578
- for (let f of sbtLockFiles) {
1537
+ for (const f of sbtLockFiles) {
1579
1538
  const dlist = parseSbtLock(f);
1580
1539
  if (dlist && dlist.length) {
1581
1540
  pkgList = pkgList.concat(dlist);
1582
1541
  }
1583
1542
  }
1584
1543
  } else {
1585
- let SBT_CMD = process.env.SBT_CMD || "sbt";
1586
- let sbtVersion = determineSbtVersion(path);
1544
+ const SBT_CMD = process.env.SBT_CMD || "sbt";
1545
+ const sbtVersion = determineSbtVersion(path);
1587
1546
  if (DEBUG_MODE) {
1588
1547
  console.log("Detected sbt version: " + sbtVersion);
1589
1548
  }
@@ -1596,11 +1555,11 @@ const createJavaBom = async (path, options) => {
1596
1555
  const useSlashSyntax = gte(sbtVersion, "1.5.0");
1597
1556
  const isDependencyTreeBuiltIn =
1598
1557
  sbtVersion != null && gte(sbtVersion, "1.4.0");
1599
- let tempDir = mkdtempSync(join(tmpdir(), "cdxsbt-"));
1600
- let tempSbtgDir = mkdtempSync(join(tmpdir(), "cdxsbtg-"));
1558
+ const tempDir = mkdtempSync(join(tmpdir(), "cdxsbt-"));
1559
+ const tempSbtgDir = mkdtempSync(join(tmpdir(), "cdxsbtg-"));
1601
1560
  mkdirSync(tempSbtgDir, { recursive: true });
1602
1561
  // Create temporary plugins file
1603
- let tempSbtPlugins = join(tempSbtgDir, "dep-plugins.sbt");
1562
+ const tempSbtPlugins = join(tempSbtgDir, "dep-plugins.sbt");
1604
1563
 
1605
1564
  // Requires a custom version of `sbt-dependency-graph` that
1606
1565
  // supports `--append` for `toFile` subtask.
@@ -1613,9 +1572,9 @@ const createJavaBom = async (path, options) => {
1613
1572
  }
1614
1573
  writeFileSync(tempSbtPlugins, sbtPluginDefinition);
1615
1574
 
1616
- for (let i in sbtProjects) {
1575
+ for (const i in sbtProjects) {
1617
1576
  const basePath = sbtProjects[i];
1618
- let dlFile = join(tempDir, "dl-" + i + ".tmp");
1577
+ const dlFile = join(tempDir, "dl-" + i + ".tmp");
1619
1578
  console.log(
1620
1579
  "Executing",
1621
1580
  SBT_CMD,
@@ -1624,8 +1583,8 @@ const createJavaBom = async (path, options) => {
1624
1583
  "using plugins",
1625
1584
  tempSbtgDir
1626
1585
  );
1627
- var sbtArgs = [];
1628
- var pluginFile = null;
1586
+ let sbtArgs = [];
1587
+ let pluginFile = null;
1629
1588
  if (standalonePluginFile) {
1630
1589
  sbtArgs = [
1631
1590
  `-addPluginSbtFile=${tempSbtPlugins}`,
@@ -1709,7 +1668,7 @@ const createJavaBom = async (path, options) => {
1709
1668
  * @param path to the project
1710
1669
  * @param options Parse options from the cli
1711
1670
  */
1712
- const createNodejsBom = async (path, options) => {
1671
+ export const createNodejsBom = async (path, options) => {
1713
1672
  let pkgList = [];
1714
1673
  let manifestFiles = [];
1715
1674
  let dependencies = [];
@@ -1720,7 +1679,7 @@ const createNodejsBom = async (path, options) => {
1720
1679
  const pkgJsonFiles = getAllFiles(path, "**/package.json");
1721
1680
  // Are there any package.json files in the container?
1722
1681
  if (pkgJsonFiles.length) {
1723
- for (let pj of pkgJsonFiles) {
1682
+ for (const pj of pkgJsonFiles) {
1724
1683
  const dlist = await parsePkgJson(pj);
1725
1684
  if (dlist && dlist.length) {
1726
1685
  pkgList = pkgList.concat(dlist);
@@ -1776,7 +1735,7 @@ const createNodejsBom = async (path, options) => {
1776
1735
  // Parse min js files
1777
1736
  if (minJsFiles && minJsFiles.length) {
1778
1737
  manifestFiles = manifestFiles.concat(minJsFiles);
1779
- for (let f of minJsFiles) {
1738
+ for (const f of minJsFiles) {
1780
1739
  const dlist = await parseMinJs(f);
1781
1740
  if (dlist && dlist.length) {
1782
1741
  pkgList = pkgList.concat(dlist);
@@ -1786,7 +1745,7 @@ const createNodejsBom = async (path, options) => {
1786
1745
  // Parse bower json files
1787
1746
  if (bowerFiles && bowerFiles.length) {
1788
1747
  manifestFiles = manifestFiles.concat(bowerFiles);
1789
- for (let f of bowerFiles) {
1748
+ for (const f of bowerFiles) {
1790
1749
  const dlist = await parseBowerJson(f);
1791
1750
  if (dlist && dlist.length) {
1792
1751
  pkgList = pkgList.concat(dlist);
@@ -1795,7 +1754,7 @@ const createNodejsBom = async (path, options) => {
1795
1754
  }
1796
1755
  if (pnpmLockFiles && pnpmLockFiles.length) {
1797
1756
  manifestFiles = manifestFiles.concat(pnpmLockFiles);
1798
- for (let f of pnpmLockFiles) {
1757
+ for (const f of pnpmLockFiles) {
1799
1758
  const basePath = dirname(f);
1800
1759
  // Determine the parent component
1801
1760
  const packageJsonF = join(basePath, "package.json");
@@ -1841,7 +1800,7 @@ const createNodejsBom = async (path, options) => {
1841
1800
  }
1842
1801
  if (pkgLockFiles && pkgLockFiles.length) {
1843
1802
  manifestFiles = manifestFiles.concat(pkgLockFiles);
1844
- for (let f of pkgLockFiles) {
1803
+ for (const f of pkgLockFiles) {
1845
1804
  if (DEBUG_MODE) {
1846
1805
  console.log(`Parsing ${f}`);
1847
1806
  }
@@ -1917,7 +1876,7 @@ const createNodejsBom = async (path, options) => {
1917
1876
  }
1918
1877
  if (yarnLockFiles && yarnLockFiles.length) {
1919
1878
  manifestFiles = manifestFiles.concat(yarnLockFiles);
1920
- for (let f of yarnLockFiles) {
1879
+ for (const f of yarnLockFiles) {
1921
1880
  if (DEBUG_MODE) {
1922
1881
  console.log(`Parsing ${f}`);
1923
1882
  }
@@ -1993,7 +1952,7 @@ const createNodejsBom = async (path, options) => {
1993
1952
  "**/package.json"
1994
1953
  );
1995
1954
  manifestFiles = manifestFiles.concat(pkgJsonFiles);
1996
- for (let pkgjf of pkgJsonFiles) {
1955
+ for (const pkgjf of pkgJsonFiles) {
1997
1956
  const dlist = await parsePkgJson(pkgjf);
1998
1957
  if (dlist && dlist.length) {
1999
1958
  pkgList = pkgList.concat(dlist);
@@ -2026,9 +1985,13 @@ const createNodejsBom = async (path, options) => {
2026
1985
  * @param path to the project
2027
1986
  * @param options Parse options from the cli
2028
1987
  */
2029
- const createPythonBom = async (path, options) => {
1988
+ export const createPythonBom = async (path, options) => {
2030
1989
  let allImports = {};
2031
1990
  let metadataFilename = "";
1991
+ let dependencies = [];
1992
+ let pkgList = [];
1993
+ const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-venv-"));
1994
+ const parentComponent = createDefaultParentComponent(path);
2032
1995
  const pipenvMode = existsSync(join(path, "Pipfile"));
2033
1996
  const poetryFiles = getAllFiles(
2034
1997
  path,
@@ -2054,7 +2017,6 @@ const createPythonBom = async (path, options) => {
2054
2017
  path,
2055
2018
  (options.multiProject ? "**/" : "") + "*.egg-info"
2056
2019
  );
2057
- let pkgList = [];
2058
2020
  const setupPy = join(path, "setup.py");
2059
2021
  const pyProjectFile = join(path, "pyproject.toml");
2060
2022
  const pyProjectMode = existsSync(pyProjectFile);
@@ -2065,7 +2027,7 @@ const createPythonBom = async (path, options) => {
2065
2027
  // Poetry sets up its own virtual env containing site-packages so
2066
2028
  // we give preference to poetry lock file. Issue# 129
2067
2029
  if (poetryMode) {
2068
- for (let f of poetryFiles) {
2030
+ for (const f of poetryFiles) {
2069
2031
  const lockData = readFileSync(f, { encoding: "utf-8" });
2070
2032
  const dlist = await parsePoetrylockData(lockData);
2071
2033
  if (dlist && dlist.length) {
@@ -2078,7 +2040,7 @@ const createPythonBom = async (path, options) => {
2078
2040
  });
2079
2041
  } else if (metadataFiles && metadataFiles.length) {
2080
2042
  // dist-info directories
2081
- for (let mf of metadataFiles) {
2043
+ for (const mf of metadataFiles) {
2082
2044
  const mData = readFileSync(mf, {
2083
2045
  encoding: "utf-8"
2084
2046
  });
@@ -2090,7 +2052,7 @@ const createPythonBom = async (path, options) => {
2090
2052
  }
2091
2053
  // .whl files. Zip file containing dist-info directory
2092
2054
  if (whlFiles && whlFiles.length) {
2093
- for (let wf of whlFiles) {
2055
+ for (const wf of whlFiles) {
2094
2056
  const mData = await readZipEntry(wf, "METADATA");
2095
2057
  if (mData) {
2096
2058
  const dlist = parseBdistMetadata(mData);
@@ -2102,7 +2064,7 @@ const createPythonBom = async (path, options) => {
2102
2064
  }
2103
2065
  // .egg-info files
2104
2066
  if (eggInfoFiles && eggInfoFiles.length) {
2105
- for (let ef of eggInfoFiles) {
2067
+ for (const ef of eggInfoFiles) {
2106
2068
  const dlist = parseBdistMetadata(readFileSync(ef, { encoding: "utf-8" }));
2107
2069
  if (dlist && dlist.length) {
2108
2070
  pkgList = pkgList.concat(dlist);
@@ -2126,15 +2088,15 @@ const createPythonBom = async (path, options) => {
2126
2088
  } else if (requirementsMode) {
2127
2089
  metadataFilename = "requirements.txt";
2128
2090
  if (reqFiles && reqFiles.length) {
2129
- for (let f of reqFiles) {
2091
+ for (const f of reqFiles) {
2130
2092
  const basePath = dirname(f);
2131
2093
  let reqData = undefined;
2132
2094
  let frozen = false;
2133
2095
  // Attempt to pip freeze in a virtualenv to improve precision
2134
2096
  if (options.installDeps) {
2135
- const dlist = await executePipFreezeInVenv(basePath, f);
2136
- if (dlist && dlist.length) {
2137
- pkgList = pkgList.concat(dlist);
2097
+ const pkgMap = getPipFrozenTree(basePath, f, tempDir);
2098
+ if (pkgMap.pkgList && pkgMap.pkgList.length) {
2099
+ pkgList = pkgList.concat(pkgMap.pkgList);
2138
2100
  frozen = true;
2139
2101
  }
2140
2102
  }
@@ -2154,7 +2116,7 @@ const createPythonBom = async (path, options) => {
2154
2116
  } // for
2155
2117
  metadataFilename = reqFiles.join(", ");
2156
2118
  } else if (reqDirFiles && reqDirFiles.length) {
2157
- for (let j in reqDirFiles) {
2119
+ for (const j in reqDirFiles) {
2158
2120
  const f = reqDirFiles[j];
2159
2121
  const reqData = readFileSync(f, { encoding: "utf-8" });
2160
2122
  const dlist = await parseReqFile(reqData, false);
@@ -2168,27 +2130,48 @@ const createPythonBom = async (path, options) => {
2168
2130
  }
2169
2131
  // Use atom in requirements, setup.py and pyproject.toml mode
2170
2132
  if (requirementsMode || setupPyMode || pyProjectMode) {
2171
- let dlist = undefined;
2172
2133
  /**
2173
2134
  * The order of preference is pyproject.toml (newer) and then setup.py
2174
2135
  */
2175
2136
  if (options.installDeps) {
2137
+ let pkgMap = undefined;
2176
2138
  if (pyProjectMode) {
2177
- dlist = await executePipFreezeInVenv(path, pyProjectFile);
2139
+ pkgMap = getPipFrozenTree(path, pyProjectFile, tempDir);
2178
2140
  } else if (setupPyMode) {
2179
- dlist = await executePipFreezeInVenv(path, setupPy);
2141
+ pkgMap = getPipFrozenTree(path, setupPy, tempDir);
2142
+ } else {
2143
+ pkgMap = getPipFrozenTree(path, undefined, tempDir);
2144
+ }
2145
+ // Get the imported modules and a dedupe list of packages
2146
+ const parentDependsOn = [];
2147
+ const retMap = await getPyModules(path, pkgList);
2148
+ if (retMap.pkgList && retMap.pkgList.length) {
2149
+ pkgList = pkgList.concat(retMap.pkgList);
2150
+ for (const p of retMap.pkgList) {
2151
+ parentDependsOn.push(`pkg:pypi/${p.name}@${p.version}`);
2152
+ }
2180
2153
  }
2181
- if (dlist && dlist.length) {
2182
- pkgList = pkgList.concat(dlist);
2154
+ if (retMap.dependenciesList) {
2155
+ dependencies = mergeDependencies(dependencies, retMap.dependenciesList);
2183
2156
  }
2184
- }
2185
- // Get the imported modules and a dedupe list of packages
2186
- const retMap = await getPyModules(path, pkgList);
2187
- if (retMap.pkgList && retMap.pkgList.length) {
2188
- pkgList = pkgList.concat(retMap.pkgList);
2189
- }
2190
- if (retMap.allImports) {
2191
- allImports = { ...allImports, ...retMap.allImports };
2157
+ if (retMap.allImports) {
2158
+ allImports = { ...allImports, ...retMap.allImports };
2159
+ }
2160
+ // Complete the dependency tree by making parent component depend on the first level
2161
+ for (const p of pkgMap.rootList) {
2162
+ parentDependsOn.push(`pkg:pypi/${p.name}@${p.version}`);
2163
+ }
2164
+ if (pkgMap.pkgList && pkgMap.pkgList.length) {
2165
+ pkgList = pkgList.concat(pkgMap.pkgList);
2166
+ }
2167
+ if (pkgMap.dependenciesList) {
2168
+ dependencies = mergeDependencies(dependencies, pkgMap.dependenciesList);
2169
+ }
2170
+ const pdependencies = {
2171
+ ref: parentComponent.purl,
2172
+ dependsOn: parentDependsOn
2173
+ };
2174
+ dependencies.splice(0, 0, pdependencies);
2192
2175
  }
2193
2176
  }
2194
2177
  // Final fallback is to manually parse setup.py if we still
@@ -2200,10 +2183,16 @@ const createPythonBom = async (path, options) => {
2200
2183
  pkgList = pkgList.concat(dlist);
2201
2184
  }
2202
2185
  }
2186
+ // Clean up
2187
+ if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
2188
+ rmSync(tempDir, { recursive: true, force: true });
2189
+ }
2203
2190
  return buildBomNSData(options, pkgList, "pypi", {
2204
2191
  allImports,
2205
2192
  src: path,
2206
- filename: metadataFilename
2193
+ filename: metadataFilename,
2194
+ dependencies,
2195
+ parentComponent
2207
2196
  });
2208
2197
  };
2209
2198
 
@@ -2213,7 +2202,7 @@ const createPythonBom = async (path, options) => {
2213
2202
  * @param path to the project
2214
2203
  * @param options Parse options from the cli
2215
2204
  */
2216
- const createGoBom = async (path, options) => {
2205
+ export const createGoBom = async (path, options) => {
2217
2206
  let pkgList = [];
2218
2207
  // Is this a binary file
2219
2208
  let maybeBinary = false;
@@ -2230,8 +2219,8 @@ const createGoBom = async (path, options) => {
2230
2219
  }
2231
2220
  // Since this pkg list is derived from the binary mark them as used.
2232
2221
  const allImports = {};
2233
- for (let mpkg of pkgList) {
2234
- let pkgFullName = `${mpkg.group}/${mpkg.name}`;
2222
+ for (const mpkg of pkgList) {
2223
+ const pkgFullName = `${mpkg.group}/${mpkg.name}`;
2235
2224
  allImports[pkgFullName] = true;
2236
2225
  }
2237
2226
  return buildBomNSData(options, pkgList, "golang", {
@@ -2255,7 +2244,7 @@ const createGoBom = async (path, options) => {
2255
2244
  "Using go.sum to generate BOMs for go projects may return an inaccurate representation of transitive dependencies.\nSee: https://github.com/golang/go/wiki/Modules#is-gosum-a-lock-file-why-does-gosum-include-information-for-module-versions-i-am-no-longer-using\n",
2256
2245
  "Set USE_GOSUM=false to generate BOMs using go.mod as the dependency source of truth."
2257
2246
  );
2258
- for (let f of gosumFiles) {
2247
+ for (const f of gosumFiles) {
2259
2248
  if (DEBUG_MODE) {
2260
2249
  console.log(`Parsing ${f}`);
2261
2250
  }
@@ -2274,7 +2263,7 @@ const createGoBom = async (path, options) => {
2274
2263
  // If USE_GOSUM is false, generate BOM components using go.mod.
2275
2264
  const gosumMap = {};
2276
2265
  if (gosumFiles.length) {
2277
- for (let f of gosumFiles) {
2266
+ for (const f of gosumFiles) {
2278
2267
  if (DEBUG_MODE) {
2279
2268
  console.log(`Parsing ${f}`);
2280
2269
  }
@@ -2303,7 +2292,7 @@ const createGoBom = async (path, options) => {
2303
2292
  let shouldManuallyParse = false;
2304
2293
  // Use the go list -deps and go mod why commands to generate a good quality BoM for non-docker invocations
2305
2294
  if (!["docker", "oci", "os"].includes(options.projectType)) {
2306
- for (let f of gomodFiles) {
2295
+ for (const f of gomodFiles) {
2307
2296
  const basePath = dirname(f);
2308
2297
  // Ignore vendor packages
2309
2298
  if (basePath.includes("/vendor/") || basePath.includes("/build/")) {
@@ -2349,11 +2338,11 @@ const createGoBom = async (path, options) => {
2349
2338
  );
2350
2339
  }
2351
2340
  // Using go mod why detect required packages
2352
- for (let apkg of pkgList) {
2341
+ for (const apkg of pkgList) {
2353
2342
  if (circuitBreak) {
2354
2343
  break;
2355
2344
  }
2356
- let pkgFullName = `${apkg.name}`;
2345
+ const pkgFullName = `${apkg.name}`;
2357
2346
  if (apkg.scope === "required") {
2358
2347
  allImports[pkgFullName] = true;
2359
2348
  continue;
@@ -2375,7 +2364,7 @@ const createGoBom = async (path, options) => {
2375
2364
  const mstdout = mresult.stdout;
2376
2365
  if (mstdout) {
2377
2366
  const cmdOutput = Buffer.from(mstdout).toString();
2378
- let whyPkg = parseGoModWhy(cmdOutput);
2367
+ const whyPkg = parseGoModWhy(cmdOutput);
2379
2368
  if (whyPkg == pkgFullName) {
2380
2369
  allImports[pkgFullName] = true;
2381
2370
  }
@@ -2399,7 +2388,7 @@ const createGoBom = async (path, options) => {
2399
2388
  "Manually parsing go.mod files. The resultant BoM would be incomplete."
2400
2389
  );
2401
2390
  }
2402
- for (let f of gomodFiles) {
2391
+ for (const f of gomodFiles) {
2403
2392
  if (DEBUG_MODE) {
2404
2393
  console.log(`Parsing ${f}`);
2405
2394
  }
@@ -2414,7 +2403,7 @@ const createGoBom = async (path, options) => {
2414
2403
  filename: gomodFiles.join(", ")
2415
2404
  });
2416
2405
  } else if (gopkgLockFiles.length) {
2417
- for (let f of gopkgLockFiles) {
2406
+ for (const f of gopkgLockFiles) {
2418
2407
  if (DEBUG_MODE) {
2419
2408
  console.log(`Parsing ${f}`);
2420
2409
  }
@@ -2440,7 +2429,7 @@ const createGoBom = async (path, options) => {
2440
2429
  * @param path to the project
2441
2430
  * @param options Parse options from the cli
2442
2431
  */
2443
- const createRustBom = async (path, options) => {
2432
+ export const createRustBom = async (path, options) => {
2444
2433
  let pkgList = [];
2445
2434
  // Is this a binary file
2446
2435
  let maybeBinary = false;
@@ -2457,8 +2446,8 @@ const createRustBom = async (path, options) => {
2457
2446
  }
2458
2447
  // Since this pkg list is derived from the binary mark them as used.
2459
2448
  const allImports = {};
2460
- for (let mpkg of pkgList) {
2461
- let pkgFullName = `${mpkg.group}/${mpkg.name}`;
2449
+ for (const mpkg of pkgList) {
2450
+ const pkgFullName = `${mpkg.group}/${mpkg.name}`;
2462
2451
  allImports[pkgFullName] = true;
2463
2452
  }
2464
2453
  return buildBomNSData(options, pkgList, "cargo", {
@@ -2476,9 +2465,9 @@ const createRustBom = async (path, options) => {
2476
2465
  (options.multiProject ? "**/" : "") + "Cargo.toml"
2477
2466
  );
2478
2467
  const cargoMode = cargoFiles.length;
2479
- let cargoLockMode = cargoLockFiles.length;
2468
+ const cargoLockMode = cargoLockFiles.length;
2480
2469
  if (cargoMode && !cargoLockMode) {
2481
- for (let f of cargoFiles) {
2470
+ for (const f of cargoFiles) {
2482
2471
  if (DEBUG_MODE) {
2483
2472
  console.log(`Parsing ${f}`);
2484
2473
  }
@@ -2499,7 +2488,7 @@ const createRustBom = async (path, options) => {
2499
2488
  (options.multiProject ? "**/" : "") + "Cargo.lock"
2500
2489
  );
2501
2490
  if (cargoLockFiles.length) {
2502
- for (let f of cargoLockFiles) {
2491
+ for (const f of cargoLockFiles) {
2503
2492
  if (DEBUG_MODE) {
2504
2493
  console.log(`Parsing ${f}`);
2505
2494
  }
@@ -2523,7 +2512,7 @@ const createRustBom = async (path, options) => {
2523
2512
  * @param path to the project
2524
2513
  * @param options Parse options from the cli
2525
2514
  */
2526
- const createDartBom = async (path, options) => {
2515
+ export const createDartBom = async (path, options) => {
2527
2516
  const pubFiles = getAllFiles(
2528
2517
  path,
2529
2518
  (options.multiProject ? "**/" : "") + "pubspec.lock"
@@ -2534,7 +2523,7 @@ const createDartBom = async (path, options) => {
2534
2523
  );
2535
2524
  let pkgList = [];
2536
2525
  if (pubFiles.length) {
2537
- for (let f of pubFiles) {
2526
+ for (const f of pubFiles) {
2538
2527
  if (DEBUG_MODE) {
2539
2528
  console.log(`Parsing ${f}`);
2540
2529
  }
@@ -2549,12 +2538,12 @@ const createDartBom = async (path, options) => {
2549
2538
  filename: pubFiles.join(", ")
2550
2539
  });
2551
2540
  } else if (pubSpecYamlFiles.length) {
2552
- for (let f of pubSpecYamlFiles) {
2541
+ for (const f of pubSpecYamlFiles) {
2553
2542
  if (DEBUG_MODE) {
2554
2543
  console.log(`Parsing ${f}`);
2555
2544
  }
2556
2545
  const pubYamlData = readFileSync(f, { encoding: "utf-8" });
2557
- const dlist = await parsePubYamlData(pubYamlData);
2546
+ const dlist = parsePubYamlData(pubYamlData);
2558
2547
  if (dlist && dlist.length) {
2559
2548
  pkgList = pkgList.concat(dlist);
2560
2549
  }
@@ -2574,7 +2563,7 @@ const createDartBom = async (path, options) => {
2574
2563
  * @param path to the project
2575
2564
  * @param options Parse options from the cli
2576
2565
  */
2577
- const createCppBom = async (path, options) => {
2566
+ export const createCppBom = async (path, options) => {
2578
2567
  const conanLockFiles = getAllFiles(
2579
2568
  path,
2580
2569
  (options.multiProject ? "**/" : "") + "conan.lock"
@@ -2585,12 +2574,12 @@ const createCppBom = async (path, options) => {
2585
2574
  );
2586
2575
  let pkgList = [];
2587
2576
  if (conanLockFiles.length) {
2588
- for (let f of conanLockFiles) {
2577
+ for (const f of conanLockFiles) {
2589
2578
  if (DEBUG_MODE) {
2590
2579
  console.log(`Parsing ${f}`);
2591
2580
  }
2592
2581
  const conanLockData = readFileSync(f, { encoding: "utf-8" });
2593
- const dlist = await parseConanLockData(conanLockData);
2582
+ const dlist = parseConanLockData(conanLockData);
2594
2583
  if (dlist && dlist.length) {
2595
2584
  pkgList = pkgList.concat(dlist);
2596
2585
  }
@@ -2600,12 +2589,12 @@ const createCppBom = async (path, options) => {
2600
2589
  filename: conanLockFiles.join(", ")
2601
2590
  });
2602
2591
  } else if (conanFiles.length) {
2603
- for (let f of conanFiles) {
2592
+ for (const f of conanFiles) {
2604
2593
  if (DEBUG_MODE) {
2605
2594
  console.log(`Parsing ${f}`);
2606
2595
  }
2607
2596
  const conanData = readFileSync(f, { encoding: "utf-8" });
2608
- const dlist = await parseConanData(conanData);
2597
+ const dlist = parseConanData(conanData);
2609
2598
  if (dlist && dlist.length) {
2610
2599
  pkgList = pkgList.concat(dlist);
2611
2600
  }
@@ -2625,7 +2614,7 @@ const createCppBom = async (path, options) => {
2625
2614
  * @param path to the project
2626
2615
  * @param options Parse options from the cli
2627
2616
  */
2628
- const createClojureBom = async (path, options) => {
2617
+ export const createClojureBom = (path, options) => {
2629
2618
  const ednFiles = getAllFiles(
2630
2619
  path,
2631
2620
  (options.multiProject ? "**/" : "") + "deps.edn"
@@ -2640,7 +2629,7 @@ const createClojureBom = async (path, options) => {
2640
2629
  if (process.env.LEIN_ARGS) {
2641
2630
  LEIN_ARGS = process.env.LEIN_ARGS.split(" ");
2642
2631
  }
2643
- for (let f of leinFiles) {
2632
+ for (const f of leinFiles) {
2644
2633
  if (DEBUG_MODE) {
2645
2634
  console.log(`Parsing ${f}`);
2646
2635
  }
@@ -2690,7 +2679,7 @@ const createClojureBom = async (path, options) => {
2690
2679
  if (process.env.CLJ_ARGS) {
2691
2680
  CLJ_ARGS = process.env.CLJ_ARGS.split(" ");
2692
2681
  }
2693
- for (let f of ednFiles) {
2682
+ for (const f of ednFiles) {
2694
2683
  const basePath = dirname(f);
2695
2684
  console.log("Executing", CLJ_CMD, CLJ_ARGS.join(" "), "in", basePath);
2696
2685
  const result = spawnSync(CLJ_CMD, CLJ_ARGS, {
@@ -2743,19 +2732,19 @@ const createClojureBom = async (path, options) => {
2743
2732
  * @param path to the project
2744
2733
  * @param options Parse options from the cli
2745
2734
  */
2746
- const createHaskellBom = async (path, options) => {
2735
+ export const createHaskellBom = async (path, options) => {
2747
2736
  const cabalFiles = getAllFiles(
2748
2737
  path,
2749
2738
  (options.multiProject ? "**/" : "") + "cabal.project.freeze"
2750
2739
  );
2751
2740
  let pkgList = [];
2752
2741
  if (cabalFiles.length) {
2753
- for (let f of cabalFiles) {
2742
+ for (const f of cabalFiles) {
2754
2743
  if (DEBUG_MODE) {
2755
2744
  console.log(`Parsing ${f}`);
2756
2745
  }
2757
2746
  const cabalData = readFileSync(f, { encoding: "utf-8" });
2758
- const dlist = await parseCabalData(cabalData);
2747
+ const dlist = parseCabalData(cabalData);
2759
2748
  if (dlist && dlist.length) {
2760
2749
  pkgList = pkgList.concat(dlist);
2761
2750
  }
@@ -2774,19 +2763,19 @@ const createHaskellBom = async (path, options) => {
2774
2763
  * @param path to the project
2775
2764
  * @param options Parse options from the cli
2776
2765
  */
2777
- const createElixirBom = async (path, options) => {
2766
+ export const createElixirBom = async (path, options) => {
2778
2767
  const mixFiles = getAllFiles(
2779
2768
  path,
2780
2769
  (options.multiProject ? "**/" : "") + "mix.lock"
2781
2770
  );
2782
2771
  let pkgList = [];
2783
2772
  if (mixFiles.length) {
2784
- for (let f of mixFiles) {
2773
+ for (const f of mixFiles) {
2785
2774
  if (DEBUG_MODE) {
2786
2775
  console.log(`Parsing ${f}`);
2787
2776
  }
2788
2777
  const mixData = readFileSync(f, { encoding: "utf-8" });
2789
- const dlist = await parseMixLockData(mixData);
2778
+ const dlist = parseMixLockData(mixData);
2790
2779
  if (dlist && dlist.length) {
2791
2780
  pkgList = pkgList.concat(dlist);
2792
2781
  }
@@ -2805,16 +2794,16 @@ const createElixirBom = async (path, options) => {
2805
2794
  * @param path to the project
2806
2795
  * @param options Parse options from the cli
2807
2796
  */
2808
- const createGitHubBom = async (path, options) => {
2797
+ export const createGitHubBom = async (path, options) => {
2809
2798
  const ghactionFiles = getAllFiles(path, ".github/workflows/" + "*.yml");
2810
2799
  let pkgList = [];
2811
2800
  if (ghactionFiles.length) {
2812
- for (let f of ghactionFiles) {
2801
+ for (const f of ghactionFiles) {
2813
2802
  if (DEBUG_MODE) {
2814
2803
  console.log(`Parsing ${f}`);
2815
2804
  }
2816
2805
  const ghwData = readFileSync(f, { encoding: "utf-8" });
2817
- const dlist = await parseGitHubWorkflowData(ghwData);
2806
+ const dlist = parseGitHubWorkflowData(ghwData);
2818
2807
  if (dlist && dlist.length) {
2819
2808
  pkgList = pkgList.concat(dlist);
2820
2809
  }
@@ -2833,16 +2822,16 @@ const createGitHubBom = async (path, options) => {
2833
2822
  * @param path to the project
2834
2823
  * @param options Parse options from the cli
2835
2824
  */
2836
- const createCloudBuildBom = async (path, options) => {
2825
+ export const createCloudBuildBom = async (path, options) => {
2837
2826
  const cbFiles = getAllFiles(path, "cloudbuild.yml");
2838
2827
  let pkgList = [];
2839
2828
  if (cbFiles.length) {
2840
- for (let f of cbFiles) {
2829
+ for (const f of cbFiles) {
2841
2830
  if (DEBUG_MODE) {
2842
2831
  console.log(`Parsing ${f}`);
2843
2832
  }
2844
2833
  const cbwData = readFileSync(f, { encoding: "utf-8" });
2845
- const dlist = await parseCloudBuildData(cbwData);
2834
+ const dlist = parseCloudBuildData(cbwData);
2846
2835
  if (dlist && dlist.length) {
2847
2836
  pkgList = pkgList.concat(dlist);
2848
2837
  }
@@ -2861,7 +2850,7 @@ const createCloudBuildBom = async (path, options) => {
2861
2850
  * @param path to the project
2862
2851
  * @param options Parse options from the cli
2863
2852
  */
2864
- const createOSBom = async (path, options) => {
2853
+ export const createOSBom = (path, options) => {
2865
2854
  console.warn(
2866
2855
  "About to generate SBoM for the current OS installation. This would take several minutes ..."
2867
2856
  );
@@ -2893,7 +2882,7 @@ const createOSBom = async (path, options) => {
2893
2882
  allLayersDir: options.allLayersExplodedDir,
2894
2883
  allLayersExplodedDir: options.allLayersExplodedDir
2895
2884
  };
2896
- let pkgPathList = [];
2885
+ const pkgPathList = [];
2897
2886
  if (options.deep) {
2898
2887
  getPkgPathList(exportData, undefined);
2899
2888
  }
@@ -2906,15 +2895,15 @@ const createOSBom = async (path, options) => {
2906
2895
  * @param path to the project
2907
2896
  * @param options Parse options from the cli
2908
2897
  */
2909
- const createJenkinsBom = async (path, options) => {
2898
+ export const createJenkinsBom = async (path, options) => {
2910
2899
  let pkgList = [];
2911
2900
  const hpiFiles = getAllFiles(
2912
2901
  path,
2913
2902
  (options.multiProject ? "**/" : "") + "*.hpi"
2914
2903
  );
2915
- let tempDir = mkdtempSync(join(tmpdir(), "hpi-deps-"));
2904
+ const tempDir = mkdtempSync(join(tmpdir(), "hpi-deps-"));
2916
2905
  if (hpiFiles.length) {
2917
- for (let f of hpiFiles) {
2906
+ for (const f of hpiFiles) {
2918
2907
  if (DEBUG_MODE) {
2919
2908
  console.log(`Parsing ${f}`);
2920
2909
  }
@@ -2926,7 +2915,7 @@ const createJenkinsBom = async (path, options) => {
2926
2915
  }
2927
2916
  const jsFiles = getAllFiles(tempDir, "**/*.js");
2928
2917
  if (jsFiles.length) {
2929
- for (let f of jsFiles) {
2918
+ for (const f of jsFiles) {
2930
2919
  if (DEBUG_MODE) {
2931
2920
  console.log(`Parsing ${f}`);
2932
2921
  }
@@ -2954,19 +2943,19 @@ const createJenkinsBom = async (path, options) => {
2954
2943
  * @param path to the project
2955
2944
  * @param options Parse options from the cli
2956
2945
  */
2957
- const createHelmBom = async (path, options) => {
2946
+ export const createHelmBom = async (path, options) => {
2958
2947
  let pkgList = [];
2959
2948
  const yamlFiles = getAllFiles(
2960
2949
  path,
2961
2950
  (options.multiProject ? "**/" : "") + "*.yaml"
2962
2951
  );
2963
2952
  if (yamlFiles.length) {
2964
- for (let f of yamlFiles) {
2953
+ for (const f of yamlFiles) {
2965
2954
  if (DEBUG_MODE) {
2966
2955
  console.log(`Parsing ${f}`);
2967
2956
  }
2968
2957
  const helmData = readFileSync(f, { encoding: "utf-8" });
2969
- const dlist = await parseHelmYamlData(helmData);
2958
+ const dlist = parseHelmYamlData(helmData);
2970
2959
  if (dlist && dlist.length) {
2971
2960
  pkgList = pkgList.concat(dlist);
2972
2961
  }
@@ -2985,7 +2974,7 @@ const createHelmBom = async (path, options) => {
2985
2974
  * @param path to the project
2986
2975
  * @param options Parse options from the cli
2987
2976
  */
2988
- const createSwiftBom = async (path, options) => {
2977
+ export const createSwiftBom = (path, options) => {
2989
2978
  const swiftFiles = getAllFiles(
2990
2979
  path,
2991
2980
  (options.multiProject ? "**/" : "") + "Package*.swift"
@@ -2997,9 +2986,9 @@ const createSwiftBom = async (path, options) => {
2997
2986
  let pkgList = [];
2998
2987
  let dependencies = [];
2999
2988
  let parentComponent = {};
3000
- let completedPath = [];
2989
+ const completedPath = [];
3001
2990
  if (pkgResolvedFiles.length) {
3002
- for (let f of pkgResolvedFiles) {
2991
+ for (const f of pkgResolvedFiles) {
3003
2992
  if (!parentComponent || !Object.keys(parentComponent).length) {
3004
2993
  parentComponent = createDefaultParentComponent(f);
3005
2994
  }
@@ -3013,7 +3002,7 @@ const createSwiftBom = async (path, options) => {
3013
3002
  }
3014
3003
  }
3015
3004
  if (swiftFiles.length) {
3016
- for (let f of swiftFiles) {
3005
+ for (const f of swiftFiles) {
3017
3006
  const basePath = dirname(f);
3018
3007
  if (completedPath.includes(basePath)) {
3019
3008
  continue;
@@ -3071,16 +3060,16 @@ const createSwiftBom = async (path, options) => {
3071
3060
  * @param path to the project
3072
3061
  * @param options Parse options from the cli
3073
3062
  */
3074
- const createContainerSpecLikeBom = async (path, options) => {
3063
+ export const createContainerSpecLikeBom = async (path, options) => {
3075
3064
  let services = [];
3076
- let ociSpecs = [];
3065
+ const ociSpecs = [];
3077
3066
  let components = [];
3078
3067
  let componentsXmls = [];
3079
- let parentComponent = {};
3068
+ const parentComponent = {};
3080
3069
  let dependencies = [];
3081
- let doneimages = [];
3082
- let doneservices = [];
3083
- let origProjectType = options.projectType;
3070
+ const doneimages = [];
3071
+ const doneservices = [];
3072
+ const origProjectType = options.projectType;
3084
3073
  let dcFiles = getAllFiles(
3085
3074
  path,
3086
3075
  (options.multiProject ? "**/" : "") + "*.yml"
@@ -3093,7 +3082,7 @@ const createContainerSpecLikeBom = async (path, options) => {
3093
3082
  path,
3094
3083
  (options.multiProject ? "**/" : "") + "open*.json"
3095
3084
  );
3096
- let oapiYamlFiles = getAllFiles(
3085
+ const oapiYamlFiles = getAllFiles(
3097
3086
  path,
3098
3087
  (options.multiProject ? "**/" : "") + "open*.yaml"
3099
3088
  );
@@ -3107,18 +3096,18 @@ const createContainerSpecLikeBom = async (path, options) => {
3107
3096
  const privadoFiles = getAllFiles(path, ".privado/" + "*.json");
3108
3097
  // parse yaml manifest files
3109
3098
  if (dcFiles.length) {
3110
- for (let f of dcFiles) {
3099
+ for (const f of dcFiles) {
3111
3100
  if (DEBUG_MODE) {
3112
3101
  console.log(`Parsing ${f}`);
3113
3102
  }
3114
3103
  const dcData = readFileSync(f, { encoding: "utf-8" });
3115
- const imglist = await parseContainerSpecData(dcData);
3104
+ const imglist = parseContainerSpecData(dcData);
3116
3105
  if (imglist && imglist.length) {
3117
3106
  if (DEBUG_MODE) {
3118
3107
  console.log("Images identified in", f, "are", imglist);
3119
3108
  }
3120
3109
  for (const img of imglist) {
3121
- let commonProperties = [
3110
+ const commonProperties = [
3122
3111
  {
3123
3112
  name: "SrcFile",
3124
3113
  value: f
@@ -3239,12 +3228,12 @@ const createContainerSpecLikeBom = async (path, options) => {
3239
3228
  } // if
3240
3229
  // Parse openapi files
3241
3230
  if (oapiFiles.length) {
3242
- for (let af of oapiFiles) {
3231
+ for (const af of oapiFiles) {
3243
3232
  if (DEBUG_MODE) {
3244
3233
  console.log(`Parsing ${af}`);
3245
3234
  }
3246
3235
  const oaData = readFileSync(af, { encoding: "utf-8" });
3247
- const servlist = await parseOpenapiSpecData(oaData);
3236
+ const servlist = parseOpenapiSpecData(oaData);
3248
3237
  if (servlist && servlist.length) {
3249
3238
  // Inject SrcFile property
3250
3239
  for (const se of servlist) {
@@ -3265,14 +3254,14 @@ const createContainerSpecLikeBom = async (path, options) => {
3265
3254
  "Enriching your SBoM with information from privado.ai scan reports"
3266
3255
  );
3267
3256
  let rows = [["Classification", "Flow"]];
3268
- let config = {
3257
+ const config = {
3269
3258
  header: {
3270
3259
  alignment: "center",
3271
3260
  content: "Data Privacy Insights from privado.ai"
3272
3261
  },
3273
3262
  columns: [{ width: 50 }, { width: 10 }]
3274
3263
  };
3275
- for (let f of privadoFiles) {
3264
+ for (const f of privadoFiles) {
3276
3265
  if (DEBUG_MODE) {
3277
3266
  console.log(`Parsing ${f}`);
3278
3267
  }
@@ -3281,14 +3270,14 @@ const createContainerSpecLikeBom = async (path, options) => {
3281
3270
  if (servlist.length) {
3282
3271
  const aservice = servlist[0];
3283
3272
  if (aservice.data) {
3284
- for (let d of aservice.data) {
3273
+ for (const d of aservice.data) {
3285
3274
  rows.push([d.classification, d.flow]);
3286
3275
  }
3287
3276
  console.log(table(rows, config));
3288
3277
  }
3289
3278
  if (aservice.endpoints) {
3290
3279
  rows = [["Leaky Endpoints"]];
3291
- for (let e of aservice.endpoints) {
3280
+ for (const e of aservice.endpoints) {
3292
3281
  rows.push([e]);
3293
3282
  }
3294
3283
  console.log(
@@ -3350,7 +3339,7 @@ const createContainerSpecLikeBom = async (path, options) => {
3350
3339
  * @param path to the project
3351
3340
  * @param options Parse options from the cli
3352
3341
  */
3353
- const createPHPBom = async (path, options) => {
3342
+ export const createPHPBom = (path, options) => {
3354
3343
  const composerJsonFiles = getAllFiles(
3355
3344
  path,
3356
3345
  (options.multiProject ? "**/" : "") + "composer.json"
@@ -3383,14 +3372,14 @@ const createPHPBom = async (path, options) => {
3383
3372
  if (DEBUG_MODE) {
3384
3373
  console.log("Parsing version", versionResult.stdout);
3385
3374
  }
3386
- let tmpV = undefined;
3375
+ const tmpV = undefined;
3387
3376
  if (versionResult && versionResult.stdout) {
3388
3377
  versionResult.stdout.split(" ");
3389
3378
  }
3390
3379
  if (tmpV && tmpV.length > 1) {
3391
3380
  composerVersion = tmpV[1];
3392
3381
  }
3393
- for (let f of composerJsonFiles) {
3382
+ for (const f of composerJsonFiles) {
3394
3383
  const basePath = dirname(f);
3395
3384
  let args = [];
3396
3385
  if (composerVersion && !composerVersion.startsWith("1")) {
@@ -3416,11 +3405,11 @@ const createPHPBom = async (path, options) => {
3416
3405
  (options.multiProject ? "**/" : "") + "composer.lock"
3417
3406
  );
3418
3407
  if (composerLockFiles.length) {
3419
- for (let f of composerLockFiles) {
3408
+ for (const f of composerLockFiles) {
3420
3409
  if (DEBUG_MODE) {
3421
3410
  console.log(`Parsing ${f}`);
3422
3411
  }
3423
- let dlist = parseComposerLock(f);
3412
+ const dlist = parseComposerLock(f);
3424
3413
  if (dlist && dlist.length) {
3425
3414
  pkgList = pkgList.concat(dlist);
3426
3415
  }
@@ -3439,7 +3428,7 @@ const createPHPBom = async (path, options) => {
3439
3428
  * @param path to the project
3440
3429
  * @param options Parse options from the cli
3441
3430
  */
3442
- const createRubyBom = async (path, options) => {
3431
+ export const createRubyBom = async (path, options) => {
3443
3432
  const gemFiles = getAllFiles(
3444
3433
  path,
3445
3434
  (options.multiProject ? "**/" : "") + "Gemfile"
@@ -3450,9 +3439,9 @@ const createRubyBom = async (path, options) => {
3450
3439
  );
3451
3440
  let pkgList = [];
3452
3441
  const gemFileMode = gemFiles.length;
3453
- let gemLockMode = gemLockFiles.length;
3442
+ const gemLockMode = gemLockFiles.length;
3454
3443
  if (gemFileMode && !gemLockMode && options.installDeps) {
3455
- for (let f of gemFiles) {
3444
+ for (const f of gemFiles) {
3456
3445
  const basePath = dirname(f);
3457
3446
  console.log("Executing 'bundle install' in", basePath);
3458
3447
  const result = spawnSync("bundle", ["install"], {
@@ -3473,11 +3462,11 @@ const createRubyBom = async (path, options) => {
3473
3462
  (options.multiProject ? "**/" : "") + "Gemfile.lock"
3474
3463
  );
3475
3464
  if (gemLockFiles.length) {
3476
- for (let f of gemLockFiles) {
3465
+ for (const f of gemLockFiles) {
3477
3466
  if (DEBUG_MODE) {
3478
3467
  console.log(`Parsing ${f}`);
3479
3468
  }
3480
- let gemLockData = readFileSync(f, { encoding: "utf-8" });
3469
+ const gemLockData = readFileSync(f, { encoding: "utf-8" });
3481
3470
  const dlist = await parseGemfileLockData(gemLockData);
3482
3471
  if (dlist && dlist.length) {
3483
3472
  pkgList = pkgList.concat(dlist);
@@ -3497,8 +3486,9 @@ const createRubyBom = async (path, options) => {
3497
3486
  * @param path to the project
3498
3487
  * @param options Parse options from the cli
3499
3488
  */
3500
- const createCsharpBom = async (path, options) => {
3489
+ export const createCsharpBom = async (path, options) => {
3501
3490
  let manifestFiles = [];
3491
+ let pkgData = undefined;
3502
3492
  const csProjFiles = getAllFiles(
3503
3493
  path,
3504
3494
  (options.multiProject ? "**/" : "") + "*.csproj"
@@ -3522,7 +3512,7 @@ const createCsharpBom = async (path, options) => {
3522
3512
  let pkgList = [];
3523
3513
  if (nupkgFiles.length) {
3524
3514
  manifestFiles = manifestFiles.concat(nupkgFiles);
3525
- for (let nf of nupkgFiles) {
3515
+ for (const nf of nupkgFiles) {
3526
3516
  if (DEBUG_MODE) {
3527
3517
  console.log(`Parsing ${nf}`);
3528
3518
  }
@@ -3535,11 +3525,11 @@ const createCsharpBom = async (path, options) => {
3535
3525
  // project.assets.json parsing
3536
3526
  if (projAssetsFiles.length) {
3537
3527
  manifestFiles = manifestFiles.concat(projAssetsFiles);
3538
- for (let af of projAssetsFiles) {
3528
+ for (const af of projAssetsFiles) {
3539
3529
  if (DEBUG_MODE) {
3540
3530
  console.log(`Parsing ${af}`);
3541
3531
  }
3542
- let pkgData = readFileSync(af, { encoding: "utf-8" });
3532
+ pkgData = readFileSync(af, { encoding: "utf-8" });
3543
3533
  const dlist = await parseCsProjAssetsData(pkgData);
3544
3534
  if (dlist && dlist.length) {
3545
3535
  pkgList = pkgList.concat(dlist);
@@ -3548,11 +3538,11 @@ const createCsharpBom = async (path, options) => {
3548
3538
  } else if (pkgLockFiles.length) {
3549
3539
  manifestFiles = manifestFiles.concat(pkgLockFiles);
3550
3540
  // packages.lock.json from nuget
3551
- for (let af of pkgLockFiles) {
3541
+ for (const af of pkgLockFiles) {
3552
3542
  if (DEBUG_MODE) {
3553
3543
  console.log(`Parsing ${af}`);
3554
3544
  }
3555
- let pkgData = readFileSync(af, { encoding: "utf-8" });
3545
+ pkgData = readFileSync(af, { encoding: "utf-8" });
3556
3546
  const dlist = await parseCsPkgLockData(pkgData);
3557
3547
  if (dlist && dlist.length) {
3558
3548
  pkgList = pkgList.concat(dlist);
@@ -3561,11 +3551,11 @@ const createCsharpBom = async (path, options) => {
3561
3551
  } else if (pkgConfigFiles.length) {
3562
3552
  manifestFiles = manifestFiles.concat(pkgConfigFiles);
3563
3553
  // packages.config parsing
3564
- for (let f of pkgConfigFiles) {
3554
+ for (const f of pkgConfigFiles) {
3565
3555
  if (DEBUG_MODE) {
3566
3556
  console.log(`Parsing ${f}`);
3567
3557
  }
3568
- let pkgData = readFileSync(f, { encoding: "utf-8" });
3558
+ pkgData = readFileSync(f, { encoding: "utf-8" });
3569
3559
  // Remove byte order mark
3570
3560
  if (pkgData.charCodeAt(0) === 0xfeff) {
3571
3561
  pkgData = pkgData.slice(1);
@@ -3578,7 +3568,7 @@ const createCsharpBom = async (path, options) => {
3578
3568
  } else if (csProjFiles.length) {
3579
3569
  manifestFiles = manifestFiles.concat(csProjFiles);
3580
3570
  // .csproj parsing
3581
- for (let f of csProjFiles) {
3571
+ for (const f of csProjFiles) {
3582
3572
  if (DEBUG_MODE) {
3583
3573
  console.log(`Parsing ${f}`);
3584
3574
  }
@@ -3602,9 +3592,9 @@ const createCsharpBom = async (path, options) => {
3602
3592
  return {};
3603
3593
  };
3604
3594
 
3605
- const mergeDependencies = (dependencies, newDependencies) => {
3595
+ export const mergeDependencies = (dependencies, newDependencies) => {
3606
3596
  const deps_map = {};
3607
- let combinedDeps = dependencies.concat(newDependencies || []);
3597
+ const combinedDeps = dependencies.concat(newDependencies || []);
3608
3598
  for (const adep of combinedDeps) {
3609
3599
  if (!deps_map[adep.ref]) {
3610
3600
  deps_map[adep.ref] = new Set();
@@ -3613,7 +3603,7 @@ const mergeDependencies = (dependencies, newDependencies) => {
3613
3603
  deps_map[adep.ref].add(eachDepends);
3614
3604
  }
3615
3605
  }
3616
- let retlist = [];
3606
+ const retlist = [];
3617
3607
  for (const akey of Object.keys(deps_map)) {
3618
3608
  retlist.push({
3619
3609
  ref: akey,
@@ -3622,13 +3612,11 @@ const mergeDependencies = (dependencies, newDependencies) => {
3622
3612
  }
3623
3613
  return retlist;
3624
3614
  };
3625
- const _mergeDependencies = mergeDependencies;
3626
- export { _mergeDependencies as mergeDependencies };
3627
3615
 
3628
- const trimComponents = (components, format) => {
3616
+ export const trimComponents = (components, format) => {
3629
3617
  const keyCache = {};
3630
3618
  const filteredComponents = [];
3631
- for (let comp of components) {
3619
+ for (const comp of components) {
3632
3620
  if (format === "xml" && comp.component) {
3633
3621
  if (!keyCache[comp.component.purl]) {
3634
3622
  keyCache[comp.component.purl] = true;
@@ -3643,10 +3631,8 @@ const trimComponents = (components, format) => {
3643
3631
  }
3644
3632
  return filteredComponents;
3645
3633
  };
3646
- const _trimComponents = trimComponents;
3647
- export { _trimComponents as trimComponents };
3648
3634
 
3649
- const dedupeBom = (
3635
+ export const dedupeBom = (
3650
3636
  options,
3651
3637
  components,
3652
3638
  componentsXmls,
@@ -3694,8 +3680,6 @@ const dedupeBom = (
3694
3680
  }
3695
3681
  };
3696
3682
  };
3697
- const _dedupeBom = dedupeBom;
3698
- export { _dedupeBom as dedupeBom };
3699
3683
 
3700
3684
  /**
3701
3685
  * Function to create bom string for all languages
@@ -3703,7 +3687,7 @@ export { _dedupeBom as dedupeBom };
3703
3687
  * @param pathList list of to the project
3704
3688
  * @param options Parse options from the cli
3705
3689
  */
3706
- const createMultiXBom = async (pathList, options) => {
3690
+ export const createMultiXBom = async (pathList, options) => {
3707
3691
  let components = [];
3708
3692
  let dependencies = [];
3709
3693
  let componentsXmls = [];
@@ -3741,7 +3725,7 @@ const createMultiXBom = async (pathList, options) => {
3741
3725
  );
3742
3726
  }
3743
3727
  }
3744
- for (let path of pathList) {
3728
+ for (const path of pathList) {
3745
3729
  if (DEBUG_MODE) {
3746
3730
  console.log("Scanning", path);
3747
3731
  }
@@ -3830,7 +3814,7 @@ const createMultiXBom = async (pathList, options) => {
3830
3814
  listComponents(options, {}, bomData.bomJson.components, "cargo", "xml")
3831
3815
  );
3832
3816
  }
3833
- bomData = await createPHPBom(path, options);
3817
+ bomData = createPHPBom(path, options);
3834
3818
  if (bomData && bomData.bomJson && bomData.bomJson.components) {
3835
3819
  if (DEBUG_MODE) {
3836
3820
  console.log(
@@ -3954,7 +3938,7 @@ const createMultiXBom = async (pathList, options) => {
3954
3938
  listComponents(options, {}, bomData.bomJson.components, "conan", "xml")
3955
3939
  );
3956
3940
  }
3957
- bomData = await createClojureBom(path, options);
3941
+ bomData = createClojureBom(path, options);
3958
3942
  if (bomData && bomData.bomJson && bomData.bomJson.components) {
3959
3943
  if (DEBUG_MODE) {
3960
3944
  console.log(
@@ -4014,7 +3998,7 @@ const createMultiXBom = async (pathList, options) => {
4014
3998
  )
4015
3999
  );
4016
4000
  }
4017
- bomData = await createSwiftBom(path, options);
4001
+ bomData = createSwiftBom(path, options);
4018
4002
  if (
4019
4003
  bomData &&
4020
4004
  bomData.bomJson &&
@@ -4035,30 +4019,23 @@ const createMultiXBom = async (pathList, options) => {
4035
4019
  listComponents(options, {}, bomData.bomJson.components, "swift", "xml")
4036
4020
  );
4037
4021
  }
4038
- // jar scanning is quite slow so this is limited to only deep scans
4039
- if (options.deep) {
4040
- bomData = createJarBom(path, options);
4041
- if (bomData && bomData.bomJson && bomData.bomJson.components) {
4042
- if (DEBUG_MODE) {
4043
- console.log(
4044
- `Found ${bomData.bomJson.components.length} jar packages at ${path}`
4045
- );
4046
- }
4047
- components = components.concat(bomData.bomJson.components);
4048
- dependencies = dependencies.concat(bomData.bomJson.dependencies);
4049
- if (!parentComponent || !Object.keys(parentComponent).length) {
4050
- parentComponent = bomData.parentComponent;
4051
- }
4052
- componentsXmls = componentsXmls.concat(
4053
- listComponents(
4054
- options,
4055
- {},
4056
- bomData.bomJson.components,
4057
- "maven",
4058
- "xml"
4059
- )
4022
+ // Jar scanning is enabled by default
4023
+ // See #330
4024
+ bomData = createJarBom(path, options);
4025
+ if (bomData && bomData.bomJson && bomData.bomJson.components) {
4026
+ if (DEBUG_MODE) {
4027
+ console.log(
4028
+ `Found ${bomData.bomJson.components.length} jar packages at ${path}`
4060
4029
  );
4061
4030
  }
4031
+ components = components.concat(bomData.bomJson.components);
4032
+ dependencies = dependencies.concat(bomData.bomJson.dependencies);
4033
+ if (!parentComponent || !Object.keys(parentComponent).length) {
4034
+ parentComponent = bomData.parentComponent;
4035
+ }
4036
+ componentsXmls = componentsXmls.concat(
4037
+ listComponents(options, {}, bomData.bomJson.components, "maven", "xml")
4038
+ );
4062
4039
  }
4063
4040
  } // for
4064
4041
  if (options.lastWorkingDir && options.lastWorkingDir !== "") {
@@ -4094,7 +4071,7 @@ const createMultiXBom = async (pathList, options) => {
4094
4071
  * @param path to the project
4095
4072
  * @param options Parse options from the cli
4096
4073
  */
4097
- const createXBom = async (path, options) => {
4074
+ export const createXBom = async (path, options) => {
4098
4075
  try {
4099
4076
  accessSync(path, constants.R_OK);
4100
4077
  } catch (err) {
@@ -4115,12 +4092,12 @@ const createXBom = async (path, options) => {
4115
4092
  (options.multiProject ? "**/" : "") + "pom.xml"
4116
4093
  );
4117
4094
  // gradle
4118
- let gradleFiles = getAllFiles(
4095
+ const gradleFiles = getAllFiles(
4119
4096
  path,
4120
4097
  (options.multiProject ? "**/" : "") + "build.gradle*"
4121
4098
  );
4122
4099
  // scala sbt
4123
- let sbtFiles = getAllFiles(
4100
+ const sbtFiles = getAllFiles(
4124
4101
  path,
4125
4102
  (options.multiProject ? "**/" : "") + "{build.sbt,Build.scala}*"
4126
4103
  );
@@ -4192,7 +4169,7 @@ const createXBom = async (path, options) => {
4192
4169
  (options.multiProject ? "**/" : "") + "composer.lock"
4193
4170
  );
4194
4171
  if (composerJsonFiles.length || composerLockFiles.length) {
4195
- return await createPHPBom(path, options);
4172
+ return createPHPBom(path, options);
4196
4173
  }
4197
4174
 
4198
4175
  // Ruby
@@ -4271,7 +4248,7 @@ const createXBom = async (path, options) => {
4271
4248
  (options.multiProject ? "**/" : "") + "project.clj"
4272
4249
  );
4273
4250
  if (ednFiles.length || leinFiles.length) {
4274
- return await createClojureBom(path, options);
4251
+ return createClojureBom(path, options);
4275
4252
  }
4276
4253
 
4277
4254
  // GitHub actions
@@ -4338,7 +4315,7 @@ const createXBom = async (path, options) => {
4338
4315
  (options.multiProject ? "**/" : "") + "Package.resolved"
4339
4316
  );
4340
4317
  if (swiftFiles.length || pkgResolvedFiles.length) {
4341
- return await createSwiftBom(path, options);
4318
+ return createSwiftBom(path, options);
4342
4319
  }
4343
4320
  };
4344
4321
 
@@ -4474,20 +4451,20 @@ export const createBom = async (path, options) => {
4474
4451
  return await createJavaBom(path, options);
4475
4452
  case "jar":
4476
4453
  options.multiProject = true;
4477
- return await createJarBom(path, options);
4454
+ return createJarBom(path, options);
4478
4455
  case "gradle-index":
4479
4456
  case "gradle-cache":
4480
4457
  options.multiProject = true;
4481
- return await createJarBom(GRADLE_CACHE_DIR, options);
4458
+ return createJarBom(GRADLE_CACHE_DIR, options);
4482
4459
  case "sbt-index":
4483
4460
  case "sbt-cache":
4484
4461
  options.multiProject = true;
4485
- return await createJarBom(SBT_CACHE_DIR, options);
4462
+ return createJarBom(SBT_CACHE_DIR, options);
4486
4463
  case "maven-index":
4487
4464
  case "maven-cache":
4488
4465
  case "maven-repo":
4489
4466
  options.multiProject = true;
4490
- return await createJarBom(join(homedir(), ".m2", "repository"), options);
4467
+ return createJarBom(join(homedir(), ".m2", "repository"), options);
4491
4468
  case "nodejs":
4492
4469
  case "js":
4493
4470
  case "javascript":
@@ -4510,7 +4487,7 @@ export const createBom = async (path, options) => {
4510
4487
  return await createRustBom(path, options);
4511
4488
  case "php":
4512
4489
  options.multiProject = true;
4513
- return await createPHPBom(path, options);
4490
+ return createPHPBom(path, options);
4514
4491
  case "ruby":
4515
4492
  options.multiProject = true;
4516
4493
  return await createRubyBom(path, options);
@@ -4545,7 +4522,7 @@ export const createBom = async (path, options) => {
4545
4522
  case "clj":
4546
4523
  case "leiningen":
4547
4524
  options.multiProject = true;
4548
- return await createClojureBom(path, options);
4525
+ return createClojureBom(path, options);
4549
4526
  case "github":
4550
4527
  case "actions":
4551
4528
  options.multiProject = true;
@@ -4587,7 +4564,7 @@ export const createBom = async (path, options) => {
4587
4564
  return await createCloudBuildBom(path, options);
4588
4565
  case "swift":
4589
4566
  options.multiProject = true;
4590
- return await createSwiftBom(path, options);
4567
+ return createSwiftBom(path, options);
4591
4568
  default:
4592
4569
  // In recurse mode return multi-language Bom
4593
4570
  // https://github.com/cyclonedx/cdxgen/issues/95
@@ -4606,7 +4583,7 @@ export const createBom = async (path, options) => {
4606
4583
  * @param bomContents BOM Json
4607
4584
  */
4608
4585
  export async function submitBom(args, bomContents) {
4609
- let serverUrl = args.serverUrl.replace(/\/$/, "") + "/api/v1/bom";
4586
+ const serverUrl = args.serverUrl.replace(/\/$/, "") + "/api/v1/bom";
4610
4587
  let encodedBomContents = Buffer.from(JSON.stringify(bomContents)).toString(
4611
4588
  "base64"
4612
4589
  );
@@ -4681,3 +4658,38 @@ export async function submitBom(args, bomContents) {
4681
4658
  }
4682
4659
  }
4683
4660
  }
4661
+
4662
+ /**
4663
+ * Validate the generated bom using jsonschema
4664
+ *
4665
+ * @param {object} bomJson content
4666
+ */
4667
+ export const validateBom = (bomJson) => {
4668
+ if (!bomJson) {
4669
+ return true;
4670
+ }
4671
+ const schema = JSON.parse(
4672
+ readFileSync(join(dirName, "data", "bom-1.5.schema.json"))
4673
+ );
4674
+ const defsSchema = JSON.parse(
4675
+ readFileSync(join(dirName, "data", "jsf-0.82.schema.json"))
4676
+ );
4677
+ const spdxSchema = JSON.parse(
4678
+ readFileSync(join(dirName, "data", "spdx.schema.json"))
4679
+ );
4680
+ const ajv = new Ajv({
4681
+ schemas: [schema, defsSchema, spdxSchema],
4682
+ strict: false,
4683
+ logger: false
4684
+ });
4685
+ addFormats(ajv);
4686
+ const validate = ajv.getSchema(
4687
+ "http://cyclonedx.org/schema/bom-1.5.schema.json"
4688
+ );
4689
+ const isValid = validate(bomJson);
4690
+ if (!isValid) {
4691
+ console.log(validate.errors);
4692
+ return false;
4693
+ }
4694
+ return true;
4695
+ };