@cyclonedx/cdxgen 11.0.3 → 11.0.5

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/lib/cli/index.js CHANGED
@@ -92,6 +92,7 @@ import {
92
92
  parseCljDep,
93
93
  parseCloudBuildData,
94
94
  parseCmakeLikeFile,
95
+ parseComposerJson,
95
96
  parseComposerLock,
96
97
  parseConanData,
97
98
  parseConanLockData,
@@ -240,11 +241,12 @@ const createDefaultParentComponent = (
240
241
  : dirname(path);
241
242
  const tmpA = dirNameStr.split(sep);
242
243
  dirNameStr = tmpA[tmpA.length - 1];
244
+ const compName = options.projectName || dirNameStr;
243
245
  const parentComponent = {
244
246
  group: options.projectGroup || "",
245
- name: options.projectName || dirNameStr,
247
+ name: compName,
246
248
  version: `${options.projectVersion}` || "latest",
247
- type: "application",
249
+ type: compName.endsWith(".tar") ? "container" : "application",
248
250
  };
249
251
  const ppurl = new PackageURL(
250
252
  type,
@@ -816,6 +818,7 @@ function addComponent(
816
818
  if (!isRootPkg) {
817
819
  const pkgIdentifier = parsePackageJsonName(pkg.name);
818
820
  const author = pkg.author || undefined;
821
+ const authors = pkg.authors || undefined;
819
822
  const publisher = pkg.publisher || undefined;
820
823
  let group = pkg.group || pkgIdentifier.scope;
821
824
  // Create empty group
@@ -868,6 +871,7 @@ function addComponent(
868
871
  }
869
872
  const component = {
870
873
  author,
874
+ authors,
871
875
  publisher,
872
876
  group,
873
877
  name,
@@ -879,7 +883,14 @@ function addComponent(
879
883
  purl: purlString,
880
884
  externalReferences: addExternalReferences(pkg),
881
885
  };
882
-
886
+ if (options.specVersion >= 1.5) {
887
+ component.pedigree = pkg.pedigree || undefined;
888
+ }
889
+ if (options.specVersion >= 1.6) {
890
+ component.releaseNotes = pkg.releaseNotes || undefined;
891
+ component.modelCard = pkg.modelCard || undefined;
892
+ component.data = pkg.data || undefined;
893
+ }
883
894
  component["type"] = determinePackageType(pkg);
884
895
  component["bom-ref"] = decodeURIComponent(purlString);
885
896
  if (
@@ -919,6 +930,15 @@ function addComponent(
919
930
  component.authors = authorsList;
920
931
  delete component.author;
921
932
  }
933
+ // Downgrade authors section for < 1.5 :(
934
+ if (options.specVersion < 1.6) {
935
+ if (component?.authors?.length) {
936
+ component.author = component.authors
937
+ .map((a) => (a.email ? `${a.name} <${a.email}>` : a.name))
938
+ .join(",");
939
+ }
940
+ delete component.authors;
941
+ }
922
942
  // Retain any tags
923
943
  if (
924
944
  options.specVersion >= 1.6 &&
@@ -957,7 +977,6 @@ function determinePackageType(pkg) {
957
977
  // Retain the exact component type in certain cases.
958
978
  if (
959
979
  [
960
- "application",
961
980
  "container",
962
981
  "platform",
963
982
  "operating-system",
@@ -972,6 +991,12 @@ function determinePackageType(pkg) {
972
991
  ) {
973
992
  return pkg.type;
974
993
  }
994
+ if (pkg.type === "application") {
995
+ if (pkg?.name?.endsWith(".tar")) {
996
+ return "container";
997
+ }
998
+ return pkg.type;
999
+ }
975
1000
  if (pkg.purl) {
976
1001
  try {
977
1002
  const purl = PackageURL.fromString(pkg.purl);
@@ -1246,7 +1271,6 @@ export async function createJavaBom(path, options) {
1246
1271
  // For java, this would correctly include the cyclonedx maven plugin.
1247
1272
  let tools = undefined;
1248
1273
  let possible_misses = false;
1249
- let mavenDepsTreeInfoShown = false;
1250
1274
  // war/ear mode
1251
1275
  if (path.endsWith(".war") || path.endsWith(".jar")) {
1252
1276
  // Check if the file exists
@@ -1276,6 +1300,11 @@ export async function createJavaBom(path, options) {
1276
1300
  parentComponent,
1277
1301
  });
1278
1302
  }
1303
+ // -t quarkus is supported
1304
+ let isQuarkus = options?.projectType?.includes("quarkus");
1305
+ let useMavenDepsTree = isQuarkus ? false : PREFER_MAVEN_DEPS_TREE;
1306
+ // Is this a multi-module project
1307
+ let rootModules;
1279
1308
  // maven - pom.xml
1280
1309
  const pomFiles = getAllFiles(
1281
1310
  path,
@@ -1287,37 +1316,72 @@ export async function createJavaBom(path, options) {
1287
1316
  pomFiles?.length &&
1288
1317
  isPackageManagerAllowed("maven", ["bazel", "sbt", "gradle"], options)
1289
1318
  ) {
1290
- let result = undefined;
1291
- const cdxMavenPlugin =
1292
- process.env.CDX_MAVEN_PLUGIN ||
1293
- "org.cyclonedx:cyclonedx-maven-plugin:2.8.0";
1294
- const cdxMavenGoal = process.env.CDX_MAVEN_GOAL || "makeAggregateBom";
1295
- let mvnArgs = [
1296
- "-fn",
1297
- `${cdxMavenPlugin}:${cdxMavenGoal}`,
1298
- "-DoutputName=bom",
1299
- ];
1300
- if (includeMavenTestScope) {
1301
- mvnArgs.push("-DincludeTestScope=true");
1302
- }
1303
- // By using quiet mode we can reduce the maxBuffer used and avoid crashes
1304
- if (!DEBUG_MODE) {
1305
- mvnArgs.push("-q");
1306
- }
1307
- // Support for passing additional settings and profile to maven
1308
- if (process.env.MVN_ARGS) {
1309
- const addArgs = process.env.MVN_ARGS.split(" ");
1310
- mvnArgs = mvnArgs.concat(addArgs);
1319
+ if (!isQuarkus) {
1320
+ // Quarkus projects require special treatment. To detect quarkus, we parse the first 3 maven file to look for a hit
1321
+ for (const pf of pomFiles.slice(0, 3)) {
1322
+ const pomMap = parsePom(pf);
1323
+ if (!rootModules && pomMap?.modules?.length) {
1324
+ rootModules = pomMap.modules;
1325
+ }
1326
+ // In quarkus mode, we cannot use the maven deps tree
1327
+ if (pomMap.isQuarkus) {
1328
+ isQuarkus = true;
1329
+ useMavenDepsTree = false;
1330
+ break;
1331
+ }
1332
+ }
1311
1333
  }
1312
- // specVersion 1.4 doesn't support externalReferences.type=disribution-intake
1313
- // so we need to run the plugin with the correct version
1314
- if (options.specVersion === 1.4) {
1315
- mvnArgs = mvnArgs.concat("-DschemaVersion=1.4");
1334
+ let result = undefined;
1335
+ let mvnArgs;
1336
+ if (isQuarkus) {
1337
+ // disable analytics. See: https://quarkus.io/usage/
1338
+ mvnArgs = [
1339
+ "-fn",
1340
+ "quarkus:dependency-sbom",
1341
+ "-Dquarkus.analytics.disabled=true",
1342
+ ];
1343
+ } else {
1344
+ const cdxMavenPlugin =
1345
+ process.env.CDX_MAVEN_PLUGIN ||
1346
+ "org.cyclonedx:cyclonedx-maven-plugin:2.9.1";
1347
+ const cdxMavenGoal = process.env.CDX_MAVEN_GOAL || "makeAggregateBom";
1348
+ mvnArgs = [
1349
+ "-fn",
1350
+ `${cdxMavenPlugin}:${cdxMavenGoal}`,
1351
+ "-DoutputName=bom",
1352
+ ];
1353
+ if (includeMavenTestScope) {
1354
+ mvnArgs.push("-DincludeTestScope=true");
1355
+ }
1356
+ // By using quiet mode we can reduce the maxBuffer used and avoid crashes
1357
+ if (!DEBUG_MODE) {
1358
+ mvnArgs.push("-q");
1359
+ }
1360
+ // Support for passing additional settings and profile to maven
1361
+ if (process.env.MVN_ARGS) {
1362
+ const addArgs = process.env.MVN_ARGS.split(" ");
1363
+ mvnArgs = mvnArgs.concat(addArgs);
1364
+ }
1365
+ // specVersion 1.4 doesn't support externalReferences.type=disribution-intake
1366
+ // so we need to run the plugin with the correct version
1367
+ if (options.specVersion === 1.4) {
1368
+ mvnArgs = mvnArgs.concat("-DschemaVersion=1.4");
1369
+ }
1316
1370
  }
1317
1371
  const firstPom = pomFiles.length ? pomFiles[0] : undefined;
1318
1372
  let mavenCmd = getMavenCommand(path, path);
1319
1373
  for (const f of pomFiles) {
1320
1374
  const basePath = dirname(f);
1375
+ if (
1376
+ isQuarkus &&
1377
+ !options.deep &&
1378
+ rootModules?.includes(basename(basePath))
1379
+ ) {
1380
+ if (DEBUG_MODE) {
1381
+ console.log("Skipped sub-module", basePath);
1382
+ }
1383
+ continue;
1384
+ }
1321
1385
  const settingsXml = join(basePath, "settings.xml");
1322
1386
  if (existsSync(settingsXml)) {
1323
1387
  console.log(
@@ -1340,16 +1404,7 @@ export async function createJavaBom(path, options) {
1340
1404
  }
1341
1405
  }
1342
1406
  // Use the cyclonedx maven plugin if there is no preference for maven deps tree
1343
- if (!PREFER_MAVEN_DEPS_TREE) {
1344
- if (!mavenDepsTreeInfoShown && DEBUG_MODE) {
1345
- console.log(
1346
- "cdxgen now supports generating SBOM with only the maven cli without the need for the cyclonedx-maven plugin. This mode works better in enterprise environments and in multi-module projects.",
1347
- );
1348
- console.log(
1349
- "Set the environment variable PREFER_MAVEN_DEPS_TREE to true to enable this.",
1350
- );
1351
- mavenDepsTreeInfoShown = true;
1352
- }
1407
+ if (!useMavenDepsTree) {
1353
1408
  console.log(
1354
1409
  `Executing '${mavenCmd} ${mvnArgs.join(" ")}' in`,
1355
1410
  basePath,
@@ -1363,19 +1418,23 @@ export async function createJavaBom(path, options) {
1363
1418
  });
1364
1419
  // Check if the cyclonedx plugin created the required bom.json file
1365
1420
  // Sometimes the plugin fails silently for complex maven projects
1366
- bomJsonFiles = getAllFiles(path, "**/target/*.json", options);
1421
+ bomJsonFiles = getAllFiles(
1422
+ path,
1423
+ "**/target/*{cdx,bom,cyclonedx}*.json",
1424
+ options,
1425
+ );
1367
1426
  // Check if the bom json files got created in a directory other than target
1368
1427
  if (!bomJsonFiles.length) {
1369
1428
  bomJsonFiles = getAllFiles(
1370
1429
  path,
1371
- "target/**/*{cdx,bom}*.json",
1430
+ "target/**/*{cdx,bom,cyclonedx}*.json",
1372
1431
  options,
1373
1432
  );
1374
1433
  }
1375
1434
  }
1376
1435
  // Also check if the user has a preference for maven deps tree command
1377
1436
  if (
1378
- PREFER_MAVEN_DEPS_TREE ||
1437
+ useMavenDepsTree ||
1379
1438
  !bomJsonFiles.length ||
1380
1439
  result?.status !== 0 ||
1381
1440
  result?.error
@@ -1521,7 +1580,7 @@ export async function createJavaBom(path, options) {
1521
1580
  }
1522
1581
  } // for
1523
1582
  // Locate and parse all bom.json files from the maven plugin
1524
- if (!PREFER_MAVEN_DEPS_TREE) {
1583
+ if (!useMavenDepsTree) {
1525
1584
  for (const abjson of bomJsonFiles) {
1526
1585
  let bomJsonObj = undefined;
1527
1586
  try {
@@ -1538,7 +1597,9 @@ export async function createJavaBom(path, options) {
1538
1597
  !tools &&
1539
1598
  bomJsonObj.metadata &&
1540
1599
  bomJsonObj.metadata.tools &&
1541
- Array.isArray(bomJsonObj.metadata.tools)
1600
+ (Array.isArray(bomJsonObj.metadata.tools) ||
1601
+ bomJsonObj.metadata.tools.components ||
1602
+ bomJsonObj.metadata.tools.services)
1542
1603
  ) {
1543
1604
  tools = bomJsonObj.metadata.tools;
1544
1605
  }
@@ -1603,10 +1664,6 @@ export async function createJavaBom(path, options) {
1603
1664
  console.warn(
1604
1665
  "Multiple errors occurred while building this project with maven. The SBOM is therefore incomplete!",
1605
1666
  );
1606
- } else if (!PREFER_MAVEN_DEPS_TREE) {
1607
- console.log(
1608
- "Try generating an SBOM with the maven dependency tree plugin. Set the environment variable PREFER_MAVEN_DEPS_TREE to true to enable this.",
1609
- );
1610
1667
  }
1611
1668
  }
1612
1669
  }
@@ -2253,7 +2310,11 @@ export async function createNodejsBom(path, options) {
2253
2310
  }
2254
2311
  }
2255
2312
  const pkgJsonLockFile = getAllFiles(path, "package-lock.json", options);
2256
- const pkgJsonFile = getAllFiles(path, "package.json", options);
2313
+ const pkgJsonFile = getAllFiles(
2314
+ path,
2315
+ `${options.multiProject ? "**/" : ""}package.json`,
2316
+ options,
2317
+ );
2257
2318
  const yarnLockFile = getAllFiles(path, "yarn.lock", options);
2258
2319
  const pnpmLockFile = getAllFiles(path, "pnpm-lock.yaml", options);
2259
2320
  if (
@@ -2265,7 +2326,7 @@ export async function createNodejsBom(path, options) {
2265
2326
  ) {
2266
2327
  let pkgMgr = "npm";
2267
2328
  const supPkgMgrs = ["npm", "yarn", "yarnpkg", "pnpm", "pnpx"];
2268
- const pkgData = JSON.parse(readFileSync(`${path}/package.json`, "utf8"));
2329
+ const pkgData = JSON.parse(readFileSync(pkgJsonFile[0], "utf8"));
2269
2330
  const mgrData = pkgData.packageManager;
2270
2331
  let mgr = "";
2271
2332
  let installArgs = ["install"];
@@ -2281,9 +2342,10 @@ export async function createNodejsBom(path, options) {
2281
2342
  process.env[`${pkgMgr.toUpperCase()}_INSTALL_ARGS`].split(" ");
2282
2343
  installArgs = installArgs.concat(addArgs);
2283
2344
  }
2284
- console.log(`Executing '${pkgMgr} ${installArgs.join(" ")}' in`, path);
2345
+ const basePath = dirname(pkgJsonFile[0]);
2346
+ console.log(`Executing '${pkgMgr} ${installArgs.join(" ")}' in`, basePath);
2285
2347
  const result = spawnSync(pkgMgr, installArgs, {
2286
- cwd: path,
2348
+ cwd: basePath,
2287
2349
  encoding: "utf-8",
2288
2350
  timeout: TIMEOUT_MS,
2289
2351
  maxBuffer: MAX_BUFFER,
@@ -4844,57 +4906,46 @@ export function createPHPBom(path, options) {
4844
4906
  options,
4845
4907
  );
4846
4908
  if (composerLockFiles.length) {
4909
+ // Look for any root composer.json to capture the parentComponent
4910
+ if (existsSync(join(path, "composer.json"))) {
4911
+ const { moduleParent } = parseComposerJson(join(path, "composer.json"));
4912
+ parentComponent = moduleParent;
4913
+ }
4847
4914
  for (const f of composerLockFiles) {
4848
4915
  const basePath = dirname(f);
4916
+ let moduleParent;
4849
4917
  if (DEBUG_MODE) {
4850
4918
  console.log(`Parsing ${f}`);
4851
4919
  }
4852
- let rootRequires = [];
4853
- // Is there a composer.json to find the parent component
4854
- if (
4855
- !Object.keys(parentComponent).length &&
4856
- existsSync(join(basePath, "composer.json"))
4857
- ) {
4858
- const composerData = JSON.parse(
4859
- readFileSync(join(basePath, "composer.json"), { encoding: "utf-8" }),
4860
- );
4861
- rootRequires = composerData.require;
4862
- const pkgName = composerData.name;
4863
- if (pkgName) {
4864
- parentComponent.group = dirname(pkgName);
4865
- if (parentComponent.group === ".") {
4866
- parentComponent.group = "";
4867
- }
4868
- parentComponent.name = basename(pkgName);
4869
- parentComponent.type = "application";
4870
- parentComponent.version = composerData.version || "latest";
4871
- parentComponent["bom-ref"] = decodeURIComponent(
4872
- new PackageURL(
4873
- "composer",
4874
- parentComponent.group,
4875
- parentComponent.name,
4876
- parentComponent.version,
4877
- null,
4878
- null,
4879
- ).toString(),
4880
- );
4920
+ const rootRequires = [];
4921
+ // Is there a composer.json to find the module parent component
4922
+ if (existsSync(join(basePath, "composer.json"))) {
4923
+ const retMap = parseComposerJson(join(basePath, "composer.json"));
4924
+ moduleParent = retMap.moduleParent;
4925
+ const rootRequires = retMap.rootRequires;
4926
+ // Track all the modules in a mono-repo
4927
+ if (!Object.keys(parentComponent).length) {
4928
+ parentComponent = moduleParent;
4929
+ } else {
4930
+ parentComponent.components = parentComponent.components || [];
4931
+ parentComponent.components.push(moduleParent);
4881
4932
  }
4882
4933
  }
4883
4934
  const retMap = parseComposerLock(f, rootRequires);
4884
4935
  if (retMap.pkgList?.length) {
4885
4936
  pkgList = pkgList.concat(retMap.pkgList);
4886
4937
  }
4938
+ if (!moduleParent) {
4939
+ moduleParent = createDefaultParentComponent(
4940
+ basePath,
4941
+ "composer",
4942
+ options,
4943
+ );
4944
+ }
4887
4945
  if (retMap.dependenciesList) {
4888
- if (!Object.keys(parentComponent).length) {
4889
- parentComponent = createDefaultParentComponent(
4890
- path,
4891
- "composer",
4892
- options,
4893
- );
4894
- }
4895
4946
  // Complete the dependency tree by making parent component depend on the first level
4896
4947
  const pdependencies = {
4897
- ref: parentComponent["bom-ref"],
4948
+ ref: moduleParent["bom-ref"],
4898
4949
  dependsOn: [
4899
4950
  ...new Set(retMap.rootList.map((p) => p["bom-ref"])),
4900
4951
  ].sort(),
@@ -4906,6 +4957,17 @@ export function createPHPBom(path, options) {
4906
4957
  );
4907
4958
  }
4908
4959
  }
4960
+ // Complete the root dependency tree
4961
+ if (parentComponent?.components?.length) {
4962
+ const parentDependsOn = parentComponent.components.map(
4963
+ (d) => d["bom-ref"],
4964
+ );
4965
+ dependencies = mergeDependencies(
4966
+ [{ ref: parentComponent["bom-ref"], dependsOn: parentDependsOn }],
4967
+ dependencies,
4968
+ parentComponent,
4969
+ );
4970
+ }
4909
4971
  return buildBomNSData(options, pkgList, "composer", {
4910
4972
  src: path,
4911
4973
  filename: composerLockFiles.join(", "),
@@ -6052,7 +6114,6 @@ export async function createXBom(path, options) {
6052
6114
  } catch (err) {
6053
6115
  return undefined;
6054
6116
  }
6055
- // node.js - package.json
6056
6117
  if (
6057
6118
  existsSync(join(path, "package.json")) ||
6058
6119
  existsSync(join(path, "rush.json")) ||