@cyclonedx/cdxgen 9.6.1 → 9.7.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/evinser.js CHANGED
@@ -196,7 +196,13 @@ export const createSlice = (purlOrLanguage, filePath, sliceType = "usages") => {
196
196
  args.push(process.env.ATOM_SLICE_DEPTH);
197
197
  }
198
198
  args.push(path.resolve(filePath));
199
- executeAtom(filePath, args);
199
+ const result = executeAtom(filePath, args);
200
+ if (!result || !fs.existsSync(slicesFile)) {
201
+ console.warn(`Unable to generate ${sliceType} slice using atom.`);
202
+ console.log(
203
+ "Set the environment variable CDXGEN_DEBUG_MODE=debug to troubleshoot."
204
+ );
205
+ }
200
206
  return {
201
207
  tempDir,
202
208
  slicesFile,
@@ -363,15 +369,11 @@ export const parseObjectSlices = async (
363
369
  ) {
364
370
  continue;
365
371
  }
366
- const locationKey = `${slice.fileName}${
367
- slice.lineNumber ? "#" + slice.lineNumber : ""
368
- }`;
369
372
  await parseSliceUsages(
370
373
  language,
371
374
  userDefinedTypesMap,
372
375
  slice,
373
376
  dbObjMap,
374
- locationKey,
375
377
  purlLocationMap,
376
378
  purlImportsMap
377
379
  );
@@ -392,7 +394,6 @@ export const parseObjectSlices = async (
392
394
  * @param {object} userDefinedTypesMap User Defined types in the application
393
395
  * @param {array} usages Usages array for each objectSlice
394
396
  * @param {object} dbObjMap DB Models
395
- * @param {string} locationKey Filename with line number to be used in occurrences evidence
396
397
  * @param {object} purlLocationMap Object to track locations where purls are used
397
398
  * @param {object} purlImportsMap Object to track package urls and their import aliases
398
399
  * @returns
@@ -402,7 +403,6 @@ export const parseSliceUsages = async (
402
403
  userDefinedTypesMap,
403
404
  slice,
404
405
  dbObjMap,
405
- locationKey,
406
406
  purlLocationMap,
407
407
  purlImportsMap
408
408
  ) => {
@@ -411,7 +411,6 @@ export const parseSliceUsages = async (
411
411
  return undefined;
412
412
  }
413
413
  const fileName = slice.fileName;
414
- const purlsSet = new Set();
415
414
  const typesToLookup = new Set();
416
415
  const lKeyOverrides = {};
417
416
  for (const ausage of usages) {
@@ -441,7 +440,7 @@ export const parseSliceUsages = async (
441
440
  }
442
441
  const maybeClassType = getClassTypeFromSignature(language, atype[1]);
443
442
  typesToLookup.add(maybeClassType);
444
- if (language == "javascript" && ausageLine) {
443
+ if (ausageLine) {
445
444
  addToOverrides(lKeyOverrides, maybeClassType, fileName, ausageLine);
446
445
  }
447
446
  }
@@ -460,7 +459,7 @@ export const parseSliceUsages = async (
460
459
  if (!acall?.resolvedMethod.includes("(")) {
461
460
  typesToLookup.add(acall?.resolvedMethod);
462
461
  // Javascript calls can be resolved to a precise line number only from the call nodes
463
- if (language == "javascript" && acall.lineNumber) {
462
+ if (acall.lineNumber) {
464
463
  addToOverrides(
465
464
  lKeyOverrides,
466
465
  acall?.resolvedMethod,
@@ -474,7 +473,7 @@ export const parseSliceUsages = async (
474
473
  acall?.resolvedMethod
475
474
  );
476
475
  typesToLookup.add(maybeClassType);
477
- if (language == "javascript" && acall.lineNumber) {
476
+ if (acall.lineNumber) {
478
477
  addToOverrides(
479
478
  lKeyOverrides,
480
479
  maybeClassType,
@@ -487,7 +486,7 @@ export const parseSliceUsages = async (
487
486
  if (!isFilterableType(language, userDefinedTypesMap, aparamType)) {
488
487
  if (!aparamType.includes("(")) {
489
488
  typesToLookup.add(aparamType);
490
- if (language == "javascript" && acall.lineNumber) {
489
+ if (acall.lineNumber) {
491
490
  if (aparamType.includes(":")) {
492
491
  typesToLookup.add(aparamType.split("::")[0].replace(/:/g, "/"));
493
492
  }
@@ -504,7 +503,7 @@ export const parseSliceUsages = async (
504
503
  aparamType
505
504
  );
506
505
  typesToLookup.add(maybeClassType);
507
- if (language == "javascript" && acall.lineNumber) {
506
+ if (acall.lineNumber) {
508
507
  addToOverrides(
509
508
  lKeyOverrides,
510
509
  maybeClassType,
@@ -524,17 +523,11 @@ export const parseSliceUsages = async (
524
523
  for (const apurl of Object.keys(purlImportsMap)) {
525
524
  const apurlImports = purlImportsMap[apurl];
526
525
  if (apurlImports && apurlImports.includes(atype)) {
527
- // For javasript, we set all the additional places where a call gets made
528
- if (language == "javascript") {
529
- if (!purlLocationMap[apurl]) {
530
- purlLocationMap[apurl] = new Set();
531
- }
532
- if (lKeyOverrides[atype]) {
533
- purlLocationMap[apurl].add(...lKeyOverrides[atype]);
534
- }
535
- } else {
536
- // This would work well for java since each call node could be mapped to a method
537
- purlsSet.add(apurl);
526
+ if (!purlLocationMap[apurl]) {
527
+ purlLocationMap[apurl] = new Set();
528
+ }
529
+ if (lKeyOverrides[atype]) {
530
+ purlLocationMap[apurl].add(...lKeyOverrides[atype]);
538
531
  }
539
532
  }
540
533
  }
@@ -552,19 +545,17 @@ export const parseSliceUsages = async (
552
545
  }));
553
546
  if (nsHits && nsHits.length) {
554
547
  for (const ns of nsHits) {
555
- purlsSet.add(ns.purl);
548
+ if (!purlLocationMap[ns.purl]) {
549
+ purlLocationMap[ns.purl] = new Set();
550
+ }
551
+ if (lKeyOverrides[atype]) {
552
+ purlLocationMap[ns.purl].add(...lKeyOverrides[atype]);
553
+ }
556
554
  }
557
555
  typePurlsCache[atype] = nsHits;
558
556
  }
559
557
  }
560
558
  }
561
- // Update the purlLocationMap
562
- for (const apurl of purlsSet) {
563
- if (!purlLocationMap[apurl]) {
564
- purlLocationMap[apurl] = new Set();
565
- }
566
- purlLocationMap[apurl].add(locationKey);
567
- }
568
559
  };
569
560
 
570
561
  export const isFilterableType = (
package/index.js CHANGED
@@ -120,12 +120,13 @@ import {
120
120
  executeOsQuery,
121
121
  getOSPackages
122
122
  } from "./binary.js";
123
- const osQueries = JSON.parse(
124
- readFileSync(join(dirName, "data", "queries.json"))
125
- );
126
123
 
127
124
  const isWin = _platform() === "win32";
128
125
 
126
+ const osQueries = !isWin
127
+ ? JSON.parse(readFileSync(join(dirName, "data", "queries.json")))
128
+ : JSON.parse(readFileSync(join(dirName, "data", "queries-win.json")));
129
+
129
130
  import { table } from "table";
130
131
 
131
132
  // Construct gradle cache directory
@@ -660,7 +661,6 @@ function addComponent(
660
661
  return;
661
662
  }
662
663
  const licenses = pkg.licenses || getLicenses(pkg, format);
663
-
664
664
  const purl =
665
665
  pkg.purl ||
666
666
  new PackageURL(
@@ -755,8 +755,22 @@ function addComponent(
755
755
  * word for it, otherwise, identify the module as a 'library'.
756
756
  */
757
757
  function determinePackageType(pkg) {
758
- if (pkg.type === "application") {
759
- return "application";
758
+ // Retain the exact component type in certain cases.
759
+ if (
760
+ [
761
+ "application",
762
+ "container",
763
+ "platform",
764
+ "operating-system",
765
+ "device",
766
+ "device-driver",
767
+ "firmware",
768
+ "file",
769
+ "machine-learning-model",
770
+ "data"
771
+ ].includes(pkg.type)
772
+ ) {
773
+ return pkg.type;
760
774
  }
761
775
  if (pkg.purl) {
762
776
  try {
@@ -1184,7 +1198,8 @@ export const createJavaBom = async (path, options) => {
1184
1198
  cwd: basePath,
1185
1199
  shell: true,
1186
1200
  encoding: "utf-8",
1187
- timeout: TIMEOUT_MS
1201
+ timeout: TIMEOUT_MS,
1202
+ maxBuffer: 50 * 1024 * 1024
1188
1203
  });
1189
1204
  // Check if the cyclonedx plugin created the required bom.xml file
1190
1205
  // Sometimes the plugin fails silently for complex maven projects
@@ -1232,6 +1247,15 @@ export const createJavaBom = async (path, options) => {
1232
1247
  console.log(
1233
1248
  "1. Try building the project with 'mvn package -Dmaven.test.skip=true' using the correct version of Java and maven before invoking cdxgen."
1234
1249
  );
1250
+ } else if (
1251
+ result.stdout &&
1252
+ result.stdout.includes(
1253
+ "Could not resolve target platform specification"
1254
+ )
1255
+ ) {
1256
+ console.log(
1257
+ "1. Some projects can be built only from the root directory. Invoke cdxgen with --no-recurse option"
1258
+ );
1235
1259
  } else {
1236
1260
  console.log(
1237
1261
  "1. Java version requirement: cdxgen container image bundles Java 20 with maven 3.9 which might be incompatible."
@@ -1291,6 +1315,7 @@ export const createJavaBom = async (path, options) => {
1291
1315
  !Object.keys(parentComponent).length
1292
1316
  ) {
1293
1317
  parentComponent = bomJsonObj.metadata.component;
1318
+ options.parentComponent = parentComponent;
1294
1319
  pkgList = [];
1295
1320
  }
1296
1321
  if (bomJsonObj.components) {
@@ -1579,7 +1604,12 @@ export const createJavaBom = async (path, options) => {
1579
1604
  result = spawnSync(
1580
1605
  BAZEL_CMD,
1581
1606
  ["aquery", "--output=textproto", "--skyframe_state"],
1582
- { cwd: basePath, encoding: "utf-8", timeout: TIMEOUT_MS }
1607
+ {
1608
+ cwd: basePath,
1609
+ encoding: "utf-8",
1610
+ timeout: TIMEOUT_MS,
1611
+ maxBuffer: 1024 * 1024 * 100
1612
+ }
1583
1613
  );
1584
1614
  if (result.status !== 0 || result.error) {
1585
1615
  console.error(result.stdout, result.stderr);
@@ -3144,34 +3174,44 @@ export const createCloudBuildBom = (path, options) => {
3144
3174
  };
3145
3175
 
3146
3176
  /**
3147
- * Function to create bom string for the current OS using osquery
3177
+ * Function to create obom string for the current OS using osquery
3148
3178
  *
3149
3179
  * @param path to the project
3150
3180
  * @param options Parse options from the cli
3151
3181
  */
3152
3182
  export const createOSBom = (path, options) => {
3153
3183
  console.warn(
3154
- "About to generate SBoM for the current OS installation. This would take several minutes ..."
3184
+ "About to generate OBoM for the current OS installation. This will take several minutes ..."
3155
3185
  );
3156
3186
  let pkgList = [];
3157
3187
  let bomData = {};
3188
+ let parentComponent = {};
3158
3189
  for (const queryCategory of Object.keys(osQueries)) {
3159
3190
  const queryObj = osQueries[queryCategory];
3160
3191
  const results = executeOsQuery(queryObj.query);
3161
3192
  const dlist = convertOSQueryResults(queryCategory, queryObj, results);
3162
3193
  if (dlist && dlist.length) {
3163
- pkgList = pkgList.concat(dlist);
3194
+ if (!Object.keys(parentComponent).length) {
3195
+ parentComponent = dlist.splice(0, 1)[0];
3196
+ }
3197
+ pkgList = pkgList.concat(
3198
+ dlist.sort(function (a, b) {
3199
+ return a.name.localeCompare(b.name);
3200
+ })
3201
+ );
3164
3202
  }
3165
3203
  } // for
3166
3204
  if (pkgList.length) {
3167
3205
  bomData = buildBomNSData(options, pkgList, "", {
3168
3206
  src: "",
3169
- filename: ""
3207
+ filename: "",
3208
+ parentComponent
3170
3209
  });
3171
3210
  }
3172
3211
  options.bomData = bomData;
3173
3212
  options.multiProject = true;
3174
3213
  options.installDeps = false;
3214
+ options.parentComponent = parentComponent;
3175
3215
  // Force the project type to os
3176
3216
  options.projectType = "os";
3177
3217
  options.lastWorkingDir = undefined;
@@ -3365,7 +3405,7 @@ export const createContainerSpecLikeBom = async (path, options) => {
3365
3405
  const ociSpecs = [];
3366
3406
  let components = [];
3367
3407
  let componentsXmls = [];
3368
- const parentComponent = {};
3408
+ let parentComponent = {};
3369
3409
  let dependencies = [];
3370
3410
  const doneimages = [];
3371
3411
  const doneservices = [];
@@ -3604,6 +3644,10 @@ export const createContainerSpecLikeBom = async (path, options) => {
3604
3644
  if (mbomData.componentsXmls && mbomData.componentsXmls.length) {
3605
3645
  componentsXmls = componentsXmls.concat(mbomData.componentsXmls);
3606
3646
  }
3647
+ // We need to retain the parentComponent. See #527
3648
+ // Parent component returned by multi X search is usually good
3649
+ parentComponent = mbomData.parentComponent;
3650
+ options.parentComponent = parentComponent;
3607
3651
  if (mbomData.bomJson) {
3608
3652
  if (mbomData.bomJson.dependencies) {
3609
3653
  dependencies = mergeDependencies(
@@ -4496,7 +4540,8 @@ export const createMultiXBom = async (pathList, options) => {
4496
4540
  parentComponent.components = trimComponents(parentSubComponents, "json");
4497
4541
  if (
4498
4542
  parentComponent.components.length == 1 &&
4499
- parentComponent.components[0].name == parentComponent.name
4543
+ parentComponent.components[0].name == parentComponent.name &&
4544
+ !parentComponent.purl.startsWith("pkg:container")
4500
4545
  ) {
4501
4546
  parentComponent = parentComponent.components[0];
4502
4547
  delete parentComponent.components;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyclonedx/cdxgen",
3
- "version": "9.6.1",
3
+ "version": "9.7.1",
4
4
  "description": "Creates CycloneDX Software Bill-of-Materials (SBOM) from source or container image",
5
5
  "homepage": "http://github.com/cyclonedx/cdxgen",
6
6
  "author": "Prabhu Subramanian <prabhu@appthreat.com>",
@@ -32,6 +32,7 @@
32
32
  "exports": "./index.js",
33
33
  "bin": {
34
34
  "cdxgen": "./bin/cdxgen.js",
35
+ "obom": "./bin/cdxgen.js",
35
36
  "cdxi": "./bin/repl.js",
36
37
  "evinse": "./bin/evinse.js",
37
38
  "cdx-verify": "./bin/verify.js"
@@ -53,13 +54,13 @@
53
54
  "url": "https://github.com/cyclonedx/cdxgen/issues"
54
55
  },
55
56
  "dependencies": {
56
- "@babel/parser": "^7.22.11",
57
+ "@babel/parser": "^7.22.14",
57
58
  "@babel/traverse": "^7.22.11",
58
59
  "ajv": "^8.12.0",
59
60
  "ajv-formats": "^2.1.1",
60
61
  "cheerio": "^1.0.0-rc.12",
61
62
  "edn-data": "^1.0.0",
62
- "glob": "^10.3.0",
63
+ "glob": "^10.3.4",
63
64
  "global-agent": "^3.0.0",
64
65
  "got": "^13.0.0",
65
66
  "iconv-lite": "^0.6.3",
@@ -79,8 +80,10 @@
79
80
  "yargs": "^17.7.2"
80
81
  },
81
82
  "optionalDependencies": {
82
- "@appthreat/atom": "^1.1.4",
83
- "@cyclonedx/cdxgen-plugins-bin": "^1.3.0",
83
+ "@appthreat/atom": "^1.1.6",
84
+ "@cyclonedx/cdxgen-plugins-bin": "^1.4.0",
85
+ "@cyclonedx/cdxgen-plugins-bin-arm64": "^1.4.0",
86
+ "@cyclonedx/cdxgen-plugins-bin-ppc64": "^1.4.0",
84
87
  "body-parser": "^1.20.2",
85
88
  "compression": "^1.7.4",
86
89
  "connect": "^3.7.0",
@@ -97,6 +100,6 @@
97
100
  "caxa": "^3.0.1",
98
101
  "eslint": "^8.48.0",
99
102
  "jest": "^29.6.4",
100
- "prettier": "3.0.2"
103
+ "prettier": "3.0.3"
101
104
  }
102
105
  }