@cyclonedx/cdxgen 8.2.4 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +47 -11
- package/package.json +1 -1
- package/utils.js +45 -20
- package/utils.test.js +17 -3
package/index.js
CHANGED
|
@@ -988,9 +988,13 @@ const createJavaBom = async (path, options) => {
|
|
|
988
988
|
if (pomFiles && pomFiles.length) {
|
|
989
989
|
const cdxMavenPlugin =
|
|
990
990
|
process.env.CDX_MAVEN_PLUGIN ||
|
|
991
|
-
"org.cyclonedx:cyclonedx-maven-plugin:2.7.
|
|
991
|
+
"org.cyclonedx:cyclonedx-maven-plugin:2.7.7";
|
|
992
992
|
const cdxMavenGoal = process.env.CDX_MAVEN_GOAL || "makeAggregateBom";
|
|
993
|
-
let mvnArgs = [
|
|
993
|
+
let mvnArgs = [
|
|
994
|
+
`${cdxMavenPlugin}:${cdxMavenGoal}`,
|
|
995
|
+
"-DoutputName=bom",
|
|
996
|
+
"-DincludeTestScope=true"
|
|
997
|
+
];
|
|
994
998
|
// By using quiet mode we can reduce the maxBuffer used and avoid crashes
|
|
995
999
|
if (!DEBUG_MODE) {
|
|
996
1000
|
mvnArgs.push("-q");
|
|
@@ -1152,8 +1156,14 @@ const createJavaBom = async (path, options) => {
|
|
|
1152
1156
|
let gradleCmd = utils.getGradleCommand(path, null);
|
|
1153
1157
|
const multiProjectMode = process.env.GRADLE_MULTI_PROJECT_MODE || "";
|
|
1154
1158
|
// Support for multi-project applications
|
|
1155
|
-
|
|
1156
|
-
|
|
1159
|
+
// Let's experiment with defaulting to multi-project mode when multiple gradle files gets detected
|
|
1160
|
+
if (
|
|
1161
|
+
["true", "1"].includes(multiProjectMode) ||
|
|
1162
|
+
(gradleFiles.length > 1 && !["false", "0"].includes(multiProjectMode))
|
|
1163
|
+
) {
|
|
1164
|
+
if (DEBUG_MODE) {
|
|
1165
|
+
console.log("Executing", gradleCmd, "projects in", path);
|
|
1166
|
+
}
|
|
1157
1167
|
const result = spawnSync(
|
|
1158
1168
|
gradleCmd,
|
|
1159
1169
|
["projects", "-q", "--console", "plain"],
|
|
@@ -1171,14 +1181,20 @@ const createJavaBom = async (path, options) => {
|
|
|
1171
1181
|
const stdout = result.stdout;
|
|
1172
1182
|
if (stdout) {
|
|
1173
1183
|
const cmdOutput = Buffer.from(stdout).toString();
|
|
1174
|
-
const
|
|
1184
|
+
const retMap = utils.parseGradleProjects(cmdOutput);
|
|
1185
|
+
const allProjects = retMap.projects || [];
|
|
1186
|
+
let rootProject = retMap.rootProject;
|
|
1175
1187
|
if (!allProjects) {
|
|
1176
1188
|
console.log(
|
|
1177
1189
|
"No projects found. Is this a gradle multi-project application?"
|
|
1178
1190
|
);
|
|
1179
1191
|
options.failOnError && process.exit(1);
|
|
1180
1192
|
} else {
|
|
1181
|
-
console.log(
|
|
1193
|
+
console.log(
|
|
1194
|
+
"Found",
|
|
1195
|
+
allProjects.length,
|
|
1196
|
+
"gradle sub-projects. This might take a while ..."
|
|
1197
|
+
);
|
|
1182
1198
|
for (let sp of allProjects) {
|
|
1183
1199
|
let gradleDepArgs = [
|
|
1184
1200
|
sp + ":dependencies",
|
|
@@ -1212,7 +1228,7 @@ const createJavaBom = async (path, options) => {
|
|
|
1212
1228
|
const sstdout = sresult.stdout;
|
|
1213
1229
|
if (sstdout) {
|
|
1214
1230
|
const cmdOutput = Buffer.from(sstdout).toString();
|
|
1215
|
-
const parsedList = utils.parseGradleDep(cmdOutput);
|
|
1231
|
+
const parsedList = utils.parseGradleDep(cmdOutput, rootProject);
|
|
1216
1232
|
const dlist = parsedList.pkgList;
|
|
1217
1233
|
parentComponent = dlist.splice(0, 1)[0];
|
|
1218
1234
|
if (
|
|
@@ -1245,7 +1261,7 @@ const createJavaBom = async (path, options) => {
|
|
|
1245
1261
|
console.log(
|
|
1246
1262
|
"Obtained",
|
|
1247
1263
|
pkgList.length,
|
|
1248
|
-
"from this gradle multi-project"
|
|
1264
|
+
"from this gradle multi-project. De-duping this list ..."
|
|
1249
1265
|
);
|
|
1250
1266
|
} else {
|
|
1251
1267
|
console.log(
|
|
@@ -1286,8 +1302,29 @@ const createJavaBom = async (path, options) => {
|
|
|
1286
1302
|
});
|
|
1287
1303
|
if (result.status !== 0 || result.error) {
|
|
1288
1304
|
if (result.stderr) {
|
|
1305
|
+
const cmdError = Buffer.from(result.stderr).toString();
|
|
1306
|
+
if (
|
|
1307
|
+
cmdError.includes(
|
|
1308
|
+
"is not part of the build defined by settings file"
|
|
1309
|
+
) ||
|
|
1310
|
+
cmdError.includes(
|
|
1311
|
+
"was not found in any of the following sources"
|
|
1312
|
+
)
|
|
1313
|
+
) {
|
|
1314
|
+
console.log(
|
|
1315
|
+
"This is a multi-project gradle application. Set the environment variable GRADLE_MULTI_PROJECT_MODE to true to improve SBoM accuracy."
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
if (DEBUG_MODE) {
|
|
1319
|
+
console.log("-----------------------");
|
|
1320
|
+
}
|
|
1289
1321
|
console.error(result.stdout, result.stderr);
|
|
1322
|
+
if (DEBUG_MODE) {
|
|
1323
|
+
console.log("-----------------------");
|
|
1324
|
+
}
|
|
1325
|
+
options.failOnError && process.exit(1);
|
|
1290
1326
|
}
|
|
1327
|
+
|
|
1291
1328
|
if (DEBUG_MODE || !result.stderr || options.failOnError) {
|
|
1292
1329
|
console.log(
|
|
1293
1330
|
"1. Check if the correct version of java and gradle are installed and available in PATH. For example, some project might require Java 11 with gradle 7.\n cdxgen container image bundles Java 17 with gradle 8 which might be incompatible."
|
|
@@ -1315,9 +1352,6 @@ const createJavaBom = async (path, options) => {
|
|
|
1315
1352
|
);
|
|
1316
1353
|
options.failOnError && process.exit(1);
|
|
1317
1354
|
}
|
|
1318
|
-
} else {
|
|
1319
|
-
console.log("Gradle unexpectedly didn't produce any output");
|
|
1320
|
-
options.failOnError && process.exit(1);
|
|
1321
1355
|
}
|
|
1322
1356
|
}
|
|
1323
1357
|
}
|
|
@@ -4308,6 +4342,7 @@ const createBom = async (path, options) => {
|
|
|
4308
4342
|
case "mvn":
|
|
4309
4343
|
case "maven":
|
|
4310
4344
|
case "sbt":
|
|
4345
|
+
options.multiProject = true;
|
|
4311
4346
|
return await createJavaBom(path, options);
|
|
4312
4347
|
case "jar":
|
|
4313
4348
|
options.multiProject = true;
|
|
@@ -4334,6 +4369,7 @@ const createBom = async (path, options) => {
|
|
|
4334
4369
|
case "typescript":
|
|
4335
4370
|
case "ts":
|
|
4336
4371
|
case "tsx":
|
|
4372
|
+
options.multiProject = true;
|
|
4337
4373
|
return await createNodejsBom(path, options);
|
|
4338
4374
|
case "python":
|
|
4339
4375
|
case "py":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.0",
|
|
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>",
|
package/utils.js
CHANGED
|
@@ -239,6 +239,7 @@ const getNpmMetadata = async function (pkgList) {
|
|
|
239
239
|
exports.getNpmMetadata = getNpmMetadata;
|
|
240
240
|
|
|
241
241
|
const _getDepPkgList = async function (
|
|
242
|
+
pkgLockFile,
|
|
242
243
|
pkgList,
|
|
243
244
|
dependenciesList,
|
|
244
245
|
depKeys,
|
|
@@ -256,7 +257,13 @@ const _getDepPkgList = async function (
|
|
|
256
257
|
name,
|
|
257
258
|
version,
|
|
258
259
|
_integrity: pkg.dependencies[name].integrity,
|
|
259
|
-
scope
|
|
260
|
+
scope,
|
|
261
|
+
properties: [
|
|
262
|
+
{
|
|
263
|
+
name: "SrcFile",
|
|
264
|
+
value: pkgLockFile
|
|
265
|
+
}
|
|
266
|
+
]
|
|
260
267
|
};
|
|
261
268
|
pkgList.push(apkg);
|
|
262
269
|
if (pkg.dependencies[name].dependencies) {
|
|
@@ -286,6 +293,7 @@ const _getDepPkgList = async function (
|
|
|
286
293
|
depKeys[purlString] = true;
|
|
287
294
|
}
|
|
288
295
|
await _getDepPkgList(
|
|
296
|
+
pkgLockFile,
|
|
289
297
|
pkgList,
|
|
290
298
|
dependenciesList,
|
|
291
299
|
depKeys,
|
|
@@ -431,6 +439,7 @@ const parsePkgLock = async (pkgLockFile) => {
|
|
|
431
439
|
});
|
|
432
440
|
}
|
|
433
441
|
pkgList = await _getDepPkgList(
|
|
442
|
+
pkgLockFile,
|
|
434
443
|
pkgList,
|
|
435
444
|
dependenciesList,
|
|
436
445
|
depKeys,
|
|
@@ -1140,15 +1149,21 @@ exports.parseMavenTree = parseMavenTree;
|
|
|
1140
1149
|
/**
|
|
1141
1150
|
* Parse gradle dependencies output
|
|
1142
1151
|
* @param {string} rawOutput Raw string output
|
|
1152
|
+
* @param {string} rootProjectName Root project name
|
|
1153
|
+
* @param {string} rootProjectVersion Root project version
|
|
1143
1154
|
*/
|
|
1144
|
-
const parseGradleDep = function (
|
|
1155
|
+
const parseGradleDep = function (
|
|
1156
|
+
rawOutput,
|
|
1157
|
+
rootProjectName = "root",
|
|
1158
|
+
rootProjectVersion = "latest"
|
|
1159
|
+
) {
|
|
1145
1160
|
if (typeof rawOutput === "string") {
|
|
1146
1161
|
let match = "";
|
|
1147
1162
|
// To render dependency tree we need a root project
|
|
1148
1163
|
const rootProject = {
|
|
1149
1164
|
group: "",
|
|
1150
|
-
name:
|
|
1151
|
-
version:
|
|
1165
|
+
name: rootProjectName,
|
|
1166
|
+
version: rootProjectVersion,
|
|
1152
1167
|
type: "maven",
|
|
1153
1168
|
qualifiers: { type: "jar" }
|
|
1154
1169
|
};
|
|
@@ -1156,7 +1171,7 @@ const parseGradleDep = function (rawOutput) {
|
|
|
1156
1171
|
const dependenciesList = [];
|
|
1157
1172
|
const keys_cache = {};
|
|
1158
1173
|
let last_level = 0;
|
|
1159
|
-
let last_purl =
|
|
1174
|
+
let last_purl = `pkg:maven/${rootProjectName}@${rootProjectVersion}?type=jar`;
|
|
1160
1175
|
const level_trees = {};
|
|
1161
1176
|
level_trees[last_purl] = [];
|
|
1162
1177
|
let stack = [last_purl];
|
|
@@ -1317,31 +1332,41 @@ exports.parseLeinMap = parseLeinMap;
|
|
|
1317
1332
|
|
|
1318
1333
|
/**
|
|
1319
1334
|
* Parse gradle projects output
|
|
1335
|
+
* FIXME: The method needs to be enhanced to capture project dependency tree. See issue #249
|
|
1336
|
+
*
|
|
1320
1337
|
* @param {string} rawOutput Raw string output
|
|
1321
1338
|
*/
|
|
1322
1339
|
const parseGradleProjects = function (rawOutput) {
|
|
1340
|
+
let rootProject = "root";
|
|
1341
|
+
const projects = new Set();
|
|
1323
1342
|
if (typeof rawOutput === "string") {
|
|
1324
|
-
const projects = [];
|
|
1325
1343
|
const tmpA = rawOutput.split("\n");
|
|
1326
1344
|
tmpA.forEach((l) => {
|
|
1327
|
-
if (l.startsWith("
|
|
1328
|
-
|
|
1329
|
-
.
|
|
1330
|
-
.
|
|
1331
|
-
.
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1345
|
+
if (l.startsWith("Root project ")) {
|
|
1346
|
+
rootProject = l
|
|
1347
|
+
.split("Root project ")[1]
|
|
1348
|
+
.split(" ")[0]
|
|
1349
|
+
.replace(/'/g, "");
|
|
1350
|
+
} else if (l.includes("--- Project")) {
|
|
1351
|
+
const tmpB = l.split("Project ");
|
|
1352
|
+
if (tmpB && tmpB.length > 1) {
|
|
1353
|
+
let projName = tmpB[1].split(" ")[0].replace(/'/g, "");
|
|
1354
|
+
// Include all projects including test projects
|
|
1355
|
+
if (projName.startsWith(":")) {
|
|
1356
|
+
projects.add(projName);
|
|
1357
|
+
}
|
|
1339
1358
|
}
|
|
1340
1359
|
}
|
|
1341
1360
|
});
|
|
1342
|
-
return
|
|
1361
|
+
return {
|
|
1362
|
+
rootProject,
|
|
1363
|
+
projects: Array.from(projects)
|
|
1364
|
+
};
|
|
1343
1365
|
}
|
|
1344
|
-
return
|
|
1366
|
+
return {
|
|
1367
|
+
rootProject,
|
|
1368
|
+
projects: []
|
|
1369
|
+
};
|
|
1345
1370
|
};
|
|
1346
1371
|
exports.parseGradleProjects = parseGradleProjects;
|
|
1347
1372
|
|
package/utils.test.js
CHANGED
|
@@ -208,11 +208,25 @@ test("parse gradle dependencies", () => {
|
|
|
208
208
|
});
|
|
209
209
|
|
|
210
210
|
test("parse gradle projects", () => {
|
|
211
|
-
expect(utils.parseGradleProjects(null)).toEqual(
|
|
212
|
-
|
|
211
|
+
expect(utils.parseGradleProjects(null)).toEqual({
|
|
212
|
+
projects: [],
|
|
213
|
+
rootProject: "root"
|
|
214
|
+
});
|
|
215
|
+
let retMap = utils.parseGradleProjects(
|
|
213
216
|
fs.readFileSync("./test/data/gradle-projects.out", { encoding: "utf-8" })
|
|
214
217
|
);
|
|
215
|
-
expect(
|
|
218
|
+
expect(retMap.rootProject).toEqual("elasticsearch");
|
|
219
|
+
expect(retMap.projects.length).toEqual(368);
|
|
220
|
+
retMap = utils.parseGradleProjects(
|
|
221
|
+
fs.readFileSync("./test/data/gradle-projects1.out", { encoding: "utf-8" })
|
|
222
|
+
);
|
|
223
|
+
expect(retMap.rootProject).toEqual("elasticsearch");
|
|
224
|
+
expect(retMap.projects.length).toEqual(403);
|
|
225
|
+
retMap = utils.parseGradleProjects(
|
|
226
|
+
fs.readFileSync("./test/data/gradle-projects2.out", { encoding: "utf-8" })
|
|
227
|
+
);
|
|
228
|
+
expect(retMap.rootProject).toEqual("fineract");
|
|
229
|
+
expect(retMap.projects.length).toEqual(22);
|
|
216
230
|
});
|
|
217
231
|
|
|
218
232
|
test("parse maven tree", () => {
|