@cyclonedx/cdxgen 9.0.1 → 9.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,18 +1315,22 @@ 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);
1319
+ const defaultDepTaskArgs = ["-q", "--console", "plain", "--build-cache"];
1360
1320
  allProjects.push(parentComponent);
1361
- for (let sp of allProjects) {
1321
+ let depTaskWithArgs = ["dependencies"];
1322
+ if (process.env.GRADLE_DEPENDENCY_TASK) {
1323
+ depTaskWithArgs = process.env.GRADLE_DEPENDENCY_TASK.split(" ");
1324
+ }
1325
+ for (const sp of allProjects) {
1362
1326
  let gradleDepArgs = [
1363
1327
  sp.purl === parentComponent.purl
1364
- ? "dependencies"
1365
- : `:${sp.name}:dependencies`,
1366
- "-q",
1367
- "--console",
1368
- "plain",
1369
- "--build-cache"
1328
+ ? depTaskWithArgs[0]
1329
+ : `:${sp.name}:${depTaskWithArgs[0]}`
1370
1330
  ];
1331
+ gradleDepArgs = gradleDepArgs
1332
+ .concat(depTaskWithArgs.slice(1))
1333
+ .concat(defaultDepTaskArgs);
1371
1334
  // Support custom GRADLE_ARGS such as --configuration runtimeClassPath
1372
1335
  if (process.env.GRADLE_ARGS) {
1373
1336
  const addArgs = process.env.GRADLE_ARGS.split(" ");
@@ -1452,13 +1415,13 @@ const createJavaBom = async (path, options) => {
1452
1415
 
1453
1416
  // Bazel
1454
1417
  // Look for the BUILD file only in the root directory
1455
- let bazelFiles = getAllFiles(path, "BUILD");
1418
+ const bazelFiles = getAllFiles(path, "BUILD");
1456
1419
  if (bazelFiles && bazelFiles.length) {
1457
1420
  let BAZEL_CMD = "bazel";
1458
1421
  if (process.env.BAZEL_HOME) {
1459
1422
  BAZEL_CMD = join(process.env.BAZEL_HOME, "bin", "bazel");
1460
1423
  }
1461
- for (let f of bazelFiles) {
1424
+ for (const f of bazelFiles) {
1462
1425
  const basePath = dirname(f);
1463
1426
  // Invoke bazel build first
1464
1427
  const bazelTarget = process.env.BAZEL_TARGET || ":all";
@@ -1500,7 +1463,7 @@ const createJavaBom = async (path, options) => {
1500
1463
  console.error(result.stdout, result.stderr);
1501
1464
  options.failOnError && process.exit(1);
1502
1465
  }
1503
- let stdout = result.stdout;
1466
+ const stdout = result.stdout;
1504
1467
  if (stdout) {
1505
1468
  const cmdOutput = Buffer.from(stdout).toString();
1506
1469
  const dlist = parseBazelSkyframe(cmdOutput);
@@ -1545,7 +1508,7 @@ const createJavaBom = async (path, options) => {
1545
1508
  );
1546
1509
 
1547
1510
  let sbtProjects = [];
1548
- for (let i in sbtProjectFiles) {
1511
+ for (const i in sbtProjectFiles) {
1549
1512
  // parent dir of sbtProjectFile is the `project` directory
1550
1513
  // parent dir of `project` is the sbt root project directory
1551
1514
  const baseDir = dirname(dirname(sbtProjectFiles[i]));
@@ -1558,7 +1521,7 @@ const createJavaBom = async (path, options) => {
1558
1521
  path,
1559
1522
  (options.multiProject ? "**/" : "") + "*.sbt"
1560
1523
  );
1561
- for (let i in sbtProjectFiles) {
1524
+ for (const i in sbtProjectFiles) {
1562
1525
  const baseDir = dirname(sbtProjectFiles[i]);
1563
1526
  sbtProjects = sbtProjects.concat(baseDir);
1564
1527
  }
@@ -1566,7 +1529,7 @@ const createJavaBom = async (path, options) => {
1566
1529
 
1567
1530
  sbtProjects = [...new Set(sbtProjects)]; // eliminate duplicates
1568
1531
 
1569
- let sbtLockFiles = getAllFiles(
1532
+ const sbtLockFiles = getAllFiles(
1570
1533
  path,
1571
1534
  (options.multiProject ? "**/" : "") + "build.sbt.lock"
1572
1535
  );
@@ -1575,15 +1538,15 @@ const createJavaBom = async (path, options) => {
1575
1538
  let pkgList = [];
1576
1539
  // If the project use sbt lock files
1577
1540
  if (sbtLockFiles && sbtLockFiles.length) {
1578
- for (let f of sbtLockFiles) {
1541
+ for (const f of sbtLockFiles) {
1579
1542
  const dlist = parseSbtLock(f);
1580
1543
  if (dlist && dlist.length) {
1581
1544
  pkgList = pkgList.concat(dlist);
1582
1545
  }
1583
1546
  }
1584
1547
  } else {
1585
- let SBT_CMD = process.env.SBT_CMD || "sbt";
1586
- let sbtVersion = determineSbtVersion(path);
1548
+ const SBT_CMD = process.env.SBT_CMD || "sbt";
1549
+ const sbtVersion = determineSbtVersion(path);
1587
1550
  if (DEBUG_MODE) {
1588
1551
  console.log("Detected sbt version: " + sbtVersion);
1589
1552
  }
@@ -1596,11 +1559,11 @@ const createJavaBom = async (path, options) => {
1596
1559
  const useSlashSyntax = gte(sbtVersion, "1.5.0");
1597
1560
  const isDependencyTreeBuiltIn =
1598
1561
  sbtVersion != null && gte(sbtVersion, "1.4.0");
1599
- let tempDir = mkdtempSync(join(tmpdir(), "cdxsbt-"));
1600
- let tempSbtgDir = mkdtempSync(join(tmpdir(), "cdxsbtg-"));
1562
+ const tempDir = mkdtempSync(join(tmpdir(), "cdxsbt-"));
1563
+ const tempSbtgDir = mkdtempSync(join(tmpdir(), "cdxsbtg-"));
1601
1564
  mkdirSync(tempSbtgDir, { recursive: true });
1602
1565
  // Create temporary plugins file
1603
- let tempSbtPlugins = join(tempSbtgDir, "dep-plugins.sbt");
1566
+ const tempSbtPlugins = join(tempSbtgDir, "dep-plugins.sbt");
1604
1567
 
1605
1568
  // Requires a custom version of `sbt-dependency-graph` that
1606
1569
  // supports `--append` for `toFile` subtask.
@@ -1613,9 +1576,9 @@ const createJavaBom = async (path, options) => {
1613
1576
  }
1614
1577
  writeFileSync(tempSbtPlugins, sbtPluginDefinition);
1615
1578
 
1616
- for (let i in sbtProjects) {
1579
+ for (const i in sbtProjects) {
1617
1580
  const basePath = sbtProjects[i];
1618
- let dlFile = join(tempDir, "dl-" + i + ".tmp");
1581
+ const dlFile = join(tempDir, "dl-" + i + ".tmp");
1619
1582
  console.log(
1620
1583
  "Executing",
1621
1584
  SBT_CMD,
@@ -1624,8 +1587,8 @@ const createJavaBom = async (path, options) => {
1624
1587
  "using plugins",
1625
1588
  tempSbtgDir
1626
1589
  );
1627
- var sbtArgs = [];
1628
- var pluginFile = null;
1590
+ let sbtArgs = [];
1591
+ let pluginFile = null;
1629
1592
  if (standalonePluginFile) {
1630
1593
  sbtArgs = [
1631
1594
  `-addPluginSbtFile=${tempSbtPlugins}`,
@@ -1709,7 +1672,7 @@ const createJavaBom = async (path, options) => {
1709
1672
  * @param path to the project
1710
1673
  * @param options Parse options from the cli
1711
1674
  */
1712
- const createNodejsBom = async (path, options) => {
1675
+ export const createNodejsBom = async (path, options) => {
1713
1676
  let pkgList = [];
1714
1677
  let manifestFiles = [];
1715
1678
  let dependencies = [];
@@ -1720,7 +1683,7 @@ const createNodejsBom = async (path, options) => {
1720
1683
  const pkgJsonFiles = getAllFiles(path, "**/package.json");
1721
1684
  // Are there any package.json files in the container?
1722
1685
  if (pkgJsonFiles.length) {
1723
- for (let pj of pkgJsonFiles) {
1686
+ for (const pj of pkgJsonFiles) {
1724
1687
  const dlist = await parsePkgJson(pj);
1725
1688
  if (dlist && dlist.length) {
1726
1689
  pkgList = pkgList.concat(dlist);
@@ -1776,7 +1739,7 @@ const createNodejsBom = async (path, options) => {
1776
1739
  // Parse min js files
1777
1740
  if (minJsFiles && minJsFiles.length) {
1778
1741
  manifestFiles = manifestFiles.concat(minJsFiles);
1779
- for (let f of minJsFiles) {
1742
+ for (const f of minJsFiles) {
1780
1743
  const dlist = await parseMinJs(f);
1781
1744
  if (dlist && dlist.length) {
1782
1745
  pkgList = pkgList.concat(dlist);
@@ -1786,7 +1749,7 @@ const createNodejsBom = async (path, options) => {
1786
1749
  // Parse bower json files
1787
1750
  if (bowerFiles && bowerFiles.length) {
1788
1751
  manifestFiles = manifestFiles.concat(bowerFiles);
1789
- for (let f of bowerFiles) {
1752
+ for (const f of bowerFiles) {
1790
1753
  const dlist = await parseBowerJson(f);
1791
1754
  if (dlist && dlist.length) {
1792
1755
  pkgList = pkgList.concat(dlist);
@@ -1795,7 +1758,7 @@ const createNodejsBom = async (path, options) => {
1795
1758
  }
1796
1759
  if (pnpmLockFiles && pnpmLockFiles.length) {
1797
1760
  manifestFiles = manifestFiles.concat(pnpmLockFiles);
1798
- for (let f of pnpmLockFiles) {
1761
+ for (const f of pnpmLockFiles) {
1799
1762
  const basePath = dirname(f);
1800
1763
  // Determine the parent component
1801
1764
  const packageJsonF = join(basePath, "package.json");
@@ -1841,7 +1804,7 @@ const createNodejsBom = async (path, options) => {
1841
1804
  }
1842
1805
  if (pkgLockFiles && pkgLockFiles.length) {
1843
1806
  manifestFiles = manifestFiles.concat(pkgLockFiles);
1844
- for (let f of pkgLockFiles) {
1807
+ for (const f of pkgLockFiles) {
1845
1808
  if (DEBUG_MODE) {
1846
1809
  console.log(`Parsing ${f}`);
1847
1810
  }
@@ -1917,7 +1880,7 @@ const createNodejsBom = async (path, options) => {
1917
1880
  }
1918
1881
  if (yarnLockFiles && yarnLockFiles.length) {
1919
1882
  manifestFiles = manifestFiles.concat(yarnLockFiles);
1920
- for (let f of yarnLockFiles) {
1883
+ for (const f of yarnLockFiles) {
1921
1884
  if (DEBUG_MODE) {
1922
1885
  console.log(`Parsing ${f}`);
1923
1886
  }
@@ -1993,7 +1956,7 @@ const createNodejsBom = async (path, options) => {
1993
1956
  "**/package.json"
1994
1957
  );
1995
1958
  manifestFiles = manifestFiles.concat(pkgJsonFiles);
1996
- for (let pkgjf of pkgJsonFiles) {
1959
+ for (const pkgjf of pkgJsonFiles) {
1997
1960
  const dlist = await parsePkgJson(pkgjf);
1998
1961
  if (dlist && dlist.length) {
1999
1962
  pkgList = pkgList.concat(dlist);
@@ -2026,9 +1989,13 @@ const createNodejsBom = async (path, options) => {
2026
1989
  * @param path to the project
2027
1990
  * @param options Parse options from the cli
2028
1991
  */
2029
- const createPythonBom = async (path, options) => {
1992
+ export const createPythonBom = async (path, options) => {
2030
1993
  let allImports = {};
2031
1994
  let metadataFilename = "";
1995
+ let dependencies = [];
1996
+ let pkgList = [];
1997
+ const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-venv-"));
1998
+ const parentComponent = createDefaultParentComponent(path);
2032
1999
  const pipenvMode = existsSync(join(path, "Pipfile"));
2033
2000
  const poetryFiles = getAllFiles(
2034
2001
  path,
@@ -2054,7 +2021,6 @@ const createPythonBom = async (path, options) => {
2054
2021
  path,
2055
2022
  (options.multiProject ? "**/" : "") + "*.egg-info"
2056
2023
  );
2057
- let pkgList = [];
2058
2024
  const setupPy = join(path, "setup.py");
2059
2025
  const pyProjectFile = join(path, "pyproject.toml");
2060
2026
  const pyProjectMode = existsSync(pyProjectFile);
@@ -2065,7 +2031,7 @@ const createPythonBom = async (path, options) => {
2065
2031
  // Poetry sets up its own virtual env containing site-packages so
2066
2032
  // we give preference to poetry lock file. Issue# 129
2067
2033
  if (poetryMode) {
2068
- for (let f of poetryFiles) {
2034
+ for (const f of poetryFiles) {
2069
2035
  const lockData = readFileSync(f, { encoding: "utf-8" });
2070
2036
  const dlist = await parsePoetrylockData(lockData);
2071
2037
  if (dlist && dlist.length) {
@@ -2078,7 +2044,7 @@ const createPythonBom = async (path, options) => {
2078
2044
  });
2079
2045
  } else if (metadataFiles && metadataFiles.length) {
2080
2046
  // dist-info directories
2081
- for (let mf of metadataFiles) {
2047
+ for (const mf of metadataFiles) {
2082
2048
  const mData = readFileSync(mf, {
2083
2049
  encoding: "utf-8"
2084
2050
  });
@@ -2090,7 +2056,7 @@ const createPythonBom = async (path, options) => {
2090
2056
  }
2091
2057
  // .whl files. Zip file containing dist-info directory
2092
2058
  if (whlFiles && whlFiles.length) {
2093
- for (let wf of whlFiles) {
2059
+ for (const wf of whlFiles) {
2094
2060
  const mData = await readZipEntry(wf, "METADATA");
2095
2061
  if (mData) {
2096
2062
  const dlist = parseBdistMetadata(mData);
@@ -2102,7 +2068,7 @@ const createPythonBom = async (path, options) => {
2102
2068
  }
2103
2069
  // .egg-info files
2104
2070
  if (eggInfoFiles && eggInfoFiles.length) {
2105
- for (let ef of eggInfoFiles) {
2071
+ for (const ef of eggInfoFiles) {
2106
2072
  const dlist = parseBdistMetadata(readFileSync(ef, { encoding: "utf-8" }));
2107
2073
  if (dlist && dlist.length) {
2108
2074
  pkgList = pkgList.concat(dlist);
@@ -2126,15 +2092,15 @@ const createPythonBom = async (path, options) => {
2126
2092
  } else if (requirementsMode) {
2127
2093
  metadataFilename = "requirements.txt";
2128
2094
  if (reqFiles && reqFiles.length) {
2129
- for (let f of reqFiles) {
2095
+ for (const f of reqFiles) {
2130
2096
  const basePath = dirname(f);
2131
2097
  let reqData = undefined;
2132
2098
  let frozen = false;
2133
2099
  // Attempt to pip freeze in a virtualenv to improve precision
2134
2100
  if (options.installDeps) {
2135
- const dlist = await executePipFreezeInVenv(basePath, f);
2136
- if (dlist && dlist.length) {
2137
- pkgList = pkgList.concat(dlist);
2101
+ const pkgMap = getPipFrozenTree(basePath, f, tempDir);
2102
+ if (pkgMap.pkgList && pkgMap.pkgList.length) {
2103
+ pkgList = pkgList.concat(pkgMap.pkgList);
2138
2104
  frozen = true;
2139
2105
  }
2140
2106
  }
@@ -2154,7 +2120,7 @@ const createPythonBom = async (path, options) => {
2154
2120
  } // for
2155
2121
  metadataFilename = reqFiles.join(", ");
2156
2122
  } else if (reqDirFiles && reqDirFiles.length) {
2157
- for (let j in reqDirFiles) {
2123
+ for (const j in reqDirFiles) {
2158
2124
  const f = reqDirFiles[j];
2159
2125
  const reqData = readFileSync(f, { encoding: "utf-8" });
2160
2126
  const dlist = await parseReqFile(reqData, false);
@@ -2168,27 +2134,48 @@ const createPythonBom = async (path, options) => {
2168
2134
  }
2169
2135
  // Use atom in requirements, setup.py and pyproject.toml mode
2170
2136
  if (requirementsMode || setupPyMode || pyProjectMode) {
2171
- let dlist = undefined;
2172
2137
  /**
2173
2138
  * The order of preference is pyproject.toml (newer) and then setup.py
2174
2139
  */
2175
2140
  if (options.installDeps) {
2141
+ let pkgMap = undefined;
2176
2142
  if (pyProjectMode) {
2177
- dlist = await executePipFreezeInVenv(path, pyProjectFile);
2143
+ pkgMap = getPipFrozenTree(path, pyProjectFile, tempDir);
2178
2144
  } else if (setupPyMode) {
2179
- dlist = await executePipFreezeInVenv(path, setupPy);
2145
+ pkgMap = getPipFrozenTree(path, setupPy, tempDir);
2146
+ } else {
2147
+ pkgMap = getPipFrozenTree(path, undefined, tempDir);
2148
+ }
2149
+ // Get the imported modules and a dedupe list of packages
2150
+ const parentDependsOn = [];
2151
+ const retMap = await getPyModules(path, pkgList);
2152
+ if (retMap.pkgList && retMap.pkgList.length) {
2153
+ pkgList = pkgList.concat(retMap.pkgList);
2154
+ for (const p of retMap.pkgList) {
2155
+ parentDependsOn.push(`pkg:pypi/${p.name}@${p.version}`);
2156
+ }
2180
2157
  }
2181
- if (dlist && dlist.length) {
2182
- pkgList = pkgList.concat(dlist);
2158
+ if (retMap.dependenciesList) {
2159
+ dependencies = mergeDependencies(dependencies, retMap.dependenciesList);
2183
2160
  }
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 };
2161
+ if (retMap.allImports) {
2162
+ allImports = { ...allImports, ...retMap.allImports };
2163
+ }
2164
+ // Complete the dependency tree by making parent component depend on the first level
2165
+ for (const p of pkgMap.rootList) {
2166
+ parentDependsOn.push(`pkg:pypi/${p.name}@${p.version}`);
2167
+ }
2168
+ if (pkgMap.pkgList && pkgMap.pkgList.length) {
2169
+ pkgList = pkgList.concat(pkgMap.pkgList);
2170
+ }
2171
+ if (pkgMap.dependenciesList) {
2172
+ dependencies = mergeDependencies(dependencies, pkgMap.dependenciesList);
2173
+ }
2174
+ const pdependencies = {
2175
+ ref: parentComponent.purl,
2176
+ dependsOn: parentDependsOn
2177
+ };
2178
+ dependencies.splice(0, 0, pdependencies);
2192
2179
  }
2193
2180
  }
2194
2181
  // Final fallback is to manually parse setup.py if we still
@@ -2200,10 +2187,16 @@ const createPythonBom = async (path, options) => {
2200
2187
  pkgList = pkgList.concat(dlist);
2201
2188
  }
2202
2189
  }
2190
+ // Clean up
2191
+ if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
2192
+ rmSync(tempDir, { recursive: true, force: true });
2193
+ }
2203
2194
  return buildBomNSData(options, pkgList, "pypi", {
2204
2195
  allImports,
2205
2196
  src: path,
2206
- filename: metadataFilename
2197
+ filename: metadataFilename,
2198
+ dependencies,
2199
+ parentComponent
2207
2200
  });
2208
2201
  };
2209
2202
 
@@ -2213,7 +2206,7 @@ const createPythonBom = async (path, options) => {
2213
2206
  * @param path to the project
2214
2207
  * @param options Parse options from the cli
2215
2208
  */
2216
- const createGoBom = async (path, options) => {
2209
+ export const createGoBom = async (path, options) => {
2217
2210
  let pkgList = [];
2218
2211
  // Is this a binary file
2219
2212
  let maybeBinary = false;
@@ -2230,8 +2223,8 @@ const createGoBom = async (path, options) => {
2230
2223
  }
2231
2224
  // Since this pkg list is derived from the binary mark them as used.
2232
2225
  const allImports = {};
2233
- for (let mpkg of pkgList) {
2234
- let pkgFullName = `${mpkg.group}/${mpkg.name}`;
2226
+ for (const mpkg of pkgList) {
2227
+ const pkgFullName = `${mpkg.group}/${mpkg.name}`;
2235
2228
  allImports[pkgFullName] = true;
2236
2229
  }
2237
2230
  return buildBomNSData(options, pkgList, "golang", {
@@ -2255,7 +2248,7 @@ const createGoBom = async (path, options) => {
2255
2248
  "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
2249
  "Set USE_GOSUM=false to generate BOMs using go.mod as the dependency source of truth."
2257
2250
  );
2258
- for (let f of gosumFiles) {
2251
+ for (const f of gosumFiles) {
2259
2252
  if (DEBUG_MODE) {
2260
2253
  console.log(`Parsing ${f}`);
2261
2254
  }
@@ -2274,7 +2267,7 @@ const createGoBom = async (path, options) => {
2274
2267
  // If USE_GOSUM is false, generate BOM components using go.mod.
2275
2268
  const gosumMap = {};
2276
2269
  if (gosumFiles.length) {
2277
- for (let f of gosumFiles) {
2270
+ for (const f of gosumFiles) {
2278
2271
  if (DEBUG_MODE) {
2279
2272
  console.log(`Parsing ${f}`);
2280
2273
  }
@@ -2303,7 +2296,7 @@ const createGoBom = async (path, options) => {
2303
2296
  let shouldManuallyParse = false;
2304
2297
  // Use the go list -deps and go mod why commands to generate a good quality BoM for non-docker invocations
2305
2298
  if (!["docker", "oci", "os"].includes(options.projectType)) {
2306
- for (let f of gomodFiles) {
2299
+ for (const f of gomodFiles) {
2307
2300
  const basePath = dirname(f);
2308
2301
  // Ignore vendor packages
2309
2302
  if (basePath.includes("/vendor/") || basePath.includes("/build/")) {
@@ -2349,11 +2342,11 @@ const createGoBom = async (path, options) => {
2349
2342
  );
2350
2343
  }
2351
2344
  // Using go mod why detect required packages
2352
- for (let apkg of pkgList) {
2345
+ for (const apkg of pkgList) {
2353
2346
  if (circuitBreak) {
2354
2347
  break;
2355
2348
  }
2356
- let pkgFullName = `${apkg.name}`;
2349
+ const pkgFullName = `${apkg.name}`;
2357
2350
  if (apkg.scope === "required") {
2358
2351
  allImports[pkgFullName] = true;
2359
2352
  continue;
@@ -2375,7 +2368,7 @@ const createGoBom = async (path, options) => {
2375
2368
  const mstdout = mresult.stdout;
2376
2369
  if (mstdout) {
2377
2370
  const cmdOutput = Buffer.from(mstdout).toString();
2378
- let whyPkg = parseGoModWhy(cmdOutput);
2371
+ const whyPkg = parseGoModWhy(cmdOutput);
2379
2372
  if (whyPkg == pkgFullName) {
2380
2373
  allImports[pkgFullName] = true;
2381
2374
  }
@@ -2399,7 +2392,7 @@ const createGoBom = async (path, options) => {
2399
2392
  "Manually parsing go.mod files. The resultant BoM would be incomplete."
2400
2393
  );
2401
2394
  }
2402
- for (let f of gomodFiles) {
2395
+ for (const f of gomodFiles) {
2403
2396
  if (DEBUG_MODE) {
2404
2397
  console.log(`Parsing ${f}`);
2405
2398
  }
@@ -2414,7 +2407,7 @@ const createGoBom = async (path, options) => {
2414
2407
  filename: gomodFiles.join(", ")
2415
2408
  });
2416
2409
  } else if (gopkgLockFiles.length) {
2417
- for (let f of gopkgLockFiles) {
2410
+ for (const f of gopkgLockFiles) {
2418
2411
  if (DEBUG_MODE) {
2419
2412
  console.log(`Parsing ${f}`);
2420
2413
  }
@@ -2440,7 +2433,7 @@ const createGoBom = async (path, options) => {
2440
2433
  * @param path to the project
2441
2434
  * @param options Parse options from the cli
2442
2435
  */
2443
- const createRustBom = async (path, options) => {
2436
+ export const createRustBom = async (path, options) => {
2444
2437
  let pkgList = [];
2445
2438
  // Is this a binary file
2446
2439
  let maybeBinary = false;
@@ -2457,8 +2450,8 @@ const createRustBom = async (path, options) => {
2457
2450
  }
2458
2451
  // Since this pkg list is derived from the binary mark them as used.
2459
2452
  const allImports = {};
2460
- for (let mpkg of pkgList) {
2461
- let pkgFullName = `${mpkg.group}/${mpkg.name}`;
2453
+ for (const mpkg of pkgList) {
2454
+ const pkgFullName = `${mpkg.group}/${mpkg.name}`;
2462
2455
  allImports[pkgFullName] = true;
2463
2456
  }
2464
2457
  return buildBomNSData(options, pkgList, "cargo", {
@@ -2476,9 +2469,9 @@ const createRustBom = async (path, options) => {
2476
2469
  (options.multiProject ? "**/" : "") + "Cargo.toml"
2477
2470
  );
2478
2471
  const cargoMode = cargoFiles.length;
2479
- let cargoLockMode = cargoLockFiles.length;
2472
+ const cargoLockMode = cargoLockFiles.length;
2480
2473
  if (cargoMode && !cargoLockMode) {
2481
- for (let f of cargoFiles) {
2474
+ for (const f of cargoFiles) {
2482
2475
  if (DEBUG_MODE) {
2483
2476
  console.log(`Parsing ${f}`);
2484
2477
  }
@@ -2499,7 +2492,7 @@ const createRustBom = async (path, options) => {
2499
2492
  (options.multiProject ? "**/" : "") + "Cargo.lock"
2500
2493
  );
2501
2494
  if (cargoLockFiles.length) {
2502
- for (let f of cargoLockFiles) {
2495
+ for (const f of cargoLockFiles) {
2503
2496
  if (DEBUG_MODE) {
2504
2497
  console.log(`Parsing ${f}`);
2505
2498
  }
@@ -2523,7 +2516,7 @@ const createRustBom = async (path, options) => {
2523
2516
  * @param path to the project
2524
2517
  * @param options Parse options from the cli
2525
2518
  */
2526
- const createDartBom = async (path, options) => {
2519
+ export const createDartBom = async (path, options) => {
2527
2520
  const pubFiles = getAllFiles(
2528
2521
  path,
2529
2522
  (options.multiProject ? "**/" : "") + "pubspec.lock"
@@ -2534,7 +2527,7 @@ const createDartBom = async (path, options) => {
2534
2527
  );
2535
2528
  let pkgList = [];
2536
2529
  if (pubFiles.length) {
2537
- for (let f of pubFiles) {
2530
+ for (const f of pubFiles) {
2538
2531
  if (DEBUG_MODE) {
2539
2532
  console.log(`Parsing ${f}`);
2540
2533
  }
@@ -2549,12 +2542,12 @@ const createDartBom = async (path, options) => {
2549
2542
  filename: pubFiles.join(", ")
2550
2543
  });
2551
2544
  } else if (pubSpecYamlFiles.length) {
2552
- for (let f of pubSpecYamlFiles) {
2545
+ for (const f of pubSpecYamlFiles) {
2553
2546
  if (DEBUG_MODE) {
2554
2547
  console.log(`Parsing ${f}`);
2555
2548
  }
2556
2549
  const pubYamlData = readFileSync(f, { encoding: "utf-8" });
2557
- const dlist = await parsePubYamlData(pubYamlData);
2550
+ const dlist = parsePubYamlData(pubYamlData);
2558
2551
  if (dlist && dlist.length) {
2559
2552
  pkgList = pkgList.concat(dlist);
2560
2553
  }
@@ -2574,7 +2567,7 @@ const createDartBom = async (path, options) => {
2574
2567
  * @param path to the project
2575
2568
  * @param options Parse options from the cli
2576
2569
  */
2577
- const createCppBom = async (path, options) => {
2570
+ export const createCppBom = async (path, options) => {
2578
2571
  const conanLockFiles = getAllFiles(
2579
2572
  path,
2580
2573
  (options.multiProject ? "**/" : "") + "conan.lock"
@@ -2585,12 +2578,12 @@ const createCppBom = async (path, options) => {
2585
2578
  );
2586
2579
  let pkgList = [];
2587
2580
  if (conanLockFiles.length) {
2588
- for (let f of conanLockFiles) {
2581
+ for (const f of conanLockFiles) {
2589
2582
  if (DEBUG_MODE) {
2590
2583
  console.log(`Parsing ${f}`);
2591
2584
  }
2592
2585
  const conanLockData = readFileSync(f, { encoding: "utf-8" });
2593
- const dlist = await parseConanLockData(conanLockData);
2586
+ const dlist = parseConanLockData(conanLockData);
2594
2587
  if (dlist && dlist.length) {
2595
2588
  pkgList = pkgList.concat(dlist);
2596
2589
  }
@@ -2600,12 +2593,12 @@ const createCppBom = async (path, options) => {
2600
2593
  filename: conanLockFiles.join(", ")
2601
2594
  });
2602
2595
  } else if (conanFiles.length) {
2603
- for (let f of conanFiles) {
2596
+ for (const f of conanFiles) {
2604
2597
  if (DEBUG_MODE) {
2605
2598
  console.log(`Parsing ${f}`);
2606
2599
  }
2607
2600
  const conanData = readFileSync(f, { encoding: "utf-8" });
2608
- const dlist = await parseConanData(conanData);
2601
+ const dlist = parseConanData(conanData);
2609
2602
  if (dlist && dlist.length) {
2610
2603
  pkgList = pkgList.concat(dlist);
2611
2604
  }
@@ -2625,7 +2618,7 @@ const createCppBom = async (path, options) => {
2625
2618
  * @param path to the project
2626
2619
  * @param options Parse options from the cli
2627
2620
  */
2628
- const createClojureBom = async (path, options) => {
2621
+ export const createClojureBom = (path, options) => {
2629
2622
  const ednFiles = getAllFiles(
2630
2623
  path,
2631
2624
  (options.multiProject ? "**/" : "") + "deps.edn"
@@ -2640,7 +2633,7 @@ const createClojureBom = async (path, options) => {
2640
2633
  if (process.env.LEIN_ARGS) {
2641
2634
  LEIN_ARGS = process.env.LEIN_ARGS.split(" ");
2642
2635
  }
2643
- for (let f of leinFiles) {
2636
+ for (const f of leinFiles) {
2644
2637
  if (DEBUG_MODE) {
2645
2638
  console.log(`Parsing ${f}`);
2646
2639
  }
@@ -2690,7 +2683,7 @@ const createClojureBom = async (path, options) => {
2690
2683
  if (process.env.CLJ_ARGS) {
2691
2684
  CLJ_ARGS = process.env.CLJ_ARGS.split(" ");
2692
2685
  }
2693
- for (let f of ednFiles) {
2686
+ for (const f of ednFiles) {
2694
2687
  const basePath = dirname(f);
2695
2688
  console.log("Executing", CLJ_CMD, CLJ_ARGS.join(" "), "in", basePath);
2696
2689
  const result = spawnSync(CLJ_CMD, CLJ_ARGS, {
@@ -2743,19 +2736,19 @@ const createClojureBom = async (path, options) => {
2743
2736
  * @param path to the project
2744
2737
  * @param options Parse options from the cli
2745
2738
  */
2746
- const createHaskellBom = async (path, options) => {
2739
+ export const createHaskellBom = async (path, options) => {
2747
2740
  const cabalFiles = getAllFiles(
2748
2741
  path,
2749
2742
  (options.multiProject ? "**/" : "") + "cabal.project.freeze"
2750
2743
  );
2751
2744
  let pkgList = [];
2752
2745
  if (cabalFiles.length) {
2753
- for (let f of cabalFiles) {
2746
+ for (const f of cabalFiles) {
2754
2747
  if (DEBUG_MODE) {
2755
2748
  console.log(`Parsing ${f}`);
2756
2749
  }
2757
2750
  const cabalData = readFileSync(f, { encoding: "utf-8" });
2758
- const dlist = await parseCabalData(cabalData);
2751
+ const dlist = parseCabalData(cabalData);
2759
2752
  if (dlist && dlist.length) {
2760
2753
  pkgList = pkgList.concat(dlist);
2761
2754
  }
@@ -2774,19 +2767,19 @@ const createHaskellBom = async (path, options) => {
2774
2767
  * @param path to the project
2775
2768
  * @param options Parse options from the cli
2776
2769
  */
2777
- const createElixirBom = async (path, options) => {
2770
+ export const createElixirBom = async (path, options) => {
2778
2771
  const mixFiles = getAllFiles(
2779
2772
  path,
2780
2773
  (options.multiProject ? "**/" : "") + "mix.lock"
2781
2774
  );
2782
2775
  let pkgList = [];
2783
2776
  if (mixFiles.length) {
2784
- for (let f of mixFiles) {
2777
+ for (const f of mixFiles) {
2785
2778
  if (DEBUG_MODE) {
2786
2779
  console.log(`Parsing ${f}`);
2787
2780
  }
2788
2781
  const mixData = readFileSync(f, { encoding: "utf-8" });
2789
- const dlist = await parseMixLockData(mixData);
2782
+ const dlist = parseMixLockData(mixData);
2790
2783
  if (dlist && dlist.length) {
2791
2784
  pkgList = pkgList.concat(dlist);
2792
2785
  }
@@ -2805,16 +2798,16 @@ const createElixirBom = async (path, options) => {
2805
2798
  * @param path to the project
2806
2799
  * @param options Parse options from the cli
2807
2800
  */
2808
- const createGitHubBom = async (path, options) => {
2801
+ export const createGitHubBom = async (path, options) => {
2809
2802
  const ghactionFiles = getAllFiles(path, ".github/workflows/" + "*.yml");
2810
2803
  let pkgList = [];
2811
2804
  if (ghactionFiles.length) {
2812
- for (let f of ghactionFiles) {
2805
+ for (const f of ghactionFiles) {
2813
2806
  if (DEBUG_MODE) {
2814
2807
  console.log(`Parsing ${f}`);
2815
2808
  }
2816
2809
  const ghwData = readFileSync(f, { encoding: "utf-8" });
2817
- const dlist = await parseGitHubWorkflowData(ghwData);
2810
+ const dlist = parseGitHubWorkflowData(ghwData);
2818
2811
  if (dlist && dlist.length) {
2819
2812
  pkgList = pkgList.concat(dlist);
2820
2813
  }
@@ -2833,16 +2826,16 @@ const createGitHubBom = async (path, options) => {
2833
2826
  * @param path to the project
2834
2827
  * @param options Parse options from the cli
2835
2828
  */
2836
- const createCloudBuildBom = async (path, options) => {
2829
+ export const createCloudBuildBom = async (path, options) => {
2837
2830
  const cbFiles = getAllFiles(path, "cloudbuild.yml");
2838
2831
  let pkgList = [];
2839
2832
  if (cbFiles.length) {
2840
- for (let f of cbFiles) {
2833
+ for (const f of cbFiles) {
2841
2834
  if (DEBUG_MODE) {
2842
2835
  console.log(`Parsing ${f}`);
2843
2836
  }
2844
2837
  const cbwData = readFileSync(f, { encoding: "utf-8" });
2845
- const dlist = await parseCloudBuildData(cbwData);
2838
+ const dlist = parseCloudBuildData(cbwData);
2846
2839
  if (dlist && dlist.length) {
2847
2840
  pkgList = pkgList.concat(dlist);
2848
2841
  }
@@ -2861,7 +2854,7 @@ const createCloudBuildBom = async (path, options) => {
2861
2854
  * @param path to the project
2862
2855
  * @param options Parse options from the cli
2863
2856
  */
2864
- const createOSBom = async (path, options) => {
2857
+ export const createOSBom = (path, options) => {
2865
2858
  console.warn(
2866
2859
  "About to generate SBoM for the current OS installation. This would take several minutes ..."
2867
2860
  );
@@ -2893,7 +2886,7 @@ const createOSBom = async (path, options) => {
2893
2886
  allLayersDir: options.allLayersExplodedDir,
2894
2887
  allLayersExplodedDir: options.allLayersExplodedDir
2895
2888
  };
2896
- let pkgPathList = [];
2889
+ const pkgPathList = [];
2897
2890
  if (options.deep) {
2898
2891
  getPkgPathList(exportData, undefined);
2899
2892
  }
@@ -2906,15 +2899,15 @@ const createOSBom = async (path, options) => {
2906
2899
  * @param path to the project
2907
2900
  * @param options Parse options from the cli
2908
2901
  */
2909
- const createJenkinsBom = async (path, options) => {
2902
+ export const createJenkinsBom = async (path, options) => {
2910
2903
  let pkgList = [];
2911
2904
  const hpiFiles = getAllFiles(
2912
2905
  path,
2913
2906
  (options.multiProject ? "**/" : "") + "*.hpi"
2914
2907
  );
2915
- let tempDir = mkdtempSync(join(tmpdir(), "hpi-deps-"));
2908
+ const tempDir = mkdtempSync(join(tmpdir(), "hpi-deps-"));
2916
2909
  if (hpiFiles.length) {
2917
- for (let f of hpiFiles) {
2910
+ for (const f of hpiFiles) {
2918
2911
  if (DEBUG_MODE) {
2919
2912
  console.log(`Parsing ${f}`);
2920
2913
  }
@@ -2926,7 +2919,7 @@ const createJenkinsBom = async (path, options) => {
2926
2919
  }
2927
2920
  const jsFiles = getAllFiles(tempDir, "**/*.js");
2928
2921
  if (jsFiles.length) {
2929
- for (let f of jsFiles) {
2922
+ for (const f of jsFiles) {
2930
2923
  if (DEBUG_MODE) {
2931
2924
  console.log(`Parsing ${f}`);
2932
2925
  }
@@ -2954,19 +2947,19 @@ const createJenkinsBom = async (path, options) => {
2954
2947
  * @param path to the project
2955
2948
  * @param options Parse options from the cli
2956
2949
  */
2957
- const createHelmBom = async (path, options) => {
2950
+ export const createHelmBom = async (path, options) => {
2958
2951
  let pkgList = [];
2959
2952
  const yamlFiles = getAllFiles(
2960
2953
  path,
2961
2954
  (options.multiProject ? "**/" : "") + "*.yaml"
2962
2955
  );
2963
2956
  if (yamlFiles.length) {
2964
- for (let f of yamlFiles) {
2957
+ for (const f of yamlFiles) {
2965
2958
  if (DEBUG_MODE) {
2966
2959
  console.log(`Parsing ${f}`);
2967
2960
  }
2968
2961
  const helmData = readFileSync(f, { encoding: "utf-8" });
2969
- const dlist = await parseHelmYamlData(helmData);
2962
+ const dlist = parseHelmYamlData(helmData);
2970
2963
  if (dlist && dlist.length) {
2971
2964
  pkgList = pkgList.concat(dlist);
2972
2965
  }
@@ -2985,7 +2978,7 @@ const createHelmBom = async (path, options) => {
2985
2978
  * @param path to the project
2986
2979
  * @param options Parse options from the cli
2987
2980
  */
2988
- const createSwiftBom = async (path, options) => {
2981
+ export const createSwiftBom = (path, options) => {
2989
2982
  const swiftFiles = getAllFiles(
2990
2983
  path,
2991
2984
  (options.multiProject ? "**/" : "") + "Package*.swift"
@@ -2997,9 +2990,9 @@ const createSwiftBom = async (path, options) => {
2997
2990
  let pkgList = [];
2998
2991
  let dependencies = [];
2999
2992
  let parentComponent = {};
3000
- let completedPath = [];
2993
+ const completedPath = [];
3001
2994
  if (pkgResolvedFiles.length) {
3002
- for (let f of pkgResolvedFiles) {
2995
+ for (const f of pkgResolvedFiles) {
3003
2996
  if (!parentComponent || !Object.keys(parentComponent).length) {
3004
2997
  parentComponent = createDefaultParentComponent(f);
3005
2998
  }
@@ -3013,7 +3006,7 @@ const createSwiftBom = async (path, options) => {
3013
3006
  }
3014
3007
  }
3015
3008
  if (swiftFiles.length) {
3016
- for (let f of swiftFiles) {
3009
+ for (const f of swiftFiles) {
3017
3010
  const basePath = dirname(f);
3018
3011
  if (completedPath.includes(basePath)) {
3019
3012
  continue;
@@ -3071,16 +3064,16 @@ const createSwiftBom = async (path, options) => {
3071
3064
  * @param path to the project
3072
3065
  * @param options Parse options from the cli
3073
3066
  */
3074
- const createContainerSpecLikeBom = async (path, options) => {
3067
+ export const createContainerSpecLikeBom = async (path, options) => {
3075
3068
  let services = [];
3076
- let ociSpecs = [];
3069
+ const ociSpecs = [];
3077
3070
  let components = [];
3078
3071
  let componentsXmls = [];
3079
- let parentComponent = {};
3072
+ const parentComponent = {};
3080
3073
  let dependencies = [];
3081
- let doneimages = [];
3082
- let doneservices = [];
3083
- let origProjectType = options.projectType;
3074
+ const doneimages = [];
3075
+ const doneservices = [];
3076
+ const origProjectType = options.projectType;
3084
3077
  let dcFiles = getAllFiles(
3085
3078
  path,
3086
3079
  (options.multiProject ? "**/" : "") + "*.yml"
@@ -3093,7 +3086,7 @@ const createContainerSpecLikeBom = async (path, options) => {
3093
3086
  path,
3094
3087
  (options.multiProject ? "**/" : "") + "open*.json"
3095
3088
  );
3096
- let oapiYamlFiles = getAllFiles(
3089
+ const oapiYamlFiles = getAllFiles(
3097
3090
  path,
3098
3091
  (options.multiProject ? "**/" : "") + "open*.yaml"
3099
3092
  );
@@ -3107,18 +3100,18 @@ const createContainerSpecLikeBom = async (path, options) => {
3107
3100
  const privadoFiles = getAllFiles(path, ".privado/" + "*.json");
3108
3101
  // parse yaml manifest files
3109
3102
  if (dcFiles.length) {
3110
- for (let f of dcFiles) {
3103
+ for (const f of dcFiles) {
3111
3104
  if (DEBUG_MODE) {
3112
3105
  console.log(`Parsing ${f}`);
3113
3106
  }
3114
3107
  const dcData = readFileSync(f, { encoding: "utf-8" });
3115
- const imglist = await parseContainerSpecData(dcData);
3108
+ const imglist = parseContainerSpecData(dcData);
3116
3109
  if (imglist && imglist.length) {
3117
3110
  if (DEBUG_MODE) {
3118
3111
  console.log("Images identified in", f, "are", imglist);
3119
3112
  }
3120
3113
  for (const img of imglist) {
3121
- let commonProperties = [
3114
+ const commonProperties = [
3122
3115
  {
3123
3116
  name: "SrcFile",
3124
3117
  value: f
@@ -3239,12 +3232,12 @@ const createContainerSpecLikeBom = async (path, options) => {
3239
3232
  } // if
3240
3233
  // Parse openapi files
3241
3234
  if (oapiFiles.length) {
3242
- for (let af of oapiFiles) {
3235
+ for (const af of oapiFiles) {
3243
3236
  if (DEBUG_MODE) {
3244
3237
  console.log(`Parsing ${af}`);
3245
3238
  }
3246
3239
  const oaData = readFileSync(af, { encoding: "utf-8" });
3247
- const servlist = await parseOpenapiSpecData(oaData);
3240
+ const servlist = parseOpenapiSpecData(oaData);
3248
3241
  if (servlist && servlist.length) {
3249
3242
  // Inject SrcFile property
3250
3243
  for (const se of servlist) {
@@ -3265,14 +3258,14 @@ const createContainerSpecLikeBom = async (path, options) => {
3265
3258
  "Enriching your SBoM with information from privado.ai scan reports"
3266
3259
  );
3267
3260
  let rows = [["Classification", "Flow"]];
3268
- let config = {
3261
+ const config = {
3269
3262
  header: {
3270
3263
  alignment: "center",
3271
3264
  content: "Data Privacy Insights from privado.ai"
3272
3265
  },
3273
3266
  columns: [{ width: 50 }, { width: 10 }]
3274
3267
  };
3275
- for (let f of privadoFiles) {
3268
+ for (const f of privadoFiles) {
3276
3269
  if (DEBUG_MODE) {
3277
3270
  console.log(`Parsing ${f}`);
3278
3271
  }
@@ -3281,14 +3274,14 @@ const createContainerSpecLikeBom = async (path, options) => {
3281
3274
  if (servlist.length) {
3282
3275
  const aservice = servlist[0];
3283
3276
  if (aservice.data) {
3284
- for (let d of aservice.data) {
3277
+ for (const d of aservice.data) {
3285
3278
  rows.push([d.classification, d.flow]);
3286
3279
  }
3287
3280
  console.log(table(rows, config));
3288
3281
  }
3289
3282
  if (aservice.endpoints) {
3290
3283
  rows = [["Leaky Endpoints"]];
3291
- for (let e of aservice.endpoints) {
3284
+ for (const e of aservice.endpoints) {
3292
3285
  rows.push([e]);
3293
3286
  }
3294
3287
  console.log(
@@ -3350,7 +3343,7 @@ const createContainerSpecLikeBom = async (path, options) => {
3350
3343
  * @param path to the project
3351
3344
  * @param options Parse options from the cli
3352
3345
  */
3353
- const createPHPBom = async (path, options) => {
3346
+ export const createPHPBom = (path, options) => {
3354
3347
  const composerJsonFiles = getAllFiles(
3355
3348
  path,
3356
3349
  (options.multiProject ? "**/" : "") + "composer.json"
@@ -3383,14 +3376,14 @@ const createPHPBom = async (path, options) => {
3383
3376
  if (DEBUG_MODE) {
3384
3377
  console.log("Parsing version", versionResult.stdout);
3385
3378
  }
3386
- let tmpV = undefined;
3379
+ const tmpV = undefined;
3387
3380
  if (versionResult && versionResult.stdout) {
3388
3381
  versionResult.stdout.split(" ");
3389
3382
  }
3390
3383
  if (tmpV && tmpV.length > 1) {
3391
3384
  composerVersion = tmpV[1];
3392
3385
  }
3393
- for (let f of composerJsonFiles) {
3386
+ for (const f of composerJsonFiles) {
3394
3387
  const basePath = dirname(f);
3395
3388
  let args = [];
3396
3389
  if (composerVersion && !composerVersion.startsWith("1")) {
@@ -3416,11 +3409,11 @@ const createPHPBom = async (path, options) => {
3416
3409
  (options.multiProject ? "**/" : "") + "composer.lock"
3417
3410
  );
3418
3411
  if (composerLockFiles.length) {
3419
- for (let f of composerLockFiles) {
3412
+ for (const f of composerLockFiles) {
3420
3413
  if (DEBUG_MODE) {
3421
3414
  console.log(`Parsing ${f}`);
3422
3415
  }
3423
- let dlist = parseComposerLock(f);
3416
+ const dlist = parseComposerLock(f);
3424
3417
  if (dlist && dlist.length) {
3425
3418
  pkgList = pkgList.concat(dlist);
3426
3419
  }
@@ -3439,7 +3432,7 @@ const createPHPBom = async (path, options) => {
3439
3432
  * @param path to the project
3440
3433
  * @param options Parse options from the cli
3441
3434
  */
3442
- const createRubyBom = async (path, options) => {
3435
+ export const createRubyBom = async (path, options) => {
3443
3436
  const gemFiles = getAllFiles(
3444
3437
  path,
3445
3438
  (options.multiProject ? "**/" : "") + "Gemfile"
@@ -3450,9 +3443,9 @@ const createRubyBom = async (path, options) => {
3450
3443
  );
3451
3444
  let pkgList = [];
3452
3445
  const gemFileMode = gemFiles.length;
3453
- let gemLockMode = gemLockFiles.length;
3446
+ const gemLockMode = gemLockFiles.length;
3454
3447
  if (gemFileMode && !gemLockMode && options.installDeps) {
3455
- for (let f of gemFiles) {
3448
+ for (const f of gemFiles) {
3456
3449
  const basePath = dirname(f);
3457
3450
  console.log("Executing 'bundle install' in", basePath);
3458
3451
  const result = spawnSync("bundle", ["install"], {
@@ -3473,11 +3466,11 @@ const createRubyBom = async (path, options) => {
3473
3466
  (options.multiProject ? "**/" : "") + "Gemfile.lock"
3474
3467
  );
3475
3468
  if (gemLockFiles.length) {
3476
- for (let f of gemLockFiles) {
3469
+ for (const f of gemLockFiles) {
3477
3470
  if (DEBUG_MODE) {
3478
3471
  console.log(`Parsing ${f}`);
3479
3472
  }
3480
- let gemLockData = readFileSync(f, { encoding: "utf-8" });
3473
+ const gemLockData = readFileSync(f, { encoding: "utf-8" });
3481
3474
  const dlist = await parseGemfileLockData(gemLockData);
3482
3475
  if (dlist && dlist.length) {
3483
3476
  pkgList = pkgList.concat(dlist);
@@ -3497,8 +3490,9 @@ const createRubyBom = async (path, options) => {
3497
3490
  * @param path to the project
3498
3491
  * @param options Parse options from the cli
3499
3492
  */
3500
- const createCsharpBom = async (path, options) => {
3493
+ export const createCsharpBom = async (path, options) => {
3501
3494
  let manifestFiles = [];
3495
+ let pkgData = undefined;
3502
3496
  const csProjFiles = getAllFiles(
3503
3497
  path,
3504
3498
  (options.multiProject ? "**/" : "") + "*.csproj"
@@ -3522,7 +3516,7 @@ const createCsharpBom = async (path, options) => {
3522
3516
  let pkgList = [];
3523
3517
  if (nupkgFiles.length) {
3524
3518
  manifestFiles = manifestFiles.concat(nupkgFiles);
3525
- for (let nf of nupkgFiles) {
3519
+ for (const nf of nupkgFiles) {
3526
3520
  if (DEBUG_MODE) {
3527
3521
  console.log(`Parsing ${nf}`);
3528
3522
  }
@@ -3535,11 +3529,11 @@ const createCsharpBom = async (path, options) => {
3535
3529
  // project.assets.json parsing
3536
3530
  if (projAssetsFiles.length) {
3537
3531
  manifestFiles = manifestFiles.concat(projAssetsFiles);
3538
- for (let af of projAssetsFiles) {
3532
+ for (const af of projAssetsFiles) {
3539
3533
  if (DEBUG_MODE) {
3540
3534
  console.log(`Parsing ${af}`);
3541
3535
  }
3542
- let pkgData = readFileSync(af, { encoding: "utf-8" });
3536
+ pkgData = readFileSync(af, { encoding: "utf-8" });
3543
3537
  const dlist = await parseCsProjAssetsData(pkgData);
3544
3538
  if (dlist && dlist.length) {
3545
3539
  pkgList = pkgList.concat(dlist);
@@ -3548,11 +3542,11 @@ const createCsharpBom = async (path, options) => {
3548
3542
  } else if (pkgLockFiles.length) {
3549
3543
  manifestFiles = manifestFiles.concat(pkgLockFiles);
3550
3544
  // packages.lock.json from nuget
3551
- for (let af of pkgLockFiles) {
3545
+ for (const af of pkgLockFiles) {
3552
3546
  if (DEBUG_MODE) {
3553
3547
  console.log(`Parsing ${af}`);
3554
3548
  }
3555
- let pkgData = readFileSync(af, { encoding: "utf-8" });
3549
+ pkgData = readFileSync(af, { encoding: "utf-8" });
3556
3550
  const dlist = await parseCsPkgLockData(pkgData);
3557
3551
  if (dlist && dlist.length) {
3558
3552
  pkgList = pkgList.concat(dlist);
@@ -3561,11 +3555,11 @@ const createCsharpBom = async (path, options) => {
3561
3555
  } else if (pkgConfigFiles.length) {
3562
3556
  manifestFiles = manifestFiles.concat(pkgConfigFiles);
3563
3557
  // packages.config parsing
3564
- for (let f of pkgConfigFiles) {
3558
+ for (const f of pkgConfigFiles) {
3565
3559
  if (DEBUG_MODE) {
3566
3560
  console.log(`Parsing ${f}`);
3567
3561
  }
3568
- let pkgData = readFileSync(f, { encoding: "utf-8" });
3562
+ pkgData = readFileSync(f, { encoding: "utf-8" });
3569
3563
  // Remove byte order mark
3570
3564
  if (pkgData.charCodeAt(0) === 0xfeff) {
3571
3565
  pkgData = pkgData.slice(1);
@@ -3578,7 +3572,7 @@ const createCsharpBom = async (path, options) => {
3578
3572
  } else if (csProjFiles.length) {
3579
3573
  manifestFiles = manifestFiles.concat(csProjFiles);
3580
3574
  // .csproj parsing
3581
- for (let f of csProjFiles) {
3575
+ for (const f of csProjFiles) {
3582
3576
  if (DEBUG_MODE) {
3583
3577
  console.log(`Parsing ${f}`);
3584
3578
  }
@@ -3602,9 +3596,9 @@ const createCsharpBom = async (path, options) => {
3602
3596
  return {};
3603
3597
  };
3604
3598
 
3605
- const mergeDependencies = (dependencies, newDependencies) => {
3599
+ export const mergeDependencies = (dependencies, newDependencies) => {
3606
3600
  const deps_map = {};
3607
- let combinedDeps = dependencies.concat(newDependencies || []);
3601
+ const combinedDeps = dependencies.concat(newDependencies || []);
3608
3602
  for (const adep of combinedDeps) {
3609
3603
  if (!deps_map[adep.ref]) {
3610
3604
  deps_map[adep.ref] = new Set();
@@ -3613,7 +3607,7 @@ const mergeDependencies = (dependencies, newDependencies) => {
3613
3607
  deps_map[adep.ref].add(eachDepends);
3614
3608
  }
3615
3609
  }
3616
- let retlist = [];
3610
+ const retlist = [];
3617
3611
  for (const akey of Object.keys(deps_map)) {
3618
3612
  retlist.push({
3619
3613
  ref: akey,
@@ -3622,13 +3616,11 @@ const mergeDependencies = (dependencies, newDependencies) => {
3622
3616
  }
3623
3617
  return retlist;
3624
3618
  };
3625
- const _mergeDependencies = mergeDependencies;
3626
- export { _mergeDependencies as mergeDependencies };
3627
3619
 
3628
- const trimComponents = (components, format) => {
3620
+ export const trimComponents = (components, format) => {
3629
3621
  const keyCache = {};
3630
3622
  const filteredComponents = [];
3631
- for (let comp of components) {
3623
+ for (const comp of components) {
3632
3624
  if (format === "xml" && comp.component) {
3633
3625
  if (!keyCache[comp.component.purl]) {
3634
3626
  keyCache[comp.component.purl] = true;
@@ -3643,10 +3635,8 @@ const trimComponents = (components, format) => {
3643
3635
  }
3644
3636
  return filteredComponents;
3645
3637
  };
3646
- const _trimComponents = trimComponents;
3647
- export { _trimComponents as trimComponents };
3648
3638
 
3649
- const dedupeBom = (
3639
+ export const dedupeBom = (
3650
3640
  options,
3651
3641
  components,
3652
3642
  componentsXmls,
@@ -3694,8 +3684,6 @@ const dedupeBom = (
3694
3684
  }
3695
3685
  };
3696
3686
  };
3697
- const _dedupeBom = dedupeBom;
3698
- export { _dedupeBom as dedupeBom };
3699
3687
 
3700
3688
  /**
3701
3689
  * Function to create bom string for all languages
@@ -3703,7 +3691,7 @@ export { _dedupeBom as dedupeBom };
3703
3691
  * @param pathList list of to the project
3704
3692
  * @param options Parse options from the cli
3705
3693
  */
3706
- const createMultiXBom = async (pathList, options) => {
3694
+ export const createMultiXBom = async (pathList, options) => {
3707
3695
  let components = [];
3708
3696
  let dependencies = [];
3709
3697
  let componentsXmls = [];
@@ -3741,7 +3729,7 @@ const createMultiXBom = async (pathList, options) => {
3741
3729
  );
3742
3730
  }
3743
3731
  }
3744
- for (let path of pathList) {
3732
+ for (const path of pathList) {
3745
3733
  if (DEBUG_MODE) {
3746
3734
  console.log("Scanning", path);
3747
3735
  }
@@ -3830,7 +3818,7 @@ const createMultiXBom = async (pathList, options) => {
3830
3818
  listComponents(options, {}, bomData.bomJson.components, "cargo", "xml")
3831
3819
  );
3832
3820
  }
3833
- bomData = await createPHPBom(path, options);
3821
+ bomData = createPHPBom(path, options);
3834
3822
  if (bomData && bomData.bomJson && bomData.bomJson.components) {
3835
3823
  if (DEBUG_MODE) {
3836
3824
  console.log(
@@ -3954,7 +3942,7 @@ const createMultiXBom = async (pathList, options) => {
3954
3942
  listComponents(options, {}, bomData.bomJson.components, "conan", "xml")
3955
3943
  );
3956
3944
  }
3957
- bomData = await createClojureBom(path, options);
3945
+ bomData = createClojureBom(path, options);
3958
3946
  if (bomData && bomData.bomJson && bomData.bomJson.components) {
3959
3947
  if (DEBUG_MODE) {
3960
3948
  console.log(
@@ -4014,7 +4002,7 @@ const createMultiXBom = async (pathList, options) => {
4014
4002
  )
4015
4003
  );
4016
4004
  }
4017
- bomData = await createSwiftBom(path, options);
4005
+ bomData = createSwiftBom(path, options);
4018
4006
  if (
4019
4007
  bomData &&
4020
4008
  bomData.bomJson &&
@@ -4035,30 +4023,23 @@ const createMultiXBom = async (pathList, options) => {
4035
4023
  listComponents(options, {}, bomData.bomJson.components, "swift", "xml")
4036
4024
  );
4037
4025
  }
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
- )
4026
+ // Jar scanning is enabled by default
4027
+ // See #330
4028
+ bomData = createJarBom(path, options);
4029
+ if (bomData && bomData.bomJson && bomData.bomJson.components) {
4030
+ if (DEBUG_MODE) {
4031
+ console.log(
4032
+ `Found ${bomData.bomJson.components.length} jar packages at ${path}`
4060
4033
  );
4061
4034
  }
4035
+ components = components.concat(bomData.bomJson.components);
4036
+ dependencies = dependencies.concat(bomData.bomJson.dependencies);
4037
+ if (!parentComponent || !Object.keys(parentComponent).length) {
4038
+ parentComponent = bomData.parentComponent;
4039
+ }
4040
+ componentsXmls = componentsXmls.concat(
4041
+ listComponents(options, {}, bomData.bomJson.components, "maven", "xml")
4042
+ );
4062
4043
  }
4063
4044
  } // for
4064
4045
  if (options.lastWorkingDir && options.lastWorkingDir !== "") {
@@ -4094,7 +4075,7 @@ const createMultiXBom = async (pathList, options) => {
4094
4075
  * @param path to the project
4095
4076
  * @param options Parse options from the cli
4096
4077
  */
4097
- const createXBom = async (path, options) => {
4078
+ export const createXBom = async (path, options) => {
4098
4079
  try {
4099
4080
  accessSync(path, constants.R_OK);
4100
4081
  } catch (err) {
@@ -4115,12 +4096,12 @@ const createXBom = async (path, options) => {
4115
4096
  (options.multiProject ? "**/" : "") + "pom.xml"
4116
4097
  );
4117
4098
  // gradle
4118
- let gradleFiles = getAllFiles(
4099
+ const gradleFiles = getAllFiles(
4119
4100
  path,
4120
4101
  (options.multiProject ? "**/" : "") + "build.gradle*"
4121
4102
  );
4122
4103
  // scala sbt
4123
- let sbtFiles = getAllFiles(
4104
+ const sbtFiles = getAllFiles(
4124
4105
  path,
4125
4106
  (options.multiProject ? "**/" : "") + "{build.sbt,Build.scala}*"
4126
4107
  );
@@ -4192,7 +4173,7 @@ const createXBom = async (path, options) => {
4192
4173
  (options.multiProject ? "**/" : "") + "composer.lock"
4193
4174
  );
4194
4175
  if (composerJsonFiles.length || composerLockFiles.length) {
4195
- return await createPHPBom(path, options);
4176
+ return createPHPBom(path, options);
4196
4177
  }
4197
4178
 
4198
4179
  // Ruby
@@ -4271,7 +4252,7 @@ const createXBom = async (path, options) => {
4271
4252
  (options.multiProject ? "**/" : "") + "project.clj"
4272
4253
  );
4273
4254
  if (ednFiles.length || leinFiles.length) {
4274
- return await createClojureBom(path, options);
4255
+ return createClojureBom(path, options);
4275
4256
  }
4276
4257
 
4277
4258
  // GitHub actions
@@ -4338,7 +4319,7 @@ const createXBom = async (path, options) => {
4338
4319
  (options.multiProject ? "**/" : "") + "Package.resolved"
4339
4320
  );
4340
4321
  if (swiftFiles.length || pkgResolvedFiles.length) {
4341
- return await createSwiftBom(path, options);
4322
+ return createSwiftBom(path, options);
4342
4323
  }
4343
4324
  };
4344
4325
 
@@ -4474,20 +4455,20 @@ export const createBom = async (path, options) => {
4474
4455
  return await createJavaBom(path, options);
4475
4456
  case "jar":
4476
4457
  options.multiProject = true;
4477
- return await createJarBom(path, options);
4458
+ return createJarBom(path, options);
4478
4459
  case "gradle-index":
4479
4460
  case "gradle-cache":
4480
4461
  options.multiProject = true;
4481
- return await createJarBom(GRADLE_CACHE_DIR, options);
4462
+ return createJarBom(GRADLE_CACHE_DIR, options);
4482
4463
  case "sbt-index":
4483
4464
  case "sbt-cache":
4484
4465
  options.multiProject = true;
4485
- return await createJarBom(SBT_CACHE_DIR, options);
4466
+ return createJarBom(SBT_CACHE_DIR, options);
4486
4467
  case "maven-index":
4487
4468
  case "maven-cache":
4488
4469
  case "maven-repo":
4489
4470
  options.multiProject = true;
4490
- return await createJarBom(join(homedir(), ".m2", "repository"), options);
4471
+ return createJarBom(join(homedir(), ".m2", "repository"), options);
4491
4472
  case "nodejs":
4492
4473
  case "js":
4493
4474
  case "javascript":
@@ -4510,7 +4491,7 @@ export const createBom = async (path, options) => {
4510
4491
  return await createRustBom(path, options);
4511
4492
  case "php":
4512
4493
  options.multiProject = true;
4513
- return await createPHPBom(path, options);
4494
+ return createPHPBom(path, options);
4514
4495
  case "ruby":
4515
4496
  options.multiProject = true;
4516
4497
  return await createRubyBom(path, options);
@@ -4545,7 +4526,7 @@ export const createBom = async (path, options) => {
4545
4526
  case "clj":
4546
4527
  case "leiningen":
4547
4528
  options.multiProject = true;
4548
- return await createClojureBom(path, options);
4529
+ return createClojureBom(path, options);
4549
4530
  case "github":
4550
4531
  case "actions":
4551
4532
  options.multiProject = true;
@@ -4587,7 +4568,7 @@ export const createBom = async (path, options) => {
4587
4568
  return await createCloudBuildBom(path, options);
4588
4569
  case "swift":
4589
4570
  options.multiProject = true;
4590
- return await createSwiftBom(path, options);
4571
+ return createSwiftBom(path, options);
4591
4572
  default:
4592
4573
  // In recurse mode return multi-language Bom
4593
4574
  // https://github.com/cyclonedx/cdxgen/issues/95
@@ -4606,7 +4587,7 @@ export const createBom = async (path, options) => {
4606
4587
  * @param bomContents BOM Json
4607
4588
  */
4608
4589
  export async function submitBom(args, bomContents) {
4609
- let serverUrl = args.serverUrl.replace(/\/$/, "") + "/api/v1/bom";
4590
+ const serverUrl = args.serverUrl.replace(/\/$/, "") + "/api/v1/bom";
4610
4591
  let encodedBomContents = Buffer.from(JSON.stringify(bomContents)).toString(
4611
4592
  "base64"
4612
4593
  );
@@ -4681,3 +4662,38 @@ export async function submitBom(args, bomContents) {
4681
4662
  }
4682
4663
  }
4683
4664
  }
4665
+
4666
+ /**
4667
+ * Validate the generated bom using jsonschema
4668
+ *
4669
+ * @param {object} bomJson content
4670
+ */
4671
+ export const validateBom = (bomJson) => {
4672
+ if (!bomJson) {
4673
+ return true;
4674
+ }
4675
+ const schema = JSON.parse(
4676
+ readFileSync(join(dirName, "data", "bom-1.5.schema.json"))
4677
+ );
4678
+ const defsSchema = JSON.parse(
4679
+ readFileSync(join(dirName, "data", "jsf-0.82.schema.json"))
4680
+ );
4681
+ const spdxSchema = JSON.parse(
4682
+ readFileSync(join(dirName, "data", "spdx.schema.json"))
4683
+ );
4684
+ const ajv = new Ajv({
4685
+ schemas: [schema, defsSchema, spdxSchema],
4686
+ strict: false,
4687
+ logger: false
4688
+ });
4689
+ addFormats(ajv);
4690
+ const validate = ajv.getSchema(
4691
+ "http://cyclonedx.org/schema/bom-1.5.schema.json"
4692
+ );
4693
+ const isValid = validate(bomJson);
4694
+ if (!isValid) {
4695
+ console.log(validate.errors);
4696
+ return false;
4697
+ }
4698
+ return true;
4699
+ };