@cyclonedx/cdxgen 10.5.2 → 10.6.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 +27 -14
- package/analyzer.js +1 -1
- package/index.js +238 -127
- package/package.json +19 -13
- package/types/evinser.d.ts +6 -6
- package/types/index.d.ts.map +1 -1
- package/types/utils.d.ts +17 -10
- package/types/utils.d.ts.map +1 -1
- package/utils.js +188 -43
- package/utils.test.js +76 -11
package/README.md
CHANGED
|
@@ -10,12 +10,16 @@
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
-
cdxgen is a CLI tool, library, [REPL](./ADVANCED.md), and server to create a valid and compliant [CycloneDX][cyclonedx-homepage] Bill of Materials (BOM) containing an aggregate of all project dependencies
|
|
13
|
+
cdxgen is a CLI tool, library, [REPL](./ADVANCED.md), and server to create a valid and compliant [CycloneDX][cyclonedx-homepage] Bill of Materials (BOM) containing an aggregate of all project dependencies in JSON format. CycloneDX is a full-stack BOM specification that is easily created, human and machine-readable, and simple to parse. The tool supports CycloneDX specification versions from 1.4 - 1.6.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Supported BOM formats:
|
|
16
16
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
17
|
+
- Software (SBOM) - For many languages and container images.
|
|
18
|
+
- Cryptography (CBOM) - For Java and Python projects.
|
|
19
|
+
- Operations (OBOM) - For Linux container images and VMs running Linux or Windows operating systems.
|
|
20
|
+
- Software-as-a-Service (SaaSBOM) - For Java, Python, JavaScript, TypeScript, and PHP projects.
|
|
21
|
+
- Attestations (CDXA) - Generate SBOM with templates for multiple standards. Sign the BOM document at a granular level to improve authenticity.
|
|
22
|
+
- Vulnerability Disclosure Report (VDR) - Use cdxgen with [OWASP depscan](https://github.com/owasp-dep-scan/dep-scan) to automate the generation of VDR at scale.
|
|
19
23
|
|
|
20
24
|
## Why cdxgen?
|
|
21
25
|
|
|
@@ -23,11 +27,19 @@ Most SBOM tools are like simple barcode scanners. For easy applications, they ca
|
|
|
23
27
|
|
|
24
28
|
<img src="./docs/_media/why-cdxgen.jpg" alt="why cdxgen" width="256">
|
|
25
29
|
|
|
30
|
+
Our philosophy:
|
|
31
|
+
|
|
32
|
+
- Explainability: Don't list, but explain with evidence.
|
|
33
|
+
- Precision: Try using multiple techniques to improve precision, even if it takes extra time.
|
|
34
|
+
- Personas: Cater to the needs of a range of personas such as security researchers, compliance auditors, developers, and SOC.
|
|
35
|
+
- Lifecycle: Support BOM generation for various product lifecycles.
|
|
36
|
+
|
|
26
37
|
## Documentation
|
|
27
38
|
|
|
28
|
-
Please visit our [documentation site][docs-homepage] for detailed usage, tutorials and support documentation.
|
|
39
|
+
Please visit our [documentation site][docs-homepage] for detailed usage, tutorials, and support documentation.
|
|
29
40
|
|
|
30
41
|
Sections include:
|
|
42
|
+
|
|
31
43
|
- [Getting Started][docs-homepage]
|
|
32
44
|
- [CLI Usage][docs-cli]
|
|
33
45
|
- [Server Usage][docs-server]
|
|
@@ -37,7 +49,6 @@ Sections include:
|
|
|
37
49
|
- [Permissions][docs-permissions]
|
|
38
50
|
- [Support (Enterprise & Community)][docs-support]
|
|
39
51
|
|
|
40
|
-
|
|
41
52
|
### Automatic usage detection
|
|
42
53
|
|
|
43
54
|
For node.js projects, lock files are parsed initially, so the SBOM would include all dependencies, including dev ones. An AST parser powered by babel-parser is then used to detect packages that are imported and used by non-test code. Such imported packages would automatically set their scope property to `required` in the resulting SBOM. You can turn off this analysis by passing the argument `--no-babel`. Scope property would then be set based on the `dev` attribute in the lock file.
|
|
@@ -472,8 +483,6 @@ Use the [CycloneDX CLI][cyclonedx-cli-github] tool for advanced use cases such a
|
|
|
472
483
|
|
|
473
484
|
Permission to modify and redistribute is granted under the terms of the Apache 2.0 license. See the [LICENSE][github-license] file for the full license.
|
|
474
485
|
|
|
475
|
-
|
|
476
|
-
|
|
477
486
|
## Integration as library
|
|
478
487
|
|
|
479
488
|
cdxgen is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) and could be imported and used with both deno and Node.js >= 20
|
|
@@ -498,21 +507,22 @@ const dbody = await submitBom(args, bomNSData.bomJson);
|
|
|
498
507
|
|
|
499
508
|
Please check out our [contribute to CycloneDX/cdxgen documentation][github-contribute] if you are interested in helping.
|
|
500
509
|
|
|
501
|
-
|
|
502
510
|
Before raising a PR, please run the following commands.
|
|
503
511
|
|
|
504
512
|
```bash
|
|
513
|
+
corepack enable
|
|
514
|
+
corepack pnpm install
|
|
505
515
|
# Generate types using jsdoc syntax
|
|
506
|
-
|
|
516
|
+
corepack pnpm run gen-types
|
|
507
517
|
# Run biomejs formatter and linter with auto fix
|
|
508
|
-
|
|
518
|
+
corepack pnpm run lint
|
|
509
519
|
# Run jest tests
|
|
510
|
-
|
|
520
|
+
corepack pnpm test
|
|
511
521
|
```
|
|
512
522
|
|
|
513
|
-
|
|
514
523
|
<!-- LINK LABELS -->
|
|
515
524
|
<!-- Badges -->
|
|
525
|
+
|
|
516
526
|
[badge-github-contributors]: https://img.shields.io/github/contributors/cyclonedx/cdxgen
|
|
517
527
|
[badge-github-license]: https://img.shields.io/github/license/cyclonedx/cdxgen
|
|
518
528
|
[badge-github-releases]: https://img.shields.io/github/v/release/cyclonedx/cdxgen
|
|
@@ -522,6 +532,7 @@ npm test
|
|
|
522
532
|
[badge-swh]: https://archive.softwareheritage.org/badge/origin/https://github.com/CycloneDX/cdxgen/
|
|
523
533
|
|
|
524
534
|
<!-- cdxgen github project -->
|
|
535
|
+
|
|
525
536
|
[github-contribute]: https://github.com/CycloneDX/cdxgen/contribute
|
|
526
537
|
[github-contributors]: https://github.com/CycloneDX/cdxgen/graphs/contributors
|
|
527
538
|
[github-issues]: https://github.com/CycloneDX/cdxgen/issues
|
|
@@ -529,6 +540,7 @@ npm test
|
|
|
529
540
|
[github-releases]: https://github.com/CycloneDX/cdxgen/releases
|
|
530
541
|
|
|
531
542
|
<!-- cdxgen documentation site -->
|
|
543
|
+
|
|
532
544
|
[docs-homepage]: https://cyclonedx.github.io/cdxgen
|
|
533
545
|
[docs-advanced-usage]: https://cyclonedx.github.io/cdxgen/#/ADVANCED
|
|
534
546
|
[docs-cli]: https://cyclonedx.github.io/cdxgen/#/CLI
|
|
@@ -539,6 +551,7 @@ npm test
|
|
|
539
551
|
[docs-support]: https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES
|
|
540
552
|
|
|
541
553
|
<!-- web links-->
|
|
554
|
+
|
|
542
555
|
[appthreat-homepage]: https://www.appthreat.com
|
|
543
556
|
[cyclonedx-homepage]: https://cyclonedx.org
|
|
544
557
|
[cyclonedx-cli-github]: https://github.com/CycloneDX/cyclonedx-cli
|
|
@@ -552,4 +565,4 @@ npm test
|
|
|
552
565
|
[npmjs-cdxgen]: https://www.npmjs.com/package/@cyclonedx/cdxgen
|
|
553
566
|
[podman-github-rootless]: https://github.com/containers/podman/blob/master/docs/tutorials/rootless_tutorial.md
|
|
554
567
|
[podman-github-remote]: https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md
|
|
555
|
-
[swh-cdxgen]: https://archive.softwareheritage.org/browse/origin/?origin_url=https://github.com/CycloneDX/cdxgen
|
|
568
|
+
[swh-cdxgen]: https://archive.softwareheritage.org/browse/origin/?origin_url=https://github.com/CycloneDX/cdxgen
|
package/analyzer.js
CHANGED
|
@@ -292,7 +292,7 @@ export const findJSImportsExports = async (src, deep) => {
|
|
|
292
292
|
const errFiles = [];
|
|
293
293
|
try {
|
|
294
294
|
const promiseMap = await getAllSrcJSAndTSFiles(src, deep);
|
|
295
|
-
const srcFiles = promiseMap.
|
|
295
|
+
const srcFiles = promiseMap.flat();
|
|
296
296
|
for (const file of srcFiles) {
|
|
297
297
|
try {
|
|
298
298
|
parseFileASTTree(src, file, allImports, allExports);
|
package/index.js
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
FETCH_LICENSE,
|
|
36
36
|
LEIN_CMD,
|
|
37
37
|
MAX_BUFFER,
|
|
38
|
+
PREFER_MAVEN_DEPS_TREE,
|
|
38
39
|
SWIFT_CMD,
|
|
39
40
|
TIMEOUT_MS,
|
|
40
41
|
addEvidenceForDotnet,
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
getSwiftPackageMetadata,
|
|
67
68
|
getTimestamp,
|
|
68
69
|
includeMavenTestScope,
|
|
70
|
+
isValidIriReference,
|
|
69
71
|
parseBazelActionGraph,
|
|
70
72
|
parseBazelSkyframe,
|
|
71
73
|
parseBdistMetadata,
|
|
@@ -726,7 +728,9 @@ function addExternalReferences(opkg) {
|
|
|
726
728
|
}
|
|
727
729
|
}
|
|
728
730
|
}
|
|
729
|
-
return externalReferences
|
|
731
|
+
return externalReferences
|
|
732
|
+
.map((reference) => ({ ...reference, url: reference.url.trim() }))
|
|
733
|
+
.filter((reference) => isValidIriReference(reference.url));
|
|
730
734
|
}
|
|
731
735
|
|
|
732
736
|
/**
|
|
@@ -1185,6 +1189,7 @@ export async function createJavaBom(path, options) {
|
|
|
1185
1189
|
// Support for tracking all the tools that created the BOM
|
|
1186
1190
|
// For java, this would correctly include the cyclonedx maven plugin.
|
|
1187
1191
|
let tools = undefined;
|
|
1192
|
+
let possible_misses = false;
|
|
1188
1193
|
// war/ear mode
|
|
1189
1194
|
if (path.endsWith(".war") || path.endsWith(".jar")) {
|
|
1190
1195
|
// Check if the file exists
|
|
@@ -1225,11 +1230,16 @@ export async function createJavaBom(path, options) {
|
|
|
1225
1230
|
pomFiles?.length &&
|
|
1226
1231
|
!["scala", "sbt", "gradle"].includes(options.projectType)
|
|
1227
1232
|
) {
|
|
1233
|
+
let result = undefined;
|
|
1228
1234
|
const cdxMavenPlugin =
|
|
1229
1235
|
process.env.CDX_MAVEN_PLUGIN ||
|
|
1230
1236
|
"org.cyclonedx:cyclonedx-maven-plugin:2.8.0";
|
|
1231
1237
|
const cdxMavenGoal = process.env.CDX_MAVEN_GOAL || "makeAggregateBom";
|
|
1232
|
-
let mvnArgs = [
|
|
1238
|
+
let mvnArgs = [
|
|
1239
|
+
"-fn",
|
|
1240
|
+
`${cdxMavenPlugin}:${cdxMavenGoal}`,
|
|
1241
|
+
"-DoutputName=bom",
|
|
1242
|
+
];
|
|
1233
1243
|
if (includeMavenTestScope) {
|
|
1234
1244
|
mvnArgs.push("-DincludeTestScope=true");
|
|
1235
1245
|
}
|
|
@@ -1247,6 +1257,7 @@ export async function createJavaBom(path, options) {
|
|
|
1247
1257
|
if (options.specVersion === 1.4) {
|
|
1248
1258
|
mvnArgs = mvnArgs.concat("-DschemaVersion=1.4");
|
|
1249
1259
|
}
|
|
1260
|
+
const firstPom = pomFiles.length ? pomFiles[0] : undefined;
|
|
1250
1261
|
for (const f of pomFiles) {
|
|
1251
1262
|
const basePath = dirname(f);
|
|
1252
1263
|
const settingsXml = join(basePath, "settings.xml");
|
|
@@ -1268,44 +1279,97 @@ export async function createJavaBom(path, options) {
|
|
|
1268
1279
|
jarNSMapping = { ...jarNSMapping, ...tmpjarNSMapping };
|
|
1269
1280
|
}
|
|
1270
1281
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
cwd: basePath,
|
|
1274
|
-
shell: true,
|
|
1275
|
-
encoding: "utf-8",
|
|
1276
|
-
timeout: TIMEOUT_MS,
|
|
1277
|
-
maxBuffer: MAX_BUFFER,
|
|
1278
|
-
});
|
|
1279
|
-
// Check if the cyclonedx plugin created the required bom.json file
|
|
1280
|
-
// Sometimes the plugin fails silently for complex maven projects
|
|
1281
|
-
bomJsonFiles = getAllFiles(path, "**/target/*.json", options);
|
|
1282
|
-
// Check if the bom json files got created in a directory other than target
|
|
1283
|
-
if (!bomJsonFiles.length) {
|
|
1284
|
-
bomJsonFiles = getAllFiles(path, "**/bom*.json", options);
|
|
1285
|
-
}
|
|
1286
|
-
const bomGenerated = bomJsonFiles.length;
|
|
1287
|
-
if (!bomGenerated || result.status !== 0 || result.error) {
|
|
1288
|
-
const tempDir = mkdtempSync(join(tmpdir(), "cdxmvn-"));
|
|
1289
|
-
const tempMvnTree = join(tempDir, "mvn-tree.txt");
|
|
1290
|
-
let mvnTreeArgs = ["dependency:tree", `-DoutputFile=${tempMvnTree}`];
|
|
1291
|
-
if (process.env.MVN_ARGS) {
|
|
1292
|
-
const addArgs = process.env.MVN_ARGS.split(" ");
|
|
1293
|
-
mvnTreeArgs = mvnTreeArgs.concat(addArgs);
|
|
1294
|
-
}
|
|
1282
|
+
// Use the cyclonedx maven plugin if there is no preference for maven deps tree
|
|
1283
|
+
if (!PREFER_MAVEN_DEPS_TREE) {
|
|
1295
1284
|
console.log(
|
|
1296
|
-
`
|
|
1285
|
+
`Executing '${mavenCmd} ${mvnArgs.join(" ")}' in`,
|
|
1286
|
+
basePath,
|
|
1297
1287
|
);
|
|
1298
|
-
result = spawnSync(mavenCmd,
|
|
1288
|
+
result = spawnSync(mavenCmd, mvnArgs, {
|
|
1299
1289
|
cwd: basePath,
|
|
1300
1290
|
shell: true,
|
|
1301
1291
|
encoding: "utf-8",
|
|
1302
1292
|
timeout: TIMEOUT_MS,
|
|
1303
1293
|
maxBuffer: MAX_BUFFER,
|
|
1304
1294
|
});
|
|
1295
|
+
// Check if the cyclonedx plugin created the required bom.json file
|
|
1296
|
+
// Sometimes the plugin fails silently for complex maven projects
|
|
1297
|
+
bomJsonFiles = getAllFiles(path, "**/target/*.json", options);
|
|
1298
|
+
// Check if the bom json files got created in a directory other than target
|
|
1299
|
+
if (!bomJsonFiles.length) {
|
|
1300
|
+
bomJsonFiles = getAllFiles(
|
|
1301
|
+
path,
|
|
1302
|
+
"target/**/*{cdx,bom}*.json",
|
|
1303
|
+
options,
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
// Also check if the user has a preference for maven deps tree command
|
|
1308
|
+
if (
|
|
1309
|
+
PREFER_MAVEN_DEPS_TREE ||
|
|
1310
|
+
!bomJsonFiles.length ||
|
|
1311
|
+
result?.status !== 0 ||
|
|
1312
|
+
result?.error
|
|
1313
|
+
) {
|
|
1314
|
+
const tempDir = mkdtempSync(join(tmpdir(), "cdxmvn-"));
|
|
1315
|
+
const tempMvnTree = join(tempDir, "mvn-tree.txt");
|
|
1316
|
+
const tempMvnParentTree = join(tempDir, "mvn-parent-tree.txt");
|
|
1317
|
+
let mvnTreeArgs = ["dependency:tree", `-DoutputFile=${tempMvnTree}`];
|
|
1318
|
+
if (process.env.MVN_ARGS) {
|
|
1319
|
+
const addArgs = process.env.MVN_ARGS.split(" ");
|
|
1320
|
+
mvnTreeArgs = mvnTreeArgs.concat(addArgs);
|
|
1321
|
+
}
|
|
1322
|
+
// Automatically use settings.xml to improve the success for fallback
|
|
1323
|
+
if (existsSync(settingsXml)) {
|
|
1324
|
+
mvnTreeArgs.push("-s");
|
|
1325
|
+
mvnTreeArgs.push(settingsXml);
|
|
1326
|
+
}
|
|
1327
|
+
// For the first pom alone, we need to execute first in non-recursive mode to capture
|
|
1328
|
+
// the parent component. Then, we execute all of them in recursive mode
|
|
1329
|
+
if (f === firstPom) {
|
|
1330
|
+
result = spawnSync(
|
|
1331
|
+
"mvn",
|
|
1332
|
+
["dependency:tree", "-N", `-DoutputFile=${tempMvnParentTree}`],
|
|
1333
|
+
{
|
|
1334
|
+
cwd: basePath,
|
|
1335
|
+
shell: true,
|
|
1336
|
+
encoding: "utf-8",
|
|
1337
|
+
timeout: TIMEOUT_MS,
|
|
1338
|
+
maxBuffer: MAX_BUFFER,
|
|
1339
|
+
},
|
|
1340
|
+
);
|
|
1341
|
+
if (result.status === 0) {
|
|
1342
|
+
if (existsSync(tempMvnParentTree)) {
|
|
1343
|
+
const mvnTreeString = readFileSync(tempMvnParentTree, {
|
|
1344
|
+
encoding: "utf-8",
|
|
1345
|
+
});
|
|
1346
|
+
const parsedList = parseMavenTree(mvnTreeString, f);
|
|
1347
|
+
const dlist = parsedList.pkgList;
|
|
1348
|
+
const tmpParentComponent = dlist.splice(0, 1)[0];
|
|
1349
|
+
tmpParentComponent.type = "application";
|
|
1350
|
+
parentComponent = tmpParentComponent;
|
|
1351
|
+
parentComponent.components = [];
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
console.log(`Executing 'mvn ${mvnTreeArgs.join(" ")}' in ${basePath}`);
|
|
1356
|
+
// Prefer the built-in maven
|
|
1357
|
+
result = spawnSync(
|
|
1358
|
+
PREFER_MAVEN_DEPS_TREE ? "mvn" : mavenCmd,
|
|
1359
|
+
mvnTreeArgs,
|
|
1360
|
+
{
|
|
1361
|
+
cwd: basePath,
|
|
1362
|
+
shell: true,
|
|
1363
|
+
encoding: "utf-8",
|
|
1364
|
+
timeout: TIMEOUT_MS,
|
|
1365
|
+
maxBuffer: MAX_BUFFER,
|
|
1366
|
+
},
|
|
1367
|
+
);
|
|
1305
1368
|
if (result.status !== 0 || result.error) {
|
|
1369
|
+
possible_misses = true;
|
|
1306
1370
|
// Our approach to recursively invoking the maven plugin for each sub-module is bound to result in failures
|
|
1307
1371
|
// These could be due to a range of reasons that are covered below.
|
|
1308
|
-
if (pomFiles.length === 1 || DEBUG_MODE) {
|
|
1372
|
+
if (pomFiles.length === 1 || DEBUG_MODE || PREFER_MAVEN_DEPS_TREE) {
|
|
1309
1373
|
console.error(result.stdout, result.stderr);
|
|
1310
1374
|
console.log("The above build errors could be due to:\n");
|
|
1311
1375
|
if (
|
|
@@ -1319,7 +1383,10 @@ export async function createJavaBom(path, options) {
|
|
|
1319
1383
|
} else if (
|
|
1320
1384
|
result.stdout &&
|
|
1321
1385
|
(result.stdout.includes("Could not resolve dependencies") ||
|
|
1322
|
-
result.stdout.includes("no dependency information available")
|
|
1386
|
+
result.stdout.includes("no dependency information available") ||
|
|
1387
|
+
result.stdout.includes(
|
|
1388
|
+
"The following artifacts could not be resolved",
|
|
1389
|
+
))
|
|
1323
1390
|
) {
|
|
1324
1391
|
console.log(
|
|
1325
1392
|
"1. Try building the project with 'mvn package -Dmaven.test.skip=true' using the correct version of Java and maven before invoking cdxgen.",
|
|
@@ -1358,95 +1425,120 @@ export async function createJavaBom(path, options) {
|
|
|
1358
1425
|
const mvnTreeString = readFileSync(tempMvnTree, {
|
|
1359
1426
|
encoding: "utf-8",
|
|
1360
1427
|
});
|
|
1361
|
-
const parsedList = parseMavenTree(mvnTreeString);
|
|
1428
|
+
const parsedList = parseMavenTree(mvnTreeString, f);
|
|
1362
1429
|
const dlist = parsedList.pkgList;
|
|
1363
|
-
|
|
1364
|
-
|
|
1430
|
+
const tmpParentComponent = dlist.splice(0, 1)[0];
|
|
1431
|
+
tmpParentComponent.type = "application";
|
|
1365
1432
|
if (dlist?.length) {
|
|
1366
1433
|
pkgList = pkgList.concat(dlist);
|
|
1367
1434
|
}
|
|
1435
|
+
// Retain the parent hierarchy
|
|
1436
|
+
if (!Object.keys(parentComponent).length) {
|
|
1437
|
+
parentComponent = tmpParentComponent;
|
|
1438
|
+
parentComponent.components = [];
|
|
1439
|
+
} else {
|
|
1440
|
+
parentComponent.components.push(tmpParentComponent);
|
|
1441
|
+
}
|
|
1368
1442
|
if (parsedList.dependenciesList && parsedList.dependenciesList) {
|
|
1369
|
-
dependencies =
|
|
1443
|
+
dependencies = mergeDependencies(
|
|
1444
|
+
dependencies,
|
|
1445
|
+
parsedList.dependenciesList,
|
|
1446
|
+
tmpParentComponent,
|
|
1447
|
+
);
|
|
1370
1448
|
}
|
|
1371
1449
|
unlinkSync(tempMvnTree);
|
|
1372
1450
|
}
|
|
1373
1451
|
}
|
|
1374
1452
|
}
|
|
1375
1453
|
} // for
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
readFileSync(abjson, {
|
|
1384
|
-
encoding: "utf-8",
|
|
1385
|
-
}),
|
|
1386
|
-
);
|
|
1387
|
-
if (bomJsonObj) {
|
|
1388
|
-
if (
|
|
1389
|
-
!tools &&
|
|
1390
|
-
bomJsonObj.metadata &&
|
|
1391
|
-
bomJsonObj.metadata.tools &&
|
|
1392
|
-
Array.isArray(bomJsonObj.metadata.tools)
|
|
1393
|
-
) {
|
|
1394
|
-
tools = bomJsonObj.metadata.tools;
|
|
1395
|
-
}
|
|
1396
|
-
if (
|
|
1397
|
-
bomJsonObj.metadata?.component &&
|
|
1398
|
-
!Object.keys(parentComponent).length
|
|
1399
|
-
) {
|
|
1400
|
-
parentComponent = bomJsonObj.metadata.component;
|
|
1401
|
-
options.parentComponent = parentComponent;
|
|
1402
|
-
pkgList = [];
|
|
1454
|
+
// Locate and parse all bom.json files from the maven plugin
|
|
1455
|
+
if (!PREFER_MAVEN_DEPS_TREE) {
|
|
1456
|
+
for (const abjson of bomJsonFiles) {
|
|
1457
|
+
let bomJsonObj = undefined;
|
|
1458
|
+
try {
|
|
1459
|
+
if (DEBUG_MODE) {
|
|
1460
|
+
console.log(`Extracting data from generated bom file ${abjson}`);
|
|
1403
1461
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1462
|
+
bomJsonObj = JSON.parse(
|
|
1463
|
+
readFileSync(abjson, {
|
|
1464
|
+
encoding: "utf-8",
|
|
1465
|
+
}),
|
|
1466
|
+
);
|
|
1467
|
+
if (bomJsonObj) {
|
|
1468
|
+
if (
|
|
1469
|
+
!tools &&
|
|
1470
|
+
bomJsonObj.metadata &&
|
|
1471
|
+
bomJsonObj.metadata.tools &&
|
|
1472
|
+
Array.isArray(bomJsonObj.metadata.tools)
|
|
1473
|
+
) {
|
|
1474
|
+
tools = bomJsonObj.metadata.tools;
|
|
1475
|
+
}
|
|
1476
|
+
if (
|
|
1477
|
+
bomJsonObj.metadata?.component &&
|
|
1478
|
+
!Object.keys(parentComponent).length
|
|
1479
|
+
) {
|
|
1480
|
+
parentComponent = bomJsonObj.metadata.component;
|
|
1481
|
+
options.parentComponent = parentComponent;
|
|
1482
|
+
pkgList = [];
|
|
1483
|
+
}
|
|
1484
|
+
if (bomJsonObj.components) {
|
|
1485
|
+
// Inject evidence into the components. #994
|
|
1486
|
+
if (options.specVersion >= 1.5) {
|
|
1487
|
+
// maven would usually generate a target directory closest to the pom.xml
|
|
1488
|
+
// I am sure there would be cases where this assumption is not true :)
|
|
1489
|
+
const srcPomFile = join(dirname(abjson), "..", "pom.xml");
|
|
1490
|
+
for (const acomp of bomJsonObj.components) {
|
|
1491
|
+
if (!acomp.evidence) {
|
|
1492
|
+
acomp.evidence = {
|
|
1493
|
+
identity: {
|
|
1494
|
+
field: "purl",
|
|
1495
|
+
confidence: 0.8,
|
|
1496
|
+
methods: [
|
|
1497
|
+
{
|
|
1498
|
+
technique: "manifest-analysis",
|
|
1499
|
+
confidence: 0.8,
|
|
1500
|
+
value: srcPomFile,
|
|
1501
|
+
},
|
|
1502
|
+
],
|
|
1503
|
+
},
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
if (!acomp.properties) {
|
|
1507
|
+
acomp.properties = [];
|
|
1508
|
+
}
|
|
1509
|
+
acomp.properties.push({
|
|
1510
|
+
name: "SrcFile",
|
|
1511
|
+
value: srcPomFile,
|
|
1512
|
+
});
|
|
1428
1513
|
}
|
|
1429
|
-
acomp.properties.push({
|
|
1430
|
-
name: "SrcFile",
|
|
1431
|
-
value: srcPomFile,
|
|
1432
|
-
});
|
|
1433
1514
|
}
|
|
1515
|
+
pkgList = pkgList.concat(bomJsonObj.components);
|
|
1516
|
+
}
|
|
1517
|
+
if (bomJsonObj.dependencies) {
|
|
1518
|
+
dependencies = mergeDependencies(
|
|
1519
|
+
dependencies,
|
|
1520
|
+
bomJsonObj.dependencies,
|
|
1521
|
+
parentComponent,
|
|
1522
|
+
);
|
|
1434
1523
|
}
|
|
1435
|
-
pkgList = pkgList.concat(bomJsonObj.components);
|
|
1436
1524
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
parentComponent,
|
|
1442
|
-
);
|
|
1525
|
+
} catch (err) {
|
|
1526
|
+
if (options.failOnError || DEBUG_MODE) {
|
|
1527
|
+
console.log(err);
|
|
1528
|
+
options.failOnError && process.exit(1);
|
|
1443
1529
|
}
|
|
1444
1530
|
}
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
if (possible_misses) {
|
|
1534
|
+
if (!DEBUG_MODE) {
|
|
1535
|
+
console.warn(
|
|
1536
|
+
"Multiple errors occurred while building this project with maven. The SBOM is therefore incomplete!",
|
|
1537
|
+
);
|
|
1538
|
+
} else if (!PREFER_MAVEN_DEPS_TREE) {
|
|
1539
|
+
console.log(
|
|
1540
|
+
"Try generating an SBOM with the maven dependency tree plugin. Set the environment variable PREFER_MAVEN_DEPS_TREE to true to enable this.",
|
|
1541
|
+
);
|
|
1450
1542
|
}
|
|
1451
1543
|
}
|
|
1452
1544
|
if (pkgList) {
|
|
@@ -4708,12 +4800,7 @@ export async function createCsharpBom(path, options) {
|
|
|
4708
4800
|
for (const f of filesToRestore) {
|
|
4709
4801
|
const buildCmd =
|
|
4710
4802
|
options.projectType === "dotnet-framework" ? "nuget" : "dotnet";
|
|
4711
|
-
|
|
4712
|
-
const basePath = dirname(f);
|
|
4713
|
-
console.log(`Executing '${buildCmd} restore' in ${basePath}`);
|
|
4714
|
-
}
|
|
4715
|
-
const result = spawnSync(
|
|
4716
|
-
buildCmd,
|
|
4803
|
+
const buildArgs =
|
|
4717
4804
|
options.projectType === "dotnet-framework"
|
|
4718
4805
|
? [
|
|
4719
4806
|
"restore",
|
|
@@ -4724,23 +4811,38 @@ export async function createCsharpBom(path, options) {
|
|
|
4724
4811
|
"-Verbosity",
|
|
4725
4812
|
"quiet",
|
|
4726
4813
|
]
|
|
4727
|
-
: ["restore", "--force", "--ignore-failed-sources", f]
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
encoding: "utf-8",
|
|
4731
|
-
},
|
|
4732
|
-
);
|
|
4733
|
-
if (DEBUG_MODE && (result.status !== 0 || result.error)) {
|
|
4734
|
-
console.error(
|
|
4735
|
-
`Restore has failed. Check if ${buildCmd} is installed and available in PATH.`,
|
|
4736
|
-
);
|
|
4737
|
-
console.log(
|
|
4738
|
-
"Authenticate with any private registries such as Azure Artifacts feed before running cdxgen.",
|
|
4739
|
-
);
|
|
4814
|
+
: ["restore", "--force", "--ignore-failed-sources", f];
|
|
4815
|
+
if (DEBUG_MODE) {
|
|
4816
|
+
const basePath = dirname(f);
|
|
4740
4817
|
console.log(
|
|
4741
|
-
|
|
4818
|
+
`Executing '${buildCmd} ${buildArgs.join(" ")}' in ${basePath}`,
|
|
4742
4819
|
);
|
|
4743
|
-
|
|
4820
|
+
}
|
|
4821
|
+
const result = spawnSync(buildCmd, buildArgs, {
|
|
4822
|
+
cwd: path,
|
|
4823
|
+
encoding: "utf-8",
|
|
4824
|
+
env: { ...process.env, DOTNET_ROLL_FORWARD: "Major" },
|
|
4825
|
+
});
|
|
4826
|
+
if (DEBUG_MODE && (result.status !== 0 || result.error)) {
|
|
4827
|
+
if (result?.stderr?.includes("To install missing framework")) {
|
|
4828
|
+
console.log(
|
|
4829
|
+
"This project requires a specific version of dotnet sdk to be installed. The cdxgen container image bundles dotnet SDK 8.0, which might be incompatible.",
|
|
4830
|
+
);
|
|
4831
|
+
console.log(
|
|
4832
|
+
"Try using the unofficial `ghcr.io/appthreat/cdxgen-dotnet6:v10` or `ghcr.io/appthreat/cdxgen-dotnet7:v10` container images.",
|
|
4833
|
+
);
|
|
4834
|
+
} else {
|
|
4835
|
+
console.error(
|
|
4836
|
+
`Restore has failed. Check if ${buildCmd} is installed and available in PATH.`,
|
|
4837
|
+
);
|
|
4838
|
+
console.log(
|
|
4839
|
+
"Authenticate with any private registries such as Azure Artifacts feed before running cdxgen.",
|
|
4840
|
+
);
|
|
4841
|
+
console.log(
|
|
4842
|
+
"Alternatively, try using the unofficial `ghcr.io/appthreat/cdxgen-dotnet6:v10` container image, which bundles nuget (mono) and a range of dotnet SDKs.",
|
|
4843
|
+
);
|
|
4844
|
+
}
|
|
4845
|
+
console.log(result.stdout, result.stderr);
|
|
4744
4846
|
options.failOnError && process.exit(1);
|
|
4745
4847
|
}
|
|
4746
4848
|
}
|
|
@@ -4932,7 +5034,7 @@ export async function createCsharpBom(path, options) {
|
|
|
4932
5034
|
parentComponent = retMap.parentComponent;
|
|
4933
5035
|
}
|
|
4934
5036
|
}
|
|
4935
|
-
if (retMap?.pkgList
|
|
5037
|
+
if (retMap?.pkgList?.length) {
|
|
4936
5038
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
4937
5039
|
}
|
|
4938
5040
|
if (retMap.dependencies?.length) {
|
|
@@ -5062,10 +5164,11 @@ export function mergeDependencies(
|
|
|
5062
5164
|
}
|
|
5063
5165
|
if (adep["dependsOn"]) {
|
|
5064
5166
|
for (const eachDepends of adep["dependsOn"]) {
|
|
5065
|
-
if (
|
|
5066
|
-
parentRef
|
|
5067
|
-
|
|
5068
|
-
|
|
5167
|
+
if (parentRef) {
|
|
5168
|
+
if (eachDepends.toLowerCase() !== parentRef.toLowerCase()) {
|
|
5169
|
+
deps_map[adep.ref].add(eachDepends);
|
|
5170
|
+
}
|
|
5171
|
+
} else {
|
|
5069
5172
|
deps_map[adep.ref].add(eachDepends);
|
|
5070
5173
|
}
|
|
5071
5174
|
}
|
|
@@ -5132,6 +5235,14 @@ export function trimComponents(components) {
|
|
|
5132
5235
|
}
|
|
5133
5236
|
}
|
|
5134
5237
|
}
|
|
5238
|
+
// If the component is required in any of the child projects, then make it required
|
|
5239
|
+
if (
|
|
5240
|
+
existingComponent?.scope !== "required" &&
|
|
5241
|
+
comp?.scope === "required"
|
|
5242
|
+
) {
|
|
5243
|
+
existingComponent.scope = "required";
|
|
5244
|
+
keyCache[key] = existingComponent;
|
|
5245
|
+
}
|
|
5135
5246
|
if (compProps.length) {
|
|
5136
5247
|
existingComponent.properties = compProps;
|
|
5137
5248
|
keyCache[key] = existingComponent;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.6.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>",
|
|
@@ -47,14 +47,6 @@
|
|
|
47
47
|
"evinse": "bin/evinse.js",
|
|
48
48
|
"cdx-verify": "bin/verify.js"
|
|
49
49
|
},
|
|
50
|
-
"scripts": {
|
|
51
|
-
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --inject-globals false docker.test.js utils.test.js display.test.js postgen.test.js",
|
|
52
|
-
"watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch --inject-globals false",
|
|
53
|
-
"lint:check": "biome check *",
|
|
54
|
-
"lint": "biome check --apply *",
|
|
55
|
-
"lint:errors": "biome check * --diagnostic-level=error",
|
|
56
|
-
"gen-types": "npx -p typescript tsc"
|
|
57
|
-
},
|
|
58
50
|
"engines": {
|
|
59
51
|
"node": ">=20"
|
|
60
52
|
},
|
|
@@ -90,7 +82,8 @@
|
|
|
90
82
|
"tar": "^6.2.1",
|
|
91
83
|
"uuid": "^9.0.1",
|
|
92
84
|
"xml-js": "^1.6.11",
|
|
93
|
-
"yargs": "^17.7.2"
|
|
85
|
+
"yargs": "^17.7.2",
|
|
86
|
+
"validate-iri": "^1.0.1"
|
|
94
87
|
},
|
|
95
88
|
"optionalDependencies": {
|
|
96
89
|
"@appthreat/atom": "2.0.12",
|
|
@@ -109,10 +102,23 @@
|
|
|
109
102
|
"sequelize": "^6.37.3",
|
|
110
103
|
"sqlite3": "^5.1.7"
|
|
111
104
|
},
|
|
112
|
-
"files": [
|
|
105
|
+
"files": [
|
|
106
|
+
"*.js",
|
|
107
|
+
"bin/",
|
|
108
|
+
"data/",
|
|
109
|
+
"types/"
|
|
110
|
+
],
|
|
113
111
|
"devDependencies": {
|
|
114
|
-
"@biomejs/biome": "1.
|
|
112
|
+
"@biomejs/biome": "1.8.0",
|
|
115
113
|
"jest": "^29.7.0",
|
|
116
114
|
"typescript": "^5.4.5"
|
|
115
|
+
},
|
|
116
|
+
"scripts": {
|
|
117
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --inject-globals false docker.test.js utils.test.js display.test.js postgen.test.js",
|
|
118
|
+
"watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch --inject-globals false",
|
|
119
|
+
"lint:check": "biome check",
|
|
120
|
+
"lint": "biome check --fix",
|
|
121
|
+
"lint:errors": "biome check --diagnostic-level=error",
|
|
122
|
+
"gen-types": "npx -p typescript tsc"
|
|
117
123
|
}
|
|
118
|
-
}
|
|
124
|
+
}
|
package/types/evinser.d.ts
CHANGED
|
@@ -104,8 +104,8 @@ export function prepareDB(options: any): Promise<{
|
|
|
104
104
|
count: number;
|
|
105
105
|
}>;
|
|
106
106
|
findAndCountAll<M_17 extends import("sequelize").Model<any, any>>(this: import("sequelize").ModelStatic<M_17>, options: {
|
|
107
|
-
type?: string;
|
|
108
107
|
attributes?: import("sequelize").FindAttributeOptions;
|
|
108
|
+
type?: string;
|
|
109
109
|
plain?: boolean;
|
|
110
110
|
logging?: boolean | ((sql: string, timing?: number) => void);
|
|
111
111
|
where?: import("sequelize").WhereOptions<import("sequelize").Attributes<M_17>>;
|
|
@@ -127,7 +127,7 @@ export function prepareDB(options: any): Promise<{
|
|
|
127
127
|
bind?: import("sequelize").BindOrReplacements;
|
|
128
128
|
instance?: import("sequelize").Model<any, any>;
|
|
129
129
|
mapToModel?: boolean;
|
|
130
|
-
retry?: import("retry-as-promised").Options;
|
|
130
|
+
retry?: import(".pnpm/retry-as-promised@7.0.4/node_modules/retry-as-promised").Options;
|
|
131
131
|
fieldMap?: import("sequelize").FieldMap;
|
|
132
132
|
benchmark?: boolean;
|
|
133
133
|
transaction?: import("sequelize").Transaction;
|
|
@@ -333,8 +333,8 @@ export function prepareDB(options: any): Promise<{
|
|
|
333
333
|
count: number;
|
|
334
334
|
}>;
|
|
335
335
|
findAndCountAll<M_17 extends import("sequelize").Model<any, any>>(this: import("sequelize").ModelStatic<M_17>, options: {
|
|
336
|
-
type?: string;
|
|
337
336
|
attributes?: import("sequelize").FindAttributeOptions;
|
|
337
|
+
type?: string;
|
|
338
338
|
plain?: boolean;
|
|
339
339
|
logging?: boolean | ((sql: string, timing?: number) => void);
|
|
340
340
|
where?: import("sequelize").WhereOptions<import("sequelize").Attributes<M_17>>;
|
|
@@ -356,7 +356,7 @@ export function prepareDB(options: any): Promise<{
|
|
|
356
356
|
bind?: import("sequelize").BindOrReplacements;
|
|
357
357
|
instance?: import("sequelize").Model<any, any>;
|
|
358
358
|
mapToModel?: boolean;
|
|
359
|
-
retry?: import("retry-as-promised").Options;
|
|
359
|
+
retry?: import(".pnpm/retry-as-promised@7.0.4/node_modules/retry-as-promised").Options;
|
|
360
360
|
fieldMap?: import("sequelize").FieldMap;
|
|
361
361
|
benchmark?: boolean;
|
|
362
362
|
transaction?: import("sequelize").Transaction;
|
|
@@ -562,8 +562,8 @@ export function prepareDB(options: any): Promise<{
|
|
|
562
562
|
count: number;
|
|
563
563
|
}>;
|
|
564
564
|
findAndCountAll<M_17 extends import("sequelize").Model<any, any>>(this: import("sequelize").ModelStatic<M_17>, options: {
|
|
565
|
-
type?: string;
|
|
566
565
|
attributes?: import("sequelize").FindAttributeOptions;
|
|
566
|
+
type?: string;
|
|
567
567
|
plain?: boolean;
|
|
568
568
|
logging?: boolean | ((sql: string, timing?: number) => void);
|
|
569
569
|
where?: import("sequelize").WhereOptions<import("sequelize").Attributes<M_17>>;
|
|
@@ -585,7 +585,7 @@ export function prepareDB(options: any): Promise<{
|
|
|
585
585
|
bind?: import("sequelize").BindOrReplacements;
|
|
586
586
|
instance?: import("sequelize").Model<any, any>;
|
|
587
587
|
mapToModel?: boolean;
|
|
588
|
-
retry?: import("retry-as-promised").Options;
|
|
588
|
+
retry?: import(".pnpm/retry-as-promised@7.0.4/node_modules/retry-as-promised").Options;
|
|
589
589
|
fieldMap?: import("sequelize").FieldMap;
|
|
590
590
|
benchmark?: boolean;
|
|
591
591
|
transaction?: import("sequelize").Transaction;
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.js"],"names":[],"mappings":"AA+tBA;;;;;;;;GAQG;AACH,gFAFW,MAAM,SAchB;AA4TD;;;;;;;GAOG;AACH,mCALW,MAAM,qBAiEhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM;;;;EAKhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM;;;;EAkBhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAq+BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAochB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAgWhB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,8BA8ThB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAqIhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAiDhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBA+KhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBAsHhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,0CAHW,MAAM,qBAuBhB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,8BAqDhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,8BA4ChB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,qCAHW,MAAM,8BAwFhB;AAED;;;;;GAKG;AACH,iDAHW,MAAM,qBA8ThB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBAwJhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAmFhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAyWhB;AAED;;;;;GAKG;AACH,2CAHW,MAAM;;;;;;;;;;;;;;;;;;;;GAoChB;AAED;;;;;;;;KA+DC;AAED;;;;;;GAMG;AACH,yDA2CC;AAED;;;;;;;;;GASG;AACH,2GA6BC;AAED;;;;;GAKG;AACH,0CAHW,MAAM,8BAoZhB;AAED;;;;;GAKG;AACH,iCAHW,MAAM,8BAkUhB;AAED;;;;;GAKG;AACH,gCAHW,MAAM,qBAuQhB;AAED;;;;;GAKG;AACH,qEAyFC"}
|
package/types/utils.d.ts
CHANGED
|
@@ -172,17 +172,11 @@ export function parsePom(pomFile: any): {
|
|
|
172
172
|
/**
|
|
173
173
|
* Parse maven tree output
|
|
174
174
|
* @param {string} rawOutput Raw string output
|
|
175
|
+
* @param {string} pomFile .pom file for evidence
|
|
176
|
+
*
|
|
177
|
+
* @returns {Object} Object containing packages and dependencies
|
|
175
178
|
*/
|
|
176
|
-
export function parseMavenTree(rawOutput: string):
|
|
177
|
-
pkgList?: undefined;
|
|
178
|
-
dependenciesList?: undefined;
|
|
179
|
-
} | {
|
|
180
|
-
pkgList: any[];
|
|
181
|
-
dependenciesList: {
|
|
182
|
-
ref: string;
|
|
183
|
-
dependsOn: any;
|
|
184
|
-
}[];
|
|
185
|
-
};
|
|
179
|
+
export function parseMavenTree(rawOutput: string, pomFile: string): any;
|
|
186
180
|
/**
|
|
187
181
|
* Parse gradle dependencies output
|
|
188
182
|
* @param {string} rawOutput Raw string output
|
|
@@ -1119,6 +1113,18 @@ export function addEvidenceForDotnet(pkgList: any, slicesFile: any): any;
|
|
|
1119
1113
|
* @returns {Object} pkgFilesMap Object with package name and list of files
|
|
1120
1114
|
*/
|
|
1121
1115
|
export function parseMakeDFile(dfile: string): any;
|
|
1116
|
+
/**
|
|
1117
|
+
* Function to validate an externalReference URL for conforming to the JSON schema or bomLink
|
|
1118
|
+
* https://github.com/CycloneDX/cyclonedx-core-java/blob/75575318b268dda9e2a290761d7db11b4f414255/src/main/resources/bom-1.5.schema.json#L1140
|
|
1119
|
+
* https://datatracker.ietf.org/doc/html/rfc3987#section-2.2
|
|
1120
|
+
* https://cyclonedx.org/capabilities/bomlink/
|
|
1121
|
+
*
|
|
1122
|
+
* @param {String} iri IRI to validate
|
|
1123
|
+
*
|
|
1124
|
+
* @returns {Boolean} Flag indicating whether the supplied URL is valid or not
|
|
1125
|
+
*
|
|
1126
|
+
*/
|
|
1127
|
+
export function isValidIriReference(iri: string): boolean;
|
|
1122
1128
|
export const dirNameStr: string;
|
|
1123
1129
|
export const isWin: boolean;
|
|
1124
1130
|
export const isMac: boolean;
|
|
@@ -1129,6 +1135,7 @@ export const TIMEOUT_MS: number;
|
|
|
1129
1135
|
export const MAX_BUFFER: number;
|
|
1130
1136
|
export let metadata_cache: {};
|
|
1131
1137
|
export const includeMavenTestScope: boolean;
|
|
1138
|
+
export const PREFER_MAVEN_DEPS_TREE: boolean;
|
|
1132
1139
|
export const FETCH_LICENSE: boolean;
|
|
1133
1140
|
export const SEARCH_MAVEN_ORG: boolean;
|
|
1134
1141
|
export let JAVA_CMD: string;
|
package/types/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.js"],"names":[],"mappings":"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"}
|
package/utils.js
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
satisfies,
|
|
47
47
|
valid,
|
|
48
48
|
} from "semver";
|
|
49
|
+
import { IriValidationStrategy, validateIri } from "validate-iri";
|
|
49
50
|
import { xml2js } from "xml-js";
|
|
50
51
|
import { getTreeWithPlugin } from "./piptree.js";
|
|
51
52
|
|
|
@@ -121,6 +122,11 @@ export const includeMavenTestScope =
|
|
|
121
122
|
!process.env.CDX_MAVEN_INCLUDE_TEST_SCOPE ||
|
|
122
123
|
["true", "1"].includes(process.env.CDX_MAVEN_INCLUDE_TEST_SCOPE);
|
|
123
124
|
|
|
125
|
+
// Whether to use the native maven dependency tree command
|
|
126
|
+
export const PREFER_MAVEN_DEPS_TREE =
|
|
127
|
+
process.env.PREFER_MAVEN_DEPS_TREE &&
|
|
128
|
+
["true", "1"].includes(process.env.PREFER_MAVEN_DEPS_TREE);
|
|
129
|
+
|
|
124
130
|
// Whether license information should be fetched
|
|
125
131
|
export const FETCH_LICENSE =
|
|
126
132
|
process.env.FETCH_LICENSE &&
|
|
@@ -1435,15 +1441,29 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) {
|
|
|
1435
1441
|
if (!yamlObj) {
|
|
1436
1442
|
return {};
|
|
1437
1443
|
}
|
|
1444
|
+
let lockfileVersion = yamlObj.lockfileVersion;
|
|
1445
|
+
try {
|
|
1446
|
+
lockfileVersion = Number.parseFloat(lockfileVersion, 10);
|
|
1447
|
+
} catch (e) {
|
|
1448
|
+
// ignore parse errors
|
|
1449
|
+
}
|
|
1438
1450
|
// This logic matches the pnpm list command to include only direct dependencies
|
|
1439
1451
|
if (ppurl !== "") {
|
|
1440
|
-
|
|
1452
|
+
// In lock file version 9, direct dependencies is under importers
|
|
1453
|
+
const ddeps =
|
|
1454
|
+
lockfileVersion >= 9
|
|
1455
|
+
? yamlObj.importers["."]?.dependencies || {}
|
|
1456
|
+
: yamlObj.dependencies || {};
|
|
1441
1457
|
const ddeplist = [];
|
|
1442
1458
|
for (const dk of Object.keys(ddeps)) {
|
|
1443
1459
|
let version = ddeps[dk];
|
|
1444
1460
|
if (typeof version === "object" && version.version) {
|
|
1445
1461
|
version = version.version;
|
|
1446
1462
|
}
|
|
1463
|
+
// version: 3.0.1(ajv@8.14.0)
|
|
1464
|
+
if (version?.includes("(")) {
|
|
1465
|
+
version = version.split("(")[0];
|
|
1466
|
+
}
|
|
1447
1467
|
const dpurl = new PackageURL(
|
|
1448
1468
|
"npm",
|
|
1449
1469
|
"",
|
|
@@ -1459,13 +1479,9 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) {
|
|
|
1459
1479
|
dependsOn: ddeplist,
|
|
1460
1480
|
});
|
|
1461
1481
|
}
|
|
1462
|
-
let lockfileVersion = yamlObj.lockfileVersion;
|
|
1463
|
-
try {
|
|
1464
|
-
lockfileVersion = Number.parseInt(lockfileVersion, 10);
|
|
1465
|
-
} catch (e) {
|
|
1466
|
-
// ignore parse errors
|
|
1467
|
-
}
|
|
1468
1482
|
const packages = yamlObj.packages || {};
|
|
1483
|
+
// snapshots is a new key under lockfile version 9
|
|
1484
|
+
const snapshots = yamlObj.snapshots || {};
|
|
1469
1485
|
const pkgKeys = Object.keys(packages);
|
|
1470
1486
|
for (const k in pkgKeys) {
|
|
1471
1487
|
// Eg: @babel/code-frame/7.10.1
|
|
@@ -1477,20 +1493,40 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) {
|
|
|
1477
1493
|
}
|
|
1478
1494
|
const parts = fullName.split("/");
|
|
1479
1495
|
const integrity = packages[pkgKeys[k]].resolution.integrity;
|
|
1480
|
-
|
|
1496
|
+
// In lock file version 9, dependencies is under snapshots
|
|
1497
|
+
const deps =
|
|
1498
|
+
packages[pkgKeys[k]].dependencies ||
|
|
1499
|
+
snapshots[pkgKeys[k]]?.dependencies ||
|
|
1500
|
+
[];
|
|
1481
1501
|
const scope = packages[pkgKeys[k]].dev === true ? "optional" : undefined;
|
|
1482
1502
|
if (parts?.length) {
|
|
1483
1503
|
let name = "";
|
|
1484
1504
|
let version = "";
|
|
1485
1505
|
let group = "";
|
|
1486
|
-
|
|
1506
|
+
const hasBin = packages[pkgKeys[k]]?.hasBin;
|
|
1507
|
+
const deprecatedMessage = packages[pkgKeys[k]]?.deprecated;
|
|
1508
|
+
if (lockfileVersion >= 9 && fullName.includes("@")) {
|
|
1509
|
+
group = parts.length > 1 ? parts[0] : "";
|
|
1510
|
+
const tmpA = parts[parts.length - 1].split("@");
|
|
1511
|
+
if (tmpA.length > 1) {
|
|
1512
|
+
name = tmpA[0];
|
|
1513
|
+
version = tmpA[1];
|
|
1514
|
+
}
|
|
1515
|
+
if (version?.includes("(")) {
|
|
1516
|
+
version = version.split("(")[0];
|
|
1517
|
+
}
|
|
1518
|
+
} else if (
|
|
1519
|
+
lockfileVersion >= 6 &&
|
|
1520
|
+
lockfileVersion < 9 &&
|
|
1521
|
+
fullName.includes("@")
|
|
1522
|
+
) {
|
|
1487
1523
|
const tmpA = parts[parts.length - 1].split("@");
|
|
1488
1524
|
group = parts[0];
|
|
1489
1525
|
if (parts.length === 2 && tmpA.length > 1) {
|
|
1490
1526
|
name = tmpA[0];
|
|
1491
1527
|
version = tmpA[1];
|
|
1492
1528
|
} else {
|
|
1493
|
-
console.log(parts, fullName);
|
|
1529
|
+
console.log("Missed", parts, fullName);
|
|
1494
1530
|
}
|
|
1495
1531
|
} else {
|
|
1496
1532
|
if (parts.length === 2) {
|
|
@@ -1519,12 +1555,24 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) {
|
|
|
1519
1555
|
null,
|
|
1520
1556
|
).toString();
|
|
1521
1557
|
const deplist = [];
|
|
1522
|
-
for (
|
|
1558
|
+
for (let dpkgName of Object.keys(deps)) {
|
|
1559
|
+
let vers = deps[dpkgName];
|
|
1560
|
+
if (vers?.includes("(")) {
|
|
1561
|
+
vers = vers.split("(")[0];
|
|
1562
|
+
}
|
|
1563
|
+
// string-width-cjs: string-width@4.2.3
|
|
1564
|
+
if (dpkgName.endsWith("-cjs")) {
|
|
1565
|
+
const tmpB = vers.split("@");
|
|
1566
|
+
if (tmpB.length > 1) {
|
|
1567
|
+
dpkgName = tmpB[0];
|
|
1568
|
+
vers = tmpB[1];
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1523
1571
|
const dpurlString = new PackageURL(
|
|
1524
1572
|
"npm",
|
|
1525
1573
|
"",
|
|
1526
1574
|
dpkgName,
|
|
1527
|
-
|
|
1575
|
+
vers,
|
|
1528
1576
|
null,
|
|
1529
1577
|
null,
|
|
1530
1578
|
).toString();
|
|
@@ -1534,6 +1582,24 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) {
|
|
|
1534
1582
|
ref: decodeURIComponent(purlString),
|
|
1535
1583
|
dependsOn: deplist,
|
|
1536
1584
|
});
|
|
1585
|
+
const properties = [
|
|
1586
|
+
{
|
|
1587
|
+
name: "SrcFile",
|
|
1588
|
+
value: pnpmLock,
|
|
1589
|
+
},
|
|
1590
|
+
];
|
|
1591
|
+
if (hasBin) {
|
|
1592
|
+
properties.push({
|
|
1593
|
+
name: "cdx:npm:has_binary",
|
|
1594
|
+
value: `${hasBin}`,
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
if (deprecatedMessage) {
|
|
1598
|
+
properties.push({
|
|
1599
|
+
name: "cdx:npm:deprecation_notice",
|
|
1600
|
+
value: deprecatedMessage,
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1537
1603
|
pkgList.push({
|
|
1538
1604
|
group: group,
|
|
1539
1605
|
name: name,
|
|
@@ -1542,12 +1608,7 @@ export async function parsePnpmLock(pnpmLock, parentComponent = null) {
|
|
|
1542
1608
|
"bom-ref": decodeURIComponent(purlString),
|
|
1543
1609
|
scope,
|
|
1544
1610
|
_integrity: integrity,
|
|
1545
|
-
properties
|
|
1546
|
-
{
|
|
1547
|
-
name: "SrcFile",
|
|
1548
|
-
value: pnpmLock,
|
|
1549
|
-
},
|
|
1550
|
-
],
|
|
1611
|
+
properties,
|
|
1551
1612
|
evidence: {
|
|
1552
1613
|
identity: {
|
|
1553
1614
|
field: "purl",
|
|
@@ -1784,8 +1845,11 @@ export function parsePom(pomFile) {
|
|
|
1784
1845
|
/**
|
|
1785
1846
|
* Parse maven tree output
|
|
1786
1847
|
* @param {string} rawOutput Raw string output
|
|
1848
|
+
* @param {string} pomFile .pom file for evidence
|
|
1849
|
+
*
|
|
1850
|
+
* @returns {Object} Object containing packages and dependencies
|
|
1787
1851
|
*/
|
|
1788
|
-
export function parseMavenTree(rawOutput) {
|
|
1852
|
+
export function parseMavenTree(rawOutput, pomFile) {
|
|
1789
1853
|
if (!rawOutput) {
|
|
1790
1854
|
return {};
|
|
1791
1855
|
}
|
|
@@ -1810,29 +1874,83 @@ export function parseMavenTree(rawOutput) {
|
|
|
1810
1874
|
}
|
|
1811
1875
|
l = tmpline[tmpline.length - 1];
|
|
1812
1876
|
const pkgArr = l.split(":");
|
|
1877
|
+
// Support for classifiers
|
|
1878
|
+
// com.github.jnr:jffi:jar:1.3.11:compile
|
|
1879
|
+
// com.github.jnr:jffi:jar:native:1.3.11:runtime
|
|
1880
|
+
let classifier = undefined;
|
|
1813
1881
|
if (pkgArr && pkgArr.length > 2) {
|
|
1814
1882
|
let versionStr = pkgArr[pkgArr.length - 2];
|
|
1883
|
+
const componentScope = pkgArr[pkgArr.length - 1];
|
|
1884
|
+
if (pkgArr.length >= 6 && pkgArr[3] !== versionStr) {
|
|
1885
|
+
classifier = pkgArr[3];
|
|
1886
|
+
}
|
|
1887
|
+
// Ignore test scope
|
|
1888
|
+
if (!includeMavenTestScope && componentScope === "test") {
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
let scope = undefined;
|
|
1892
|
+
if (["compile", "runtime"].includes(componentScope)) {
|
|
1893
|
+
scope = "required";
|
|
1894
|
+
} else if (componentScope === "test") {
|
|
1895
|
+
scope = "optional";
|
|
1896
|
+
} else if (componentScope === "provided") {
|
|
1897
|
+
scope = "excluded";
|
|
1898
|
+
}
|
|
1815
1899
|
if (pkgArr.length === 4) {
|
|
1816
1900
|
versionStr = pkgArr[pkgArr.length - 1];
|
|
1817
1901
|
}
|
|
1818
|
-
const
|
|
1902
|
+
const qualifiers = { type: pkgArr[2] };
|
|
1903
|
+
if (classifier) {
|
|
1904
|
+
qualifiers.classifier = classifier;
|
|
1905
|
+
}
|
|
1906
|
+
const purlString = new PackageURL(
|
|
1907
|
+
"maven",
|
|
1908
|
+
pkgArr[0],
|
|
1909
|
+
pkgArr[1],
|
|
1910
|
+
versionStr,
|
|
1911
|
+
qualifiers,
|
|
1912
|
+
null,
|
|
1913
|
+
).toString();
|
|
1914
|
+
const key = purlString;
|
|
1819
1915
|
if (!keys_cache[key]) {
|
|
1820
1916
|
keys_cache[key] = key;
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
purlString = decodeURIComponent(purlString);
|
|
1830
|
-
deps.push({
|
|
1917
|
+
const properties = [];
|
|
1918
|
+
if (scope) {
|
|
1919
|
+
properties.push({
|
|
1920
|
+
name: "cdx:maven:component_scope",
|
|
1921
|
+
value: componentScope,
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
const apkg = {
|
|
1831
1925
|
group: pkgArr[0],
|
|
1832
1926
|
name: pkgArr[1],
|
|
1833
1927
|
version: versionStr,
|
|
1834
|
-
qualifiers
|
|
1835
|
-
|
|
1928
|
+
qualifiers,
|
|
1929
|
+
scope,
|
|
1930
|
+
properties,
|
|
1931
|
+
purl: purlString,
|
|
1932
|
+
"bom-ref": decodeURIComponent(purlString),
|
|
1933
|
+
};
|
|
1934
|
+
if (pomFile) {
|
|
1935
|
+
properties.push({
|
|
1936
|
+
name: "SrcFile",
|
|
1937
|
+
value: pomFile,
|
|
1938
|
+
});
|
|
1939
|
+
apkg.evidence = {
|
|
1940
|
+
identity: {
|
|
1941
|
+
field: "purl",
|
|
1942
|
+
confidence: 0.5,
|
|
1943
|
+
methods: [
|
|
1944
|
+
{
|
|
1945
|
+
technique: "manifest-analysis",
|
|
1946
|
+
confidence: 0.5,
|
|
1947
|
+
value: pomFile,
|
|
1948
|
+
},
|
|
1949
|
+
],
|
|
1950
|
+
},
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
deps.push(apkg);
|
|
1836
1954
|
if (!level_trees[purlString]) {
|
|
1837
1955
|
level_trees[purlString] = [];
|
|
1838
1956
|
}
|
|
@@ -6282,14 +6400,19 @@ export function parseCsProjData(csProjData, projFile, pkgNameVersions = {}) {
|
|
|
6282
6400
|
if (!csProjData) {
|
|
6283
6401
|
return pkgList;
|
|
6284
6402
|
}
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6403
|
+
let projects = undefined;
|
|
6404
|
+
try {
|
|
6405
|
+
projects = xml2js(csProjData, {
|
|
6406
|
+
compact: true,
|
|
6407
|
+
alwaysArray: true,
|
|
6408
|
+
spaces: 4,
|
|
6409
|
+
textKey: "_",
|
|
6410
|
+
attributesKey: "$",
|
|
6411
|
+
commentKey: "value",
|
|
6412
|
+
}).Project;
|
|
6413
|
+
} catch (e) {
|
|
6414
|
+
console.log(`Unable to parse ${projFile} with utf-8 encoding!`);
|
|
6415
|
+
}
|
|
6293
6416
|
if (!projects || projects.length === 0) {
|
|
6294
6417
|
return pkgList;
|
|
6295
6418
|
}
|
|
@@ -10222,9 +10345,7 @@ async function queryNuget(p, NUGET_URL) {
|
|
|
10222
10345
|
let version = `${tmpVersion.major}.${tmpVersion.minor}.${tmpVersion.patch}`;
|
|
10223
10346
|
if (compare(version, upper) === 1) {
|
|
10224
10347
|
if (tmpVersion.patch > 0) {
|
|
10225
|
-
version = `${tmpVersion.major}.${tmpVersion.minor}.${(
|
|
10226
|
-
tmpVersion.patch - 1
|
|
10227
|
-
).toString()}`;
|
|
10348
|
+
version = `${tmpVersion.major}.${tmpVersion.minor}.${(tmpVersion.patch - 1).toString()}`;
|
|
10228
10349
|
}
|
|
10229
10350
|
}
|
|
10230
10351
|
return version;
|
|
@@ -10540,3 +10661,27 @@ export function parseMakeDFile(dfile) {
|
|
|
10540
10661
|
pkgFilesMap[pkgName] = Array.from(filesList);
|
|
10541
10662
|
return pkgFilesMap;
|
|
10542
10663
|
}
|
|
10664
|
+
|
|
10665
|
+
/**
|
|
10666
|
+
* Function to validate an externalReference URL for conforming to the JSON schema or bomLink
|
|
10667
|
+
* https://github.com/CycloneDX/cyclonedx-core-java/blob/75575318b268dda9e2a290761d7db11b4f414255/src/main/resources/bom-1.5.schema.json#L1140
|
|
10668
|
+
* https://datatracker.ietf.org/doc/html/rfc3987#section-2.2
|
|
10669
|
+
* https://cyclonedx.org/capabilities/bomlink/
|
|
10670
|
+
*
|
|
10671
|
+
* @param {String} iri IRI to validate
|
|
10672
|
+
*
|
|
10673
|
+
* @returns {Boolean} Flag indicating whether the supplied URL is valid or not
|
|
10674
|
+
*
|
|
10675
|
+
*/
|
|
10676
|
+
export function isValidIriReference(iri) {
|
|
10677
|
+
const result = validateIri(iri, IriValidationStrategy.Strict);
|
|
10678
|
+
|
|
10679
|
+
if (result instanceof Error) {
|
|
10680
|
+
if (DEBUG_MODE) {
|
|
10681
|
+
console.log(`IRI failed validation ${iri}`);
|
|
10682
|
+
}
|
|
10683
|
+
return false;
|
|
10684
|
+
}
|
|
10685
|
+
|
|
10686
|
+
return true;
|
|
10687
|
+
}
|
package/utils.test.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getNugetMetadata,
|
|
14
14
|
getPyMetadata,
|
|
15
15
|
guessPypiMatchingVersion,
|
|
16
|
+
isValidIriReference,
|
|
16
17
|
parseBazelActionGraph,
|
|
17
18
|
parseBazelBuild,
|
|
18
19
|
parseBazelSkyframe,
|
|
@@ -525,10 +526,14 @@ test("parse maven tree", () => {
|
|
|
525
526
|
expect(parsedList.pkgList.length).toEqual(61);
|
|
526
527
|
expect(parsedList.dependenciesList.length).toEqual(61);
|
|
527
528
|
expect(parsedList.pkgList[0]).toEqual({
|
|
529
|
+
"bom-ref": "pkg:maven/com.pogeyan.cmis/copper-server@1.15.2?type=war",
|
|
528
530
|
group: "com.pogeyan.cmis",
|
|
529
531
|
name: "copper-server",
|
|
530
532
|
version: "1.15.2",
|
|
531
533
|
qualifiers: { type: "war" },
|
|
534
|
+
properties: [],
|
|
535
|
+
purl: "pkg:maven/com.pogeyan.cmis/copper-server@1.15.2?type=war",
|
|
536
|
+
scope: undefined,
|
|
532
537
|
});
|
|
533
538
|
expect(parsedList.dependenciesList[0]).toEqual({
|
|
534
539
|
ref: "pkg:maven/com.pogeyan.cmis/copper-server@1.15.2?type=war",
|
|
@@ -559,17 +564,24 @@ test("parse maven tree", () => {
|
|
|
559
564
|
encoding: "utf-8",
|
|
560
565
|
}),
|
|
561
566
|
);
|
|
562
|
-
expect(parsedList.pkgList.length).toEqual(
|
|
563
|
-
expect(parsedList.dependenciesList.length).toEqual(
|
|
567
|
+
expect(parsedList.pkgList.length).toEqual(39);
|
|
568
|
+
expect(parsedList.dependenciesList.length).toEqual(39);
|
|
564
569
|
expect(parsedList.pkgList[0]).toEqual({
|
|
570
|
+
"bom-ref":
|
|
571
|
+
"pkg:maven/com.gitlab.security_products.tests/java-maven@1.0-SNAPSHOT?type=jar",
|
|
572
|
+
purl: "pkg:maven/com.gitlab.security_products.tests/java-maven@1.0-SNAPSHOT?type=jar",
|
|
565
573
|
group: "com.gitlab.security_products.tests",
|
|
566
574
|
name: "java-maven",
|
|
567
575
|
version: "1.0-SNAPSHOT",
|
|
568
576
|
qualifiers: { type: "jar" },
|
|
577
|
+
properties: [],
|
|
578
|
+
scope: undefined,
|
|
569
579
|
});
|
|
570
580
|
expect(parsedList.dependenciesList[0]).toEqual({
|
|
571
581
|
ref: "pkg:maven/com.gitlab.security_products.tests/java-maven@1.0-SNAPSHOT?type=jar",
|
|
572
582
|
dependsOn: [
|
|
583
|
+
"pkg:maven/com.github.jnr/jffi@1.3.11?type=jar",
|
|
584
|
+
"pkg:maven/com.github.jnr/jffi@1.3.11?classifier=native&type=jar",
|
|
573
585
|
"pkg:maven/org.powermock/powermock-api-mockito@1.7.3?type=jar",
|
|
574
586
|
"pkg:maven/io.netty/netty@3.9.1.Final?type=jar",
|
|
575
587
|
"pkg:maven/junit/junit@3.8.1?type=jar",
|
|
@@ -586,16 +598,26 @@ test("parse maven tree", () => {
|
|
|
586
598
|
);
|
|
587
599
|
expect(parsedList.pkgList.length).toEqual(79);
|
|
588
600
|
expect(parsedList.pkgList[0]).toEqual({
|
|
601
|
+
"bom-ref":
|
|
602
|
+
"pkg:maven/example.group/eclipse-repository@1.0.0-SNAPSHOT?type=eclipse-repository",
|
|
603
|
+
purl: "pkg:maven/example.group/eclipse-repository@1.0.0-SNAPSHOT?type=eclipse-repository",
|
|
589
604
|
group: "example.group",
|
|
590
605
|
name: "eclipse-repository",
|
|
591
606
|
version: "1.0.0-SNAPSHOT",
|
|
592
607
|
qualifiers: { type: "eclipse-repository" },
|
|
608
|
+
scope: undefined,
|
|
609
|
+
properties: [],
|
|
593
610
|
});
|
|
594
611
|
expect(parsedList.pkgList[4]).toEqual({
|
|
612
|
+
"bom-ref":
|
|
613
|
+
"pkg:maven/p2.eclipse.plugin/com.ibm.icu@67.1.0.v20200706-1749?type=eclipse-plugin",
|
|
614
|
+
purl: "pkg:maven/p2.eclipse.plugin/com.ibm.icu@67.1.0.v20200706-1749?type=eclipse-plugin",
|
|
595
615
|
group: "p2.eclipse.plugin",
|
|
596
616
|
name: "com.ibm.icu",
|
|
597
617
|
version: "67.1.0.v20200706-1749",
|
|
598
618
|
qualifiers: { type: "eclipse-plugin" },
|
|
619
|
+
scope: undefined,
|
|
620
|
+
properties: [],
|
|
599
621
|
});
|
|
600
622
|
expect(parsedList.dependenciesList.length).toEqual(79);
|
|
601
623
|
expect(parsedList.dependenciesList[0]).toEqual({
|
|
@@ -2369,14 +2391,14 @@ test("parsePkgLock v2 workspace", async () => {
|
|
|
2369
2391
|
});
|
|
2370
2392
|
|
|
2371
2393
|
test("parsePkgLock v3", async () => {
|
|
2372
|
-
|
|
2394
|
+
const parsedList = await parsePkgLock(
|
|
2373
2395
|
"./test/data/package-json/v3/package-lock.json",
|
|
2374
2396
|
{
|
|
2375
2397
|
projectVersion: "latest",
|
|
2376
2398
|
projectName: "cdxgen",
|
|
2377
2399
|
},
|
|
2378
2400
|
);
|
|
2379
|
-
|
|
2401
|
+
const deps = parsedList.pkgList;
|
|
2380
2402
|
expect(deps.length).toEqual(161);
|
|
2381
2403
|
expect(deps[1]._integrity).toEqual(
|
|
2382
2404
|
"sha512-s93jiP6GkRApn5duComx6RLwtP23YrulPxShz+8peX7svd6Q+MS8nKLhKCCazbP92C13eTVaIOxgeLt0ezIiCg==",
|
|
@@ -2393,13 +2415,6 @@ test("parsePkgLock v3", async () => {
|
|
|
2393
2415
|
});
|
|
2394
2416
|
expect(deps[deps.length - 1].name).toEqual("uid2");
|
|
2395
2417
|
expect(parsedList.dependenciesList.length).toEqual(161);
|
|
2396
|
-
parsedList = await parsePkgLock("./package-lock.json", {
|
|
2397
|
-
projectVersion: "latest",
|
|
2398
|
-
projectName: "cdxgen",
|
|
2399
|
-
});
|
|
2400
|
-
deps = parsedList.pkgList;
|
|
2401
|
-
expect(deps.length).toEqual(848);
|
|
2402
|
-
expect(parsedList.dependenciesList.length).toEqual(848);
|
|
2403
2418
|
});
|
|
2404
2419
|
|
|
2405
2420
|
test("parseBowerJson", async () => {
|
|
@@ -2664,6 +2679,39 @@ test("parsePnpmLock", async () => {
|
|
|
2664
2679
|
},
|
|
2665
2680
|
},
|
|
2666
2681
|
});
|
|
2682
|
+
parsedList = await parsePnpmLock("./pnpm-lock.yaml");
|
|
2683
|
+
expect(parsedList.pkgList.length).toEqual(644);
|
|
2684
|
+
expect(parsedList.dependenciesList.length).toEqual(644);
|
|
2685
|
+
expect(parsedList.pkgList[0]).toEqual({
|
|
2686
|
+
group: "@ampproject",
|
|
2687
|
+
name: "remapping",
|
|
2688
|
+
version: "2.3.0",
|
|
2689
|
+
purl: "pkg:npm/%40ampproject/remapping@2.3.0",
|
|
2690
|
+
"bom-ref": "pkg:npm/@ampproject/remapping@2.3.0",
|
|
2691
|
+
_integrity:
|
|
2692
|
+
"sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
|
2693
|
+
properties: [{ name: "SrcFile", value: "./pnpm-lock.yaml" }],
|
|
2694
|
+
evidence: {
|
|
2695
|
+
identity: {
|
|
2696
|
+
field: "purl",
|
|
2697
|
+
confidence: 1,
|
|
2698
|
+
methods: [
|
|
2699
|
+
{
|
|
2700
|
+
technique: "manifest-analysis",
|
|
2701
|
+
confidence: 1,
|
|
2702
|
+
value: "./pnpm-lock.yaml",
|
|
2703
|
+
},
|
|
2704
|
+
],
|
|
2705
|
+
},
|
|
2706
|
+
},
|
|
2707
|
+
});
|
|
2708
|
+
expect(parsedList.dependenciesList[0]).toEqual({
|
|
2709
|
+
ref: "pkg:npm/@ampproject/remapping@2.3.0",
|
|
2710
|
+
dependsOn: [
|
|
2711
|
+
"pkg:npm/@jridgewell/gen-mapping@0.3.5",
|
|
2712
|
+
"pkg:npm/@jridgewell/trace-mapping@0.3.25",
|
|
2713
|
+
],
|
|
2714
|
+
});
|
|
2667
2715
|
});
|
|
2668
2716
|
|
|
2669
2717
|
test("parseYarnLock", async () => {
|
|
@@ -4040,3 +4088,20 @@ test("parseMakeDFile tests", () => {
|
|
|
4040
4088
|
],
|
|
4041
4089
|
});
|
|
4042
4090
|
});
|
|
4091
|
+
|
|
4092
|
+
test.each([
|
|
4093
|
+
["", false],
|
|
4094
|
+
["git@gitlab.com:behat-chrome/chrome-mink-driver.git", false],
|
|
4095
|
+
[" git@gitlab.com:behat-chrome/chrome-mink-driver.git ", false],
|
|
4096
|
+
["${repository.url}", false],
|
|
4097
|
+
// bomLink - https://cyclonedx.org/capabilities/bomlink/]
|
|
4098
|
+
["urn:cdx:f08a6ccd-4dce-4759-bd84-c626675d60a7/1#componentA", true],
|
|
4099
|
+
// http uri - https://www.ietf.org/rfc/rfc7230.txt]
|
|
4100
|
+
["https://gitlab.com/behat-chrome/chrome-mink-driver.git", true],
|
|
4101
|
+
[" https://gitlab.com/behat-chrome/chrome-mink-driver.git ", false],
|
|
4102
|
+
["http://gitlab.com/behat-chrome/chrome-mink-driver.git", true],
|
|
4103
|
+
["git+https://github.com/Alex-D/check-disk-space.git", true],
|
|
4104
|
+
["UNKNOWN", false],
|
|
4105
|
+
])("isValidIriReference tests: %s", (url, isValid) => {
|
|
4106
|
+
expect(isValidIriReference(url)).toBe(isValid);
|
|
4107
|
+
});
|