@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/README.md +18 -10
- package/bin/cdxgen.js +9 -4
- package/bin/repl.js +154 -26
- package/bin/verify.js +19 -0
- package/binary.js +41 -10
- package/data/queries-win.json +206 -0
- package/data/queries.json +160 -15
- package/display.js +58 -14
- package/docker.js +18 -1
- package/evinser.js +23 -32
- package/index.js +59 -14
- package/package.json +9 -6
- package/utils.js +222 -91
- package/utils.test.js +33 -4
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
|
|
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
|
-
|
|
759
|
-
|
|
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
|
-
{
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
83
|
-
"@cyclonedx/cdxgen-plugins-bin": "^1.
|
|
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.
|
|
103
|
+
"prettier": "3.0.3"
|
|
101
104
|
}
|
|
102
105
|
}
|