@cyclonedx/cdxgen 9.9.8 → 9.10.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/README.md +4 -3
- package/evinser.js +17 -5
- package/index.js +14 -10
- package/package.json +3 -3
- package/utils.js +203 -152
- package/utils.test.js +30 -24
package/README.md
CHANGED
|
@@ -229,14 +229,14 @@ To generate SBOM for an older specification version, such as 1.4, pass the versi
|
|
|
229
229
|
cdxgen -r -o bom.json --spec-version 1.4
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
-
To generate SBOM for C or Python, ensure Java >=
|
|
232
|
+
To generate SBOM for C or Python, ensure Java >= 21 is installed.
|
|
233
233
|
|
|
234
234
|
```shell
|
|
235
|
-
# Install java >=
|
|
235
|
+
# Install java >= 21
|
|
236
236
|
cdxgen -t c -o bom.json
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
-
NOTE: cdxgen is known to freeze with Java 8 or 11, so ensure >=
|
|
239
|
+
NOTE: cdxgen is known to freeze with Java 8 or 11, so ensure >= 21 is installed and JAVA_HOME environment variable is configured correctly. If in doubt, use the cdxgen container image.
|
|
240
240
|
|
|
241
241
|
## Universal SBOM
|
|
242
242
|
|
|
@@ -371,6 +371,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
|
|
|
371
371
|
| GRADLE_DEPENDENCY_TASK | By default cdxgen use the task "dependencies" to collect packages. Set to override the task name. |
|
|
372
372
|
| SBT_CACHE_DIR | Specify sbt cache directory. Useful for class name resolving |
|
|
373
373
|
| FETCH_LICENSE | Set this variable to `true` or `1` to fetch license information from the registry. npm and golang |
|
|
374
|
+
| SEARCH_MAVEN_ORG | If maven metadata is missing in jar file, a search is performed on search.maven.org. Set to `false` or `0` to disable search. |
|
|
374
375
|
| USE_GOSUM | Set to `true` or `1` to generate BOMs for golang projects using go.sum as the dependency source of truth, instead of go.mod |
|
|
375
376
|
| CDXGEN_TIMEOUT_MS | Default timeout for known execution involving maven, gradle or sbt |
|
|
376
377
|
| CDXGEN_SERVER_TIMEOUT_MS | Default timeout in server mode |
|
package/evinser.js
CHANGED
|
@@ -478,13 +478,15 @@ export const parseSliceUsages = async (
|
|
|
478
478
|
purlLocationMap,
|
|
479
479
|
purlImportsMap
|
|
480
480
|
) => {
|
|
481
|
-
const usages = slice.usages;
|
|
482
|
-
if (!usages || !usages.length) {
|
|
483
|
-
return undefined;
|
|
484
|
-
}
|
|
485
481
|
const fileName = slice.fileName;
|
|
486
482
|
const typesToLookup = new Set();
|
|
487
483
|
const lKeyOverrides = {};
|
|
484
|
+
const usages = slice.usages || [];
|
|
485
|
+
// Annotations from usages
|
|
486
|
+
if (slice.signature && slice.signature.startsWith("@") && !usages.length) {
|
|
487
|
+
typesToLookup.add(slice.fullName);
|
|
488
|
+
addToOverrides(lKeyOverrides, slice.fullName, fileName, slice.lineNumber);
|
|
489
|
+
}
|
|
488
490
|
for (const ausage of usages) {
|
|
489
491
|
const ausageLine =
|
|
490
492
|
ausage?.targetObj?.lineNumber || ausage?.definedBy?.lineNumber;
|
|
@@ -527,7 +529,17 @@ export const parseSliceUsages = async (
|
|
|
527
529
|
.concat(ausage?.invokedCalls || [])
|
|
528
530
|
.concat(ausage?.argToCalls || [])
|
|
529
531
|
.concat(ausage?.procedures || [])) {
|
|
530
|
-
if (acall.
|
|
532
|
+
if (acall.resolvedMethod && acall.resolvedMethod.startsWith("@")) {
|
|
533
|
+
typesToLookup.add(acall.callName);
|
|
534
|
+
if (acall.lineNumber) {
|
|
535
|
+
addToOverrides(
|
|
536
|
+
lKeyOverrides,
|
|
537
|
+
acall.callName,
|
|
538
|
+
fileName,
|
|
539
|
+
acall.lineNumber
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
} else if (acall.isExternal == false) {
|
|
531
543
|
continue;
|
|
532
544
|
}
|
|
533
545
|
if (
|
package/index.js
CHANGED
|
@@ -108,7 +108,8 @@ import {
|
|
|
108
108
|
parseContainerFile,
|
|
109
109
|
parseBitbucketPipelinesFile,
|
|
110
110
|
getPyMetadata,
|
|
111
|
-
addEvidenceForDotnet
|
|
111
|
+
addEvidenceForDotnet,
|
|
112
|
+
getSwiftPackageMetadata
|
|
112
113
|
} from "./utils.js";
|
|
113
114
|
import { spawnSync } from "node:child_process";
|
|
114
115
|
import { fileURLToPath } from "node:url";
|
|
@@ -1085,7 +1086,7 @@ export const createJarBom = async (path, options) => {
|
|
|
1085
1086
|
if (DEBUG_MODE) {
|
|
1086
1087
|
console.log(`Parsing ${jar}`);
|
|
1087
1088
|
}
|
|
1088
|
-
const dlist = extractJarArchive(jar, tempDir);
|
|
1089
|
+
const dlist = await extractJarArchive(jar, tempDir);
|
|
1089
1090
|
if (dlist && dlist.length) {
|
|
1090
1091
|
pkgList = pkgList.concat(dlist);
|
|
1091
1092
|
}
|
|
@@ -1127,7 +1128,7 @@ export const createJavaBom = async (path, options) => {
|
|
|
1127
1128
|
}
|
|
1128
1129
|
const tempDir = mkdtempSync(join(tmpdir(), "war-deps-"));
|
|
1129
1130
|
jarNSMapping = collectJarNS(tempDir);
|
|
1130
|
-
pkgList = extractJarArchive(path, tempDir, jarNSMapping);
|
|
1131
|
+
pkgList = await extractJarArchive(path, tempDir, jarNSMapping);
|
|
1131
1132
|
if (pkgList.length) {
|
|
1132
1133
|
pkgList = await getMvnMetadata(pkgList);
|
|
1133
1134
|
}
|
|
@@ -1266,7 +1267,7 @@ export const createJavaBom = async (path, options) => {
|
|
|
1266
1267
|
);
|
|
1267
1268
|
} else {
|
|
1268
1269
|
console.log(
|
|
1269
|
-
"1. Java version requirement: cdxgen container image bundles Java
|
|
1270
|
+
"1. Java version requirement: cdxgen container image bundles Java 21 with maven 3.9 which might be incompatible."
|
|
1270
1271
|
);
|
|
1271
1272
|
}
|
|
1272
1273
|
console.log(
|
|
@@ -3169,7 +3170,7 @@ export const createCppBom = (path, options) => {
|
|
|
3169
3170
|
}
|
|
3170
3171
|
}
|
|
3171
3172
|
}
|
|
3172
|
-
// The need for java >=
|
|
3173
|
+
// The need for java >= 21 with atom is causing confusions since there could be C projects
|
|
3173
3174
|
// inside of other project types. So we currently limit this analyis only when -t argument
|
|
3174
3175
|
// is used.
|
|
3175
3176
|
if (
|
|
@@ -3560,7 +3561,7 @@ export const createJenkinsBom = async (path, options) => {
|
|
|
3560
3561
|
if (DEBUG_MODE) {
|
|
3561
3562
|
console.log(`Parsing ${f}`);
|
|
3562
3563
|
}
|
|
3563
|
-
const dlist = extractJarArchive(f, tempDir);
|
|
3564
|
+
const dlist = await extractJarArchive(f, tempDir);
|
|
3564
3565
|
if (dlist && dlist.length) {
|
|
3565
3566
|
pkgList = pkgList.concat(dlist);
|
|
3566
3567
|
}
|
|
@@ -3628,7 +3629,7 @@ export const createHelmBom = (path, options) => {
|
|
|
3628
3629
|
* @param path to the project
|
|
3629
3630
|
* @param options Parse options from the cli
|
|
3630
3631
|
*/
|
|
3631
|
-
export const createSwiftBom = (path, options) => {
|
|
3632
|
+
export const createSwiftBom = async (path, options) => {
|
|
3632
3633
|
const swiftFiles = getAllFiles(
|
|
3633
3634
|
path,
|
|
3634
3635
|
(options.multiProject ? "**/" : "") + "Package*.swift",
|
|
@@ -3704,6 +3705,9 @@ export const createSwiftBom = (path, options) => {
|
|
|
3704
3705
|
}
|
|
3705
3706
|
}
|
|
3706
3707
|
}
|
|
3708
|
+
if (FETCH_LICENSE) {
|
|
3709
|
+
pkgList = await getSwiftPackageMetadata(pkgList);
|
|
3710
|
+
}
|
|
3707
3711
|
return buildBomNSData(options, pkgList, "swift", {
|
|
3708
3712
|
src: path,
|
|
3709
3713
|
filename: swiftFiles.join(", "),
|
|
@@ -4899,7 +4903,7 @@ export const createMultiXBom = async (pathList, options) => {
|
|
|
4899
4903
|
)
|
|
4900
4904
|
);
|
|
4901
4905
|
}
|
|
4902
|
-
bomData = createSwiftBom(path, options);
|
|
4906
|
+
bomData = await createSwiftBom(path, options);
|
|
4903
4907
|
if (
|
|
4904
4908
|
bomData &&
|
|
4905
4909
|
bomData.bomJson &&
|
|
@@ -5329,7 +5333,7 @@ export const createXBom = async (path, options) => {
|
|
|
5329
5333
|
options
|
|
5330
5334
|
);
|
|
5331
5335
|
if (swiftFiles.length || pkgResolvedFiles.length) {
|
|
5332
|
-
return createSwiftBom(path, options);
|
|
5336
|
+
return await createSwiftBom(path, options);
|
|
5333
5337
|
}
|
|
5334
5338
|
};
|
|
5335
5339
|
|
|
@@ -5585,7 +5589,7 @@ export const createBom = async (path, options) => {
|
|
|
5585
5589
|
case "cloudbuild":
|
|
5586
5590
|
return createCloudBuildBom(path, options);
|
|
5587
5591
|
case "swift":
|
|
5588
|
-
return createSwiftBom(path, options);
|
|
5592
|
+
return await createSwiftBom(path, options);
|
|
5589
5593
|
default:
|
|
5590
5594
|
// In recurse mode return multi-language Bom
|
|
5591
5595
|
// https://github.com/cyclonedx/cdxgen/issues/95
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.10.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>",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"yargs": "^17.7.2"
|
|
84
84
|
},
|
|
85
85
|
"optionalDependencies": {
|
|
86
|
-
"@appthreat/atom": "1.7.
|
|
86
|
+
"@appthreat/atom": "1.7.5",
|
|
87
87
|
"@cyclonedx/cdxgen-plugins-bin": "^1.5.4",
|
|
88
88
|
"@cyclonedx/cdxgen-plugins-bin-windows-amd64": "^1.5.4",
|
|
89
89
|
"@cyclonedx/cdxgen-plugins-bin-arm64": "^1.5.4",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"devDependencies": {
|
|
104
104
|
"caxa": "^3.0.1",
|
|
105
105
|
"docsify-cli": "^4.4.4",
|
|
106
|
-
"eslint": "^8.
|
|
106
|
+
"eslint": "^8.56.0",
|
|
107
107
|
"eslint-config-prettier": "^9.1.0",
|
|
108
108
|
"eslint-plugin-prettier": "^5.0.1",
|
|
109
109
|
"jest": "^29.7.0",
|
package/utils.js
CHANGED
|
@@ -19,8 +19,10 @@ import {
|
|
|
19
19
|
readFileSync,
|
|
20
20
|
rmSync,
|
|
21
21
|
unlinkSync,
|
|
22
|
-
writeFileSync
|
|
22
|
+
writeFileSync,
|
|
23
|
+
createReadStream
|
|
23
24
|
} from "node:fs";
|
|
25
|
+
import { createHash } from "node:crypto";
|
|
24
26
|
import got from "got";
|
|
25
27
|
import Arborist from "@npmcli/arborist";
|
|
26
28
|
import path from "node:path";
|
|
@@ -120,6 +122,19 @@ export const FETCH_LICENSE =
|
|
|
120
122
|
process.env.FETCH_LICENSE &&
|
|
121
123
|
["true", "1"].includes(process.env.FETCH_LICENSE);
|
|
122
124
|
|
|
125
|
+
// Wether search.maven.org will be used to identify jars without maven metadata; default, if unset shall be 'true'
|
|
126
|
+
export const SEARCH_MAVEN_ORG =
|
|
127
|
+
!process.env.SEARCH_MAVEN_ORG ||
|
|
128
|
+
["true", "1"].includes(process.env.SEARCH_MAVEN_ORG);
|
|
129
|
+
|
|
130
|
+
// circuit breaker for search maven.org
|
|
131
|
+
let search_maven_org_errors = 0;
|
|
132
|
+
const MAX_SEARCH_MAVEN_ORG_ERRORS = 5;
|
|
133
|
+
|
|
134
|
+
// circuit breaker for get repo license
|
|
135
|
+
let get_repo_license_errors = 0;
|
|
136
|
+
const MAX_GET_REPO_LICENSE_ERRORS = 5;
|
|
137
|
+
|
|
123
138
|
const MAX_LICENSE_ID_LENGTH = 100;
|
|
124
139
|
|
|
125
140
|
let PYTHON_CMD = "python";
|
|
@@ -389,6 +404,34 @@ export function readLicenseText(
|
|
|
389
404
|
return null;
|
|
390
405
|
}
|
|
391
406
|
|
|
407
|
+
export const getSwiftPackageMetadata = async (pkgList) => {
|
|
408
|
+
const cdepList = [];
|
|
409
|
+
for (const p of pkgList) {
|
|
410
|
+
if (p.repository && p.repository.url) {
|
|
411
|
+
if (p.repository.url.includes("://github.com/")) {
|
|
412
|
+
try {
|
|
413
|
+
p.license = await getRepoLicense(p.repository.url, undefined);
|
|
414
|
+
} catch (e) {
|
|
415
|
+
console.error("error fetching repo license from", p.repository.url);
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
if (DEBUG_MODE) {
|
|
419
|
+
console.log(
|
|
420
|
+
p.repository.url,
|
|
421
|
+
"is currently not supported to fetch for licenses"
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} else {
|
|
426
|
+
if (DEBUG_MODE) {
|
|
427
|
+
console.warn("no repository url found for", p.name);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
cdepList.push(p);
|
|
431
|
+
}
|
|
432
|
+
return cdepList;
|
|
433
|
+
};
|
|
434
|
+
|
|
392
435
|
/**
|
|
393
436
|
* Method to retrieve metadata for npm packages by querying npmjs
|
|
394
437
|
*
|
|
@@ -2092,7 +2135,7 @@ export const executeGradleProperties = function (dir, rootPath, subProject) {
|
|
|
2092
2135
|
} else {
|
|
2093
2136
|
console.error(result.stdout, result.stderr);
|
|
2094
2137
|
console.log(
|
|
2095
|
-
"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
|
|
2138
|
+
"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 21 with gradle 8 which might be incompatible."
|
|
2096
2139
|
);
|
|
2097
2140
|
}
|
|
2098
2141
|
if (result.stderr.includes("not get unknown property")) {
|
|
@@ -3318,21 +3361,30 @@ export const repoMetadataToGitHubApiUrl = function (repoMetadata) {
|
|
|
3318
3361
|
};
|
|
3319
3362
|
|
|
3320
3363
|
/**
|
|
3321
|
-
* Method to
|
|
3364
|
+
* Method to split GitHub url into its parts
|
|
3322
3365
|
* @param {String} repoUrl Repository url
|
|
3323
|
-
* @
|
|
3324
|
-
* @return {String|undefined} github api url (or undefined - if not a GitHub repo)
|
|
3366
|
+
* @return {[String]} parts from url
|
|
3325
3367
|
*/
|
|
3326
|
-
export const
|
|
3327
|
-
if (!repoUrl || !repoUrl.includes("://github.com/")) {
|
|
3328
|
-
return repoMetadataToGitHubApiUrl(repoMetadata);
|
|
3329
|
-
}
|
|
3368
|
+
export const getGithubUrlParts = (repoUrl) => {
|
|
3330
3369
|
if (repoUrl.toLowerCase().endsWith(".git")) {
|
|
3331
3370
|
repoUrl = repoUrl.slice(0, -4);
|
|
3332
3371
|
}
|
|
3333
3372
|
repoUrl.replace(/\/$/, "");
|
|
3334
3373
|
const parts = repoUrl.split("/");
|
|
3374
|
+
return parts;
|
|
3375
|
+
};
|
|
3335
3376
|
|
|
3377
|
+
/**
|
|
3378
|
+
* Method to construct GitHub api url from repo metadata or one of multiple formats of repo URLs
|
|
3379
|
+
* @param {String} repoUrl Repository url
|
|
3380
|
+
* @param {Object} repoMetadata Object containing group and package name strings
|
|
3381
|
+
* @return {String|undefined} github api url (or undefined - if not a GitHub repo)
|
|
3382
|
+
*/
|
|
3383
|
+
export const toGitHubApiUrl = function (repoUrl, repoMetadata) {
|
|
3384
|
+
if (repoMetadata) {
|
|
3385
|
+
return repoMetadataToGitHubApiUrl(repoMetadata);
|
|
3386
|
+
}
|
|
3387
|
+
const parts = getGithubUrlParts(repoUrl);
|
|
3336
3388
|
if (parts.length < 5 || parts[2] !== "github.com") {
|
|
3337
3389
|
return undefined; // Not a valid GitHub repo URL
|
|
3338
3390
|
} else {
|
|
@@ -3353,7 +3405,7 @@ export const toGitHubApiUrl = function (repoUrl, repoMetadata) {
|
|
|
3353
3405
|
export const getRepoLicense = async function (repoUrl, repoMetadata) {
|
|
3354
3406
|
let apiUrl = toGitHubApiUrl(repoUrl, repoMetadata);
|
|
3355
3407
|
// Perform github lookups
|
|
3356
|
-
if (apiUrl) {
|
|
3408
|
+
if (apiUrl && get_repo_license_errors < MAX_GET_REPO_LICENSE_ERRORS) {
|
|
3357
3409
|
let licenseUrl = apiUrl + "/license";
|
|
3358
3410
|
const headers = {};
|
|
3359
3411
|
if (process.env.GITHUB_TOKEN) {
|
|
@@ -3399,8 +3451,10 @@ export const getRepoLicense = async function (repoUrl, repoMetadata) {
|
|
|
3399
3451
|
"Please ensure GITHUB_TOKEN is set as environment variable. " +
|
|
3400
3452
|
"See: https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api"
|
|
3401
3453
|
);
|
|
3454
|
+
get_repo_license_errors++;
|
|
3402
3455
|
} else if (!err.message.includes("404")) {
|
|
3403
3456
|
console.log(err);
|
|
3457
|
+
get_repo_license_errors++;
|
|
3404
3458
|
}
|
|
3405
3459
|
}
|
|
3406
3460
|
}
|
|
@@ -6105,87 +6159,89 @@ export const convertOSQueryResults = function (
|
|
|
6105
6159
|
return pkgList;
|
|
6106
6160
|
};
|
|
6107
6161
|
|
|
6108
|
-
|
|
6162
|
+
const purlFromUrlString = (type, repoUrl, version) => {
|
|
6163
|
+
let namespace = "",
|
|
6164
|
+
name;
|
|
6165
|
+
if (repoUrl && repoUrl.includes("://github.com/")) {
|
|
6166
|
+
const parts = getGithubUrlParts(repoUrl);
|
|
6167
|
+
if (parts.length < 5 || parts[2] !== "github.com") {
|
|
6168
|
+
return undefined; // Not a valid GitHub repo URL
|
|
6169
|
+
} else {
|
|
6170
|
+
namespace = parts[2] + "/" + parts[3];
|
|
6171
|
+
name = parts[4];
|
|
6172
|
+
}
|
|
6173
|
+
} else if (repoUrl && repoUrl.startsWith("/")) {
|
|
6174
|
+
const parts = repoUrl.split("/");
|
|
6175
|
+
name = parts[parts.length - 1];
|
|
6176
|
+
} else {
|
|
6177
|
+
if (DEBUG_MODE) {
|
|
6178
|
+
console.warn("unsupported repo url for swift type");
|
|
6179
|
+
}
|
|
6180
|
+
return undefined;
|
|
6181
|
+
}
|
|
6182
|
+
|
|
6183
|
+
const purl = new PackageURL(type, namespace, name, version, null, null);
|
|
6184
|
+
return purl;
|
|
6185
|
+
};
|
|
6186
|
+
|
|
6187
|
+
/**
|
|
6188
|
+
* Parse swift dependency tree output json object
|
|
6189
|
+
* @param {string} jsonObject Swift dependencies json object
|
|
6190
|
+
* @param {string} pkgFile Package.swift file
|
|
6191
|
+
*/
|
|
6192
|
+
export const parseSwiftJsonTreeObject = (
|
|
6109
6193
|
pkgList,
|
|
6110
6194
|
dependenciesList,
|
|
6111
|
-
|
|
6112
|
-
|
|
6195
|
+
jsonObject,
|
|
6196
|
+
pkgFile
|
|
6113
6197
|
) => {
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
}
|
|
6140
|
-
];
|
|
6141
|
-
}
|
|
6142
|
-
} else {
|
|
6143
|
-
apkg.properties = [
|
|
6144
|
-
{
|
|
6145
|
-
name: "SrcPath",
|
|
6146
|
-
value: urlOrPath
|
|
6147
|
-
}
|
|
6148
|
-
];
|
|
6149
|
-
}
|
|
6150
|
-
}
|
|
6151
|
-
pkgList.push(apkg);
|
|
6152
|
-
// Handle the immediate dependencies before recursing
|
|
6153
|
-
if (adep.dependencies && adep.dependencies.length) {
|
|
6154
|
-
const deplist = [];
|
|
6155
|
-
for (const cdep of adep.dependencies) {
|
|
6156
|
-
const deppurl = new PackageURL(
|
|
6157
|
-
"swift",
|
|
6158
|
-
cdep.identity || "",
|
|
6159
|
-
cdep.name,
|
|
6160
|
-
cdep.version,
|
|
6161
|
-
null,
|
|
6162
|
-
null
|
|
6163
|
-
);
|
|
6164
|
-
const deppurlString = decodeURIComponent(deppurl.toString());
|
|
6165
|
-
deplist.push(deppurlString);
|
|
6166
|
-
}
|
|
6167
|
-
if (!depKeys[purlString]) {
|
|
6168
|
-
dependenciesList.push({
|
|
6169
|
-
ref: purlString,
|
|
6170
|
-
dependsOn: deplist
|
|
6171
|
-
});
|
|
6172
|
-
depKeys[purlString] = true;
|
|
6173
|
-
}
|
|
6174
|
-
if (adep.dependencies && adep.dependencies.length) {
|
|
6175
|
-
_swiftDepPkgList(pkgList, dependenciesList, depKeys, adep);
|
|
6176
|
-
}
|
|
6177
|
-
} else {
|
|
6178
|
-
if (!depKeys[purlString]) {
|
|
6179
|
-
dependenciesList.push({
|
|
6180
|
-
ref: purlString,
|
|
6181
|
-
dependsOn: []
|
|
6182
|
-
});
|
|
6183
|
-
depKeys[purlString] = true;
|
|
6184
|
-
}
|
|
6198
|
+
const urlOrPath = jsonObject.url || jsonObject.path;
|
|
6199
|
+
const version = jsonObject.version;
|
|
6200
|
+
const purl = purlFromUrlString("swift", urlOrPath, version);
|
|
6201
|
+
const purlString = decodeURIComponent(purl.toString());
|
|
6202
|
+
const rootPkg = {
|
|
6203
|
+
name: purl.name,
|
|
6204
|
+
group: purl.namespace,
|
|
6205
|
+
version: purl.version,
|
|
6206
|
+
purl: purlString,
|
|
6207
|
+
"bom-ref": purlString
|
|
6208
|
+
};
|
|
6209
|
+
if (urlOrPath) {
|
|
6210
|
+
if (urlOrPath.startsWith("http")) {
|
|
6211
|
+
rootPkg.repository = { url: urlOrPath };
|
|
6212
|
+
} else {
|
|
6213
|
+
const properties = [];
|
|
6214
|
+
properties.push({
|
|
6215
|
+
name: "SrcPath",
|
|
6216
|
+
value: urlOrPath
|
|
6217
|
+
});
|
|
6218
|
+
if (pkgFile) {
|
|
6219
|
+
properties.push({
|
|
6220
|
+
name: "SrcFile",
|
|
6221
|
+
value: pkgFile
|
|
6222
|
+
});
|
|
6185
6223
|
}
|
|
6224
|
+
rootPkg.properties = properties;
|
|
6186
6225
|
}
|
|
6187
6226
|
}
|
|
6188
|
-
|
|
6227
|
+
pkgList.push(rootPkg);
|
|
6228
|
+
const depList = [];
|
|
6229
|
+
if (jsonObject.dependencies) {
|
|
6230
|
+
for (const dependency of jsonObject.dependencies) {
|
|
6231
|
+
const res = parseSwiftJsonTreeObject(
|
|
6232
|
+
pkgList,
|
|
6233
|
+
dependenciesList,
|
|
6234
|
+
dependency,
|
|
6235
|
+
pkgFile
|
|
6236
|
+
);
|
|
6237
|
+
depList.push(res);
|
|
6238
|
+
}
|
|
6239
|
+
}
|
|
6240
|
+
dependenciesList.push({
|
|
6241
|
+
ref: purlString,
|
|
6242
|
+
dependsOn: depList
|
|
6243
|
+
});
|
|
6244
|
+
return purlString;
|
|
6189
6245
|
};
|
|
6190
6246
|
|
|
6191
6247
|
/**
|
|
@@ -6199,64 +6255,9 @@ export const parseSwiftJsonTree = (rawOutput, pkgFile) => {
|
|
|
6199
6255
|
}
|
|
6200
6256
|
const pkgList = [];
|
|
6201
6257
|
const dependenciesList = [];
|
|
6202
|
-
const depKeys = {};
|
|
6203
|
-
let rootPkg = {};
|
|
6204
|
-
let jsonData = {};
|
|
6205
6258
|
try {
|
|
6206
|
-
jsonData = JSON.parse(rawOutput);
|
|
6207
|
-
|
|
6208
|
-
rootPkg = {
|
|
6209
|
-
group: jsonData.identity || "",
|
|
6210
|
-
name: jsonData.name,
|
|
6211
|
-
version: jsonData.version
|
|
6212
|
-
};
|
|
6213
|
-
const urlOrPath = jsonData.url || jsonData.path;
|
|
6214
|
-
if (urlOrPath) {
|
|
6215
|
-
if (urlOrPath.startsWith("http")) {
|
|
6216
|
-
rootPkg.repository = { url: urlOrPath };
|
|
6217
|
-
} else {
|
|
6218
|
-
rootPkg.properties = [
|
|
6219
|
-
{
|
|
6220
|
-
name: "SrcPath",
|
|
6221
|
-
value: urlOrPath
|
|
6222
|
-
},
|
|
6223
|
-
{
|
|
6224
|
-
name: "SrcFile",
|
|
6225
|
-
value: pkgFile
|
|
6226
|
-
}
|
|
6227
|
-
];
|
|
6228
|
-
}
|
|
6229
|
-
}
|
|
6230
|
-
const purl = new PackageURL(
|
|
6231
|
-
"swift",
|
|
6232
|
-
rootPkg.group,
|
|
6233
|
-
rootPkg.name,
|
|
6234
|
-
rootPkg.version,
|
|
6235
|
-
null,
|
|
6236
|
-
null
|
|
6237
|
-
);
|
|
6238
|
-
const bomRefString = decodeURIComponent(purl.toString());
|
|
6239
|
-
rootPkg["bom-ref"] = bomRefString;
|
|
6240
|
-
pkgList.push(rootPkg);
|
|
6241
|
-
const deplist = [];
|
|
6242
|
-
for (const rd of jsonData.dependencies) {
|
|
6243
|
-
const deppurl = new PackageURL(
|
|
6244
|
-
"swift",
|
|
6245
|
-
rd.identity || "",
|
|
6246
|
-
rd.name,
|
|
6247
|
-
rd.version,
|
|
6248
|
-
null,
|
|
6249
|
-
null
|
|
6250
|
-
);
|
|
6251
|
-
const deppurlString = decodeURIComponent(deppurl.toString());
|
|
6252
|
-
deplist.push(deppurlString);
|
|
6253
|
-
}
|
|
6254
|
-
dependenciesList.push({
|
|
6255
|
-
ref: bomRefString,
|
|
6256
|
-
dependsOn: deplist
|
|
6257
|
-
});
|
|
6258
|
-
_swiftDepPkgList(pkgList, dependenciesList, depKeys, jsonData);
|
|
6259
|
-
}
|
|
6259
|
+
const jsonData = JSON.parse(rawOutput);
|
|
6260
|
+
parseSwiftJsonTreeObject(pkgList, dependenciesList, jsonData, pkgFile);
|
|
6260
6261
|
} catch (e) {
|
|
6261
6262
|
if (DEBUG_MODE) {
|
|
6262
6263
|
console.log(e);
|
|
@@ -6287,10 +6288,16 @@ export const parseSwiftResolved = (resolvedFile) => {
|
|
|
6287
6288
|
resolvedList = pkgData.object.pins;
|
|
6288
6289
|
}
|
|
6289
6290
|
for (const adep of resolvedList) {
|
|
6290
|
-
const
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6291
|
+
const locationOrUrl = adep.location || adep.repositoryURL;
|
|
6292
|
+
const version = adep.state.version || adep.state.revision;
|
|
6293
|
+
const purl = purlFromUrlString("swift", locationOrUrl, version);
|
|
6294
|
+
const purlString = decodeURIComponent(purl.toString());
|
|
6295
|
+
const rootPkg = {
|
|
6296
|
+
name: purl.name,
|
|
6297
|
+
group: purl.namespace,
|
|
6298
|
+
version: purl.version,
|
|
6299
|
+
purl: purlString,
|
|
6300
|
+
"bom-ref": purlString,
|
|
6294
6301
|
properties: [
|
|
6295
6302
|
{
|
|
6296
6303
|
name: "SrcFile",
|
|
@@ -6311,11 +6318,10 @@ export const parseSwiftResolved = (resolvedFile) => {
|
|
|
6311
6318
|
}
|
|
6312
6319
|
}
|
|
6313
6320
|
};
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
apkg.repository = { url: repLocation };
|
|
6321
|
+
if (locationOrUrl) {
|
|
6322
|
+
rootPkg.repository = { url: locationOrUrl };
|
|
6317
6323
|
}
|
|
6318
|
-
pkgList.push(
|
|
6324
|
+
pkgList.push(rootPkg);
|
|
6319
6325
|
}
|
|
6320
6326
|
} catch (err) {
|
|
6321
6327
|
// continue regardless of error
|
|
@@ -6578,7 +6584,7 @@ export const collectJarNS = function (jarPath, pomPathMap = {}) {
|
|
|
6578
6584
|
) {
|
|
6579
6585
|
jarCommandAvailable = false;
|
|
6580
6586
|
console.log(
|
|
6581
|
-
"jar command is not available in PATH. Ensure JDK >=
|
|
6587
|
+
"jar command is not available in PATH. Ensure JDK >= 21 is installed and set the environment variables JAVA_HOME and PATH to the bin directory inside JAVA_HOME."
|
|
6582
6588
|
);
|
|
6583
6589
|
}
|
|
6584
6590
|
const consolelines = (jarResult.stdout || "").split("\n");
|
|
@@ -6775,6 +6781,22 @@ export const getPomPropertiesFromMavenDir = function (mavenDir) {
|
|
|
6775
6781
|
return pomProperties;
|
|
6776
6782
|
};
|
|
6777
6783
|
|
|
6784
|
+
/**
|
|
6785
|
+
*
|
|
6786
|
+
* @param {string} hashName name of hash algorithm
|
|
6787
|
+
* @param {string} path path to file
|
|
6788
|
+
* @returns {Promise<String>} hex value of hash
|
|
6789
|
+
*/
|
|
6790
|
+
async function checksumFile(hashName, path) {
|
|
6791
|
+
return new Promise((resolve, reject) => {
|
|
6792
|
+
const hash = createHash(hashName);
|
|
6793
|
+
const stream = createReadStream(path);
|
|
6794
|
+
stream.on("error", (err) => reject(err));
|
|
6795
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
6796
|
+
stream.on("end", () => resolve(hash.digest("hex")));
|
|
6797
|
+
});
|
|
6798
|
+
}
|
|
6799
|
+
|
|
6778
6800
|
/**
|
|
6779
6801
|
* Method to extract a war or ear file
|
|
6780
6802
|
*
|
|
@@ -6784,7 +6806,7 @@ export const getPomPropertiesFromMavenDir = function (mavenDir) {
|
|
|
6784
6806
|
*
|
|
6785
6807
|
* @return pkgList Package list
|
|
6786
6808
|
*/
|
|
6787
|
-
export const extractJarArchive = function (
|
|
6809
|
+
export const extractJarArchive = async function (
|
|
6788
6810
|
jarFile,
|
|
6789
6811
|
tempDir,
|
|
6790
6812
|
jarNSMapping = {}
|
|
@@ -6882,6 +6904,35 @@ export const extractJarArchive = function (
|
|
|
6882
6904
|
version = pomProperties["version"],
|
|
6883
6905
|
confidence = 1,
|
|
6884
6906
|
technique = "manifest-analysis";
|
|
6907
|
+
if (
|
|
6908
|
+
(!group || !name || !version) &&
|
|
6909
|
+
SEARCH_MAVEN_ORG &&
|
|
6910
|
+
search_maven_org_errors < MAX_SEARCH_MAVEN_ORG_ERRORS
|
|
6911
|
+
) {
|
|
6912
|
+
try {
|
|
6913
|
+
const sha = await checksumFile("sha1", jf);
|
|
6914
|
+
const searchurl =
|
|
6915
|
+
"https://search.maven.org/solrsearch/select?q=1:%22" +
|
|
6916
|
+
sha +
|
|
6917
|
+
"%22&rows=20&wt=json";
|
|
6918
|
+
const res = await cdxgenAgent.get(searchurl, {
|
|
6919
|
+
responseType: "json"
|
|
6920
|
+
});
|
|
6921
|
+
const data = res && res.body ? res.body["response"] : undefined;
|
|
6922
|
+
if (data && data["numFound"] == 1) {
|
|
6923
|
+
const jarInfo = data["docs"][0];
|
|
6924
|
+
group = jarInfo["g"];
|
|
6925
|
+
name = jarInfo["a"];
|
|
6926
|
+
version = jarInfo["v"];
|
|
6927
|
+
technique = "hash-comparison";
|
|
6928
|
+
}
|
|
6929
|
+
} catch (err) {
|
|
6930
|
+
if (err && err.message && !err.message.includes("404")) {
|
|
6931
|
+
console.log(err);
|
|
6932
|
+
search_maven_org_errors++;
|
|
6933
|
+
}
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6885
6936
|
if ((!group || !name || !version) && existsSync(manifestFile)) {
|
|
6886
6937
|
confidence = 0.8;
|
|
6887
6938
|
const jarMetadata = parseJarManifest(
|
package/utils.test.js
CHANGED
|
@@ -2919,28 +2919,29 @@ test("parse swift deps files", () => {
|
|
|
2919
2919
|
);
|
|
2920
2920
|
expect(retData.pkgList.length).toEqual(5);
|
|
2921
2921
|
expect(retData.pkgList[0]).toEqual({
|
|
2922
|
-
group: "swift-markdown",
|
|
2923
2922
|
name: "swift-markdown",
|
|
2923
|
+
group: "",
|
|
2924
|
+
purl: "pkg:swift/swift-markdown@unspecified",
|
|
2924
2925
|
version: "unspecified",
|
|
2925
2926
|
properties: [
|
|
2926
2927
|
{ name: "SrcPath", value: "/Volumes/Work/sandbox/swift-markdown" },
|
|
2927
2928
|
{ name: "SrcFile", value: "./test/data/swift-deps.json" }
|
|
2928
2929
|
],
|
|
2929
|
-
"bom-ref": "pkg:swift/swift-markdown
|
|
2930
|
+
"bom-ref": "pkg:swift/swift-markdown@unspecified"
|
|
2930
2931
|
});
|
|
2931
2932
|
expect(retData.dependenciesList.length).toEqual(5);
|
|
2932
2933
|
expect(retData.dependenciesList[0]).toEqual({
|
|
2933
|
-
ref: "pkg:swift/
|
|
2934
|
-
dependsOn: [
|
|
2935
|
-
"pkg:swift/swift-cmark/cmark-gfm@unspecified",
|
|
2936
|
-
"pkg:swift/swift-argument-parser/swift-argument-parser@1.0.3",
|
|
2937
|
-
"pkg:swift/swift-docc-plugin/SwiftDocCPlugin@1.1.0"
|
|
2938
|
-
]
|
|
2934
|
+
ref: "pkg:swift/github.com/apple/swift-cmark@unspecified",
|
|
2935
|
+
dependsOn: []
|
|
2939
2936
|
});
|
|
2940
2937
|
expect(retData.dependenciesList[retData.dependenciesList.length - 1]).toEqual(
|
|
2941
2938
|
{
|
|
2942
|
-
ref: "pkg:swift/swift-
|
|
2943
|
-
dependsOn: [
|
|
2939
|
+
ref: "pkg:swift/swift-markdown@unspecified",
|
|
2940
|
+
dependsOn: [
|
|
2941
|
+
"pkg:swift/github.com/apple/swift-cmark@unspecified",
|
|
2942
|
+
"pkg:swift/github.com/apple/swift-argument-parser@1.0.3",
|
|
2943
|
+
"pkg:swift/github.com/apple/swift-docc-plugin@1.1.0"
|
|
2944
|
+
]
|
|
2944
2945
|
}
|
|
2945
2946
|
);
|
|
2946
2947
|
retData = parseSwiftJsonTree(
|
|
@@ -2949,8 +2950,9 @@ test("parse swift deps files", () => {
|
|
|
2949
2950
|
);
|
|
2950
2951
|
expect(retData.pkgList.length).toEqual(5);
|
|
2951
2952
|
expect(retData.pkgList[0]).toEqual({
|
|
2952
|
-
group: "swift-certificates",
|
|
2953
2953
|
name: "swift-certificates",
|
|
2954
|
+
group: "",
|
|
2955
|
+
purl: "pkg:swift/swift-certificates@unspecified",
|
|
2954
2956
|
version: "unspecified",
|
|
2955
2957
|
properties: [
|
|
2956
2958
|
{
|
|
@@ -2959,36 +2961,37 @@ test("parse swift deps files", () => {
|
|
|
2959
2961
|
},
|
|
2960
2962
|
{ name: "SrcFile", value: "./test/data/swift-deps.json" }
|
|
2961
2963
|
],
|
|
2962
|
-
"bom-ref": "pkg:swift/swift-certificates
|
|
2964
|
+
"bom-ref": "pkg:swift/swift-certificates@unspecified"
|
|
2963
2965
|
});
|
|
2964
2966
|
expect(retData.dependenciesList).toEqual([
|
|
2965
2967
|
{
|
|
2966
|
-
ref: "pkg:swift/
|
|
2967
|
-
dependsOn: [
|
|
2968
|
+
ref: "pkg:swift/github.com/apple/swift-docc-symbolkit@1.0.0",
|
|
2969
|
+
dependsOn: []
|
|
2968
2970
|
},
|
|
2969
2971
|
{
|
|
2970
|
-
ref: "pkg:swift/
|
|
2971
|
-
dependsOn: ["pkg:swift/
|
|
2972
|
+
ref: "pkg:swift/github.com/apple/swift-docc-plugin@1.1.0",
|
|
2973
|
+
dependsOn: ["pkg:swift/github.com/apple/swift-docc-symbolkit@1.0.0"]
|
|
2972
2974
|
},
|
|
2973
2975
|
{
|
|
2974
|
-
ref: "pkg:swift/
|
|
2975
|
-
dependsOn: ["pkg:swift/swift-docc-plugin
|
|
2976
|
+
ref: "pkg:swift/github.com/apple/swift-asn1@0.7.0",
|
|
2977
|
+
dependsOn: ["pkg:swift/github.com/apple/swift-docc-plugin@1.1.0"]
|
|
2976
2978
|
},
|
|
2977
2979
|
{
|
|
2978
|
-
ref: "pkg:swift/swift-
|
|
2979
|
-
dependsOn: ["pkg:swift/swift-
|
|
2980
|
+
ref: "pkg:swift/github.com/apple/swift-crypto@2.4.0",
|
|
2981
|
+
dependsOn: ["pkg:swift/github.com/apple/swift-asn1@0.7.0"]
|
|
2980
2982
|
},
|
|
2981
2983
|
{
|
|
2982
|
-
ref: "pkg:swift/swift-
|
|
2983
|
-
dependsOn: []
|
|
2984
|
+
ref: "pkg:swift/swift-certificates@unspecified",
|
|
2985
|
+
dependsOn: ["pkg:swift/github.com/apple/swift-crypto@2.4.0"]
|
|
2984
2986
|
}
|
|
2985
2987
|
]);
|
|
2986
2988
|
let pkgList = parseSwiftResolved("./test/data/Package.resolved");
|
|
2987
2989
|
expect(pkgList.length).toEqual(4);
|
|
2988
2990
|
expect(pkgList[0]).toEqual({
|
|
2989
2991
|
name: "swift-argument-parser",
|
|
2990
|
-
group: "",
|
|
2992
|
+
group: "github.com/apple",
|
|
2991
2993
|
version: "1.0.3",
|
|
2994
|
+
purl: "pkg:swift/github.com/apple/swift-argument-parser@1.0.3",
|
|
2992
2995
|
properties: [{ name: "SrcFile", value: "./test/data/Package.resolved" }],
|
|
2993
2996
|
evidence: {
|
|
2994
2997
|
identity: {
|
|
@@ -3003,14 +3006,16 @@ test("parse swift deps files", () => {
|
|
|
3003
3006
|
]
|
|
3004
3007
|
}
|
|
3005
3008
|
},
|
|
3009
|
+
"bom-ref": "pkg:swift/github.com/apple/swift-argument-parser@1.0.3",
|
|
3006
3010
|
repository: { url: "https://github.com/apple/swift-argument-parser" }
|
|
3007
3011
|
});
|
|
3008
3012
|
pkgList = parseSwiftResolved("./test/data/Package2.resolved");
|
|
3009
3013
|
expect(pkgList.length).toEqual(4);
|
|
3010
3014
|
expect(pkgList[0]).toEqual({
|
|
3011
3015
|
name: "swift-argument-parser",
|
|
3012
|
-
group: "",
|
|
3016
|
+
group: "github.com/apple",
|
|
3013
3017
|
version: "1.2.2",
|
|
3018
|
+
purl: "pkg:swift/github.com/apple/swift-argument-parser@1.2.2",
|
|
3014
3019
|
properties: [{ name: "SrcFile", value: "./test/data/Package2.resolved" }],
|
|
3015
3020
|
evidence: {
|
|
3016
3021
|
identity: {
|
|
@@ -3025,6 +3030,7 @@ test("parse swift deps files", () => {
|
|
|
3025
3030
|
]
|
|
3026
3031
|
}
|
|
3027
3032
|
},
|
|
3033
|
+
"bom-ref": "pkg:swift/github.com/apple/swift-argument-parser@1.2.2",
|
|
3028
3034
|
repository: { url: "https://github.com/apple/swift-argument-parser.git" }
|
|
3029
3035
|
});
|
|
3030
3036
|
});
|