@cyclonedx/cdxgen 9.9.5 → 9.9.7
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/data/frameworks-list.json +20 -1
- package/data/known-licenses.json +78 -27
- package/docker.js +20 -4
- package/docker.test.js +30 -7
- package/evinser.js +35 -31
- package/index.js +50 -14
- package/package.json +7 -7
- package/server.js +2 -1
- package/utils.js +343 -119
- package/utils.test.js +36 -1
package/README.md
CHANGED
|
@@ -51,6 +51,7 @@ Most SBOM tools are like barcode scanners. They can scan a few package manifest
|
|
|
51
51
|
| Docker compose | docker-compose\*.yml. Images would also be scanned. | N/A | |
|
|
52
52
|
| Dockerfile | `*Dockerfile*` Images would also be scanned. | N/A | |
|
|
53
53
|
| Containerfile | `*Containerfile*`. Images would also be scanned. | N/A | |
|
|
54
|
+
| Bitbucket Pipelines | `bitbucket-pipelines.yml` images and pipes would also be scanned. | N/A | |
|
|
54
55
|
| Google CloudBuild configuration | cloudbuild.yaml | N/A | |
|
|
55
56
|
| OpenAPI | openapi\*.json, openapi\*.yaml | N/A | |
|
|
56
57
|
|
|
@@ -86,10 +87,10 @@ For go, `go mod why` command is used to identify required packages. For php, com
|
|
|
86
87
|
## Installing
|
|
87
88
|
|
|
88
89
|
```shell
|
|
89
|
-
|
|
90
|
+
npm install -g @cyclonedx/cdxgen
|
|
90
91
|
|
|
91
92
|
# For CycloneDX 1.4 compatibility use version 8.6.0 or pass the argument `--spec-version 1.4`
|
|
92
|
-
|
|
93
|
+
npm install -g @cyclonedx/cdxgen@8.6.0
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
If you are a [Homebrew](https://brew.sh/) user, you can also install [cdxgen](https://formulae.brew.sh/formula/cdxgen) via:
|
|
@@ -327,7 +328,7 @@ This would create a bom.json.map file with the jar - class name mapping. Refer t
|
|
|
327
328
|
|
|
328
329
|
## Resolving licenses
|
|
329
330
|
|
|
330
|
-
cdxgen can automatically query public registries such as maven, npm, or nuget to resolve the package licenses. This is a time-consuming operation and is disabled by default. To enable, set the environment variable `FETCH_LICENSE` to `true`, as shown.
|
|
331
|
+
cdxgen can automatically query public registries such as maven, npm, or nuget to resolve the package licenses. This is a time-consuming operation and is disabled by default. To enable, set the environment variable `FETCH_LICENSE` to `true`, as shown. Ensure that `GITHUB_TOKEN` is set or provided by [built-in GITHUB_TOKEN in GitHub Actions](https://docs.github.com/en/rest/overview/rate-limits-for-the-rest-api#primary-rate-limit-for-github_token-in-github-actions), otherwise rate limiting might prevent license resolving.
|
|
331
332
|
|
|
332
333
|
```bash
|
|
333
334
|
export FETCH_LICENSE=true
|
|
@@ -141,6 +141,25 @@
|
|
|
141
141
|
"pkg:cargo/nickel",
|
|
142
142
|
"pkg:cargo/yew",
|
|
143
143
|
"pkg:cargo/azul",
|
|
144
|
-
"pkg:cargo/conrod"
|
|
144
|
+
"pkg:cargo/conrod",
|
|
145
|
+
"pkg:generic/Aws",
|
|
146
|
+
"pkg:generic/Azure",
|
|
147
|
+
"pkg:generic/google",
|
|
148
|
+
"pkg:generic/CivetServer",
|
|
149
|
+
"pkg:generic/civetweb",
|
|
150
|
+
"pkg:generic/cpprest",
|
|
151
|
+
"pkg:generic/QCoreApplication",
|
|
152
|
+
"pkg:generic/drogon",
|
|
153
|
+
"pkg:generic/wfrest",
|
|
154
|
+
"pkg:generic/http",
|
|
155
|
+
"pkg:generic/fio",
|
|
156
|
+
"pkg:generic/onion",
|
|
157
|
+
"pkg:generic/lwan",
|
|
158
|
+
"pkg:generic/oatpp",
|
|
159
|
+
"pkg:generic/QDjango",
|
|
160
|
+
"pkg:generic/userver",
|
|
161
|
+
"pkg:generic/Wt/",
|
|
162
|
+
"pkg:generic/klone",
|
|
163
|
+
"pkg:generic/kcgi"
|
|
145
164
|
]
|
|
146
165
|
}
|
package/data/known-licenses.json
CHANGED
|
@@ -1,31 +1,82 @@
|
|
|
1
1
|
[
|
|
2
|
-
{ "license": "Apache-2.0", "group": "cloud.google.com", "name": "go" },
|
|
3
|
-
{ "license": "Apache-2.0", "group": "cloud.google.com/go", "name": "*" },
|
|
4
|
-
{ "license": "Apache-2.0", "group": "cuelang.org", "name": "go" },
|
|
5
|
-
{ "license": "MIT", "group": "pack.ag", "name": "amqp" },
|
|
6
|
-
{ "license": "Apache-2.0", "group": "google.golang.org", "name": "*" },
|
|
7
|
-
{ "license": "BSD-3-Clause", "group": "golang.org/x", "name": "*" },
|
|
8
2
|
{
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"name": "*"
|
|
3
|
+
"packageNamespace": "*",
|
|
4
|
+
"knownLicenses": [{ "license": "MIT", "urlIncludes": "mit-license" }]
|
|
12
5
|
},
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
6
|
+
{
|
|
7
|
+
"packageNamespace": "pkg:golang/",
|
|
8
|
+
"knownLicenses": [
|
|
9
|
+
{ "license": "Apache-2.0", "group": "cloud.google.com", "name": "go" },
|
|
10
|
+
{ "license": "Apache-2.0", "group": "cloud.google.com/go", "name": "*" },
|
|
11
|
+
{ "license": "Apache-2.0", "group": "cuelang.org", "name": "go" },
|
|
12
|
+
{ "license": "MIT", "group": "pack.ag", "name": "amqp" },
|
|
13
|
+
{ "license": "Apache-2.0", "group": "google.golang.org", "name": "*" },
|
|
14
|
+
{ "license": "BSD-3-Clause", "group": "golang.org/x", "name": "*" },
|
|
15
|
+
{
|
|
16
|
+
"license": "BSD-3-Clause",
|
|
17
|
+
"group": "dmitri.shuralyov.com/gpu",
|
|
18
|
+
"name": "*"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"license": "Apache-2.0",
|
|
22
|
+
"group": "contrib.go.opencensus.io",
|
|
23
|
+
"name": "*"
|
|
24
|
+
},
|
|
25
|
+
{ "license": "Apache-2.0", "group": "git.apache.org", "name": "*" },
|
|
26
|
+
{ "license": "Apache-2.0", "group": ".", "name": "go.opencensus.io" },
|
|
27
|
+
{ "license": "MIT", "group": "sigs.k8s.io", "name": "*" },
|
|
28
|
+
{ "license": "BSD-3-Clause", "group": "rsc.io", "name": "*" },
|
|
29
|
+
{ "license": "Apache-2.0", "group": "openpitrix.io", "name": "*" },
|
|
30
|
+
{ "license": "BSD-3-Clause", "group": "modernc.org", "name": "*" },
|
|
31
|
+
{ "license": "Apache-2.0", "group": "kubesphere.io", "name": "*" },
|
|
32
|
+
{ "license": "Apache-2.0", "group": "k8s.io", "name": "*" },
|
|
33
|
+
{ "license": "Apache-2.0", "group": "istio.io", "name": "*" },
|
|
34
|
+
{ "license": "MIT", "group": "honnef.co/go", "name": "*" },
|
|
35
|
+
{ "license": "Apache-2.0", "group": ".", "name": "gotest.tools" },
|
|
36
|
+
{ "license": "Apache-2.0", "group": "gopkg.in", "name": "*" },
|
|
37
|
+
{
|
|
38
|
+
"license": "Apache-2.0",
|
|
39
|
+
"group": "code.cloudfoundry.org",
|
|
40
|
+
"name": "*"
|
|
41
|
+
},
|
|
42
|
+
{ "license": "BSD-3-Clause", "group": "gonum.org/v1", "name": "*" },
|
|
43
|
+
{
|
|
44
|
+
"license": "Apache-2.0",
|
|
45
|
+
"group": "gomodules.xyz/jsonpatch",
|
|
46
|
+
"name": "*"
|
|
47
|
+
},
|
|
48
|
+
{ "license": "MIT", "group": "go.uber.org", "name": "*" },
|
|
49
|
+
{ "license": "MIT", "group": "go.etcd.io", "name": "*" }
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"packageNamespace": "pkg:nuget/",
|
|
54
|
+
"knownLicenses": [
|
|
55
|
+
{
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"urlIncludes": "//github.com/dotnet/standard/",
|
|
58
|
+
"licenseEvidence": "https://github.com/dotnet/standard/blob/release/3.0/LICENSE.TXT"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"urlIncludes": "//github.com/dotnet/corefx/",
|
|
63
|
+
"licenseEvidence": "https://github.com/dotnet/corefx/blob/release/2.0.0/LICENSE.TXT"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"license": "MIT",
|
|
67
|
+
"urlIncludes": "//github.com/dotnet/core-setup/",
|
|
68
|
+
"licenseEvidence": "https://github.com/dotnet/core-setup/blob/release/2.0.0/LICENSE.TXT"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"licenseName": ".NET Library License",
|
|
72
|
+
"urlEndswith": "?LinkId=329770",
|
|
73
|
+
"licenseEvidence": "https://go.microsoft.com/fwlink/?LinkId=329770"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"licenseName": ".NET Library License",
|
|
77
|
+
"urlEndswith": "dotnet_library_license.htm",
|
|
78
|
+
"licenseEvidence": "https://dotnet.microsoft.com/en-us/dotnet_library_license.htm"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
31
82
|
]
|
package/docker.js
CHANGED
|
@@ -416,11 +416,16 @@ export const parseImageName = (fullImageName) => {
|
|
|
416
416
|
repo: "",
|
|
417
417
|
tag: "",
|
|
418
418
|
digest: "",
|
|
419
|
-
platform: ""
|
|
419
|
+
platform: "",
|
|
420
|
+
group: "",
|
|
421
|
+
name: ""
|
|
420
422
|
};
|
|
421
423
|
if (!fullImageName) {
|
|
422
424
|
return nameObj;
|
|
423
425
|
}
|
|
426
|
+
// ensure it's lowercased
|
|
427
|
+
fullImageName = fullImageName.toLowerCase();
|
|
428
|
+
|
|
424
429
|
// Extract registry name
|
|
425
430
|
if (
|
|
426
431
|
fullImageName.includes("/") &&
|
|
@@ -437,6 +442,7 @@ export const parseImageName = (fullImageName) => {
|
|
|
437
442
|
fullImageName = fullImageName.replace(tmpA[0] + "/", "");
|
|
438
443
|
}
|
|
439
444
|
}
|
|
445
|
+
|
|
440
446
|
// Extract digest name
|
|
441
447
|
if (fullImageName.includes("@sha256:")) {
|
|
442
448
|
const tmpA = fullImageName.split("@sha256:");
|
|
@@ -445,6 +451,7 @@ export const parseImageName = (fullImageName) => {
|
|
|
445
451
|
fullImageName = fullImageName.replace("@sha256:" + nameObj.digest, "");
|
|
446
452
|
}
|
|
447
453
|
}
|
|
454
|
+
|
|
448
455
|
// Extract tag name
|
|
449
456
|
if (fullImageName.includes(":")) {
|
|
450
457
|
const tmpA = fullImageName.split(":");
|
|
@@ -453,11 +460,20 @@ export const parseImageName = (fullImageName) => {
|
|
|
453
460
|
fullImageName = fullImageName.replace(":" + nameObj.tag, "");
|
|
454
461
|
}
|
|
455
462
|
}
|
|
456
|
-
|
|
457
|
-
fullImageName = fullImageName.replace("library/", "");
|
|
458
|
-
}
|
|
463
|
+
|
|
459
464
|
// The left over string is the repo name
|
|
460
465
|
nameObj.repo = fullImageName;
|
|
466
|
+
nameObj.name = fullImageName;
|
|
467
|
+
|
|
468
|
+
// extract group name
|
|
469
|
+
if (fullImageName.includes("/")) {
|
|
470
|
+
const tmpA = fullImageName.split("/");
|
|
471
|
+
if (tmpA.length > 1) {
|
|
472
|
+
nameObj.name = tmpA[tmpA.length - 1];
|
|
473
|
+
nameObj.group = fullImageName.replace("/" + tmpA[tmpA.length - 1], "");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
461
477
|
return nameObj;
|
|
462
478
|
};
|
|
463
479
|
|
package/docker.test.js
CHANGED
|
@@ -24,35 +24,54 @@ test("parseImageName tests", () => {
|
|
|
24
24
|
repo: "debian",
|
|
25
25
|
tag: "",
|
|
26
26
|
digest: "",
|
|
27
|
-
platform: ""
|
|
27
|
+
platform: "",
|
|
28
|
+
group: "",
|
|
29
|
+
name: "debian"
|
|
28
30
|
});
|
|
29
31
|
expect(parseImageName("debian:latest")).toEqual({
|
|
30
32
|
registry: "",
|
|
31
33
|
repo: "debian",
|
|
32
34
|
tag: "latest",
|
|
33
35
|
digest: "",
|
|
34
|
-
platform: ""
|
|
36
|
+
platform: "",
|
|
37
|
+
group: "",
|
|
38
|
+
name: "debian"
|
|
39
|
+
});
|
|
40
|
+
expect(parseImageName("library/debian:latest")).toEqual({
|
|
41
|
+
registry: "",
|
|
42
|
+
repo: "library/debian",
|
|
43
|
+
tag: "latest",
|
|
44
|
+
digest: "",
|
|
45
|
+
platform: "",
|
|
46
|
+
group: "library",
|
|
47
|
+
name: "debian"
|
|
35
48
|
});
|
|
36
49
|
expect(parseImageName("shiftleft/scan:v1.15.6")).toEqual({
|
|
37
50
|
registry: "",
|
|
38
51
|
repo: "shiftleft/scan",
|
|
39
52
|
tag: "v1.15.6",
|
|
40
53
|
digest: "",
|
|
41
|
-
platform: ""
|
|
54
|
+
platform: "",
|
|
55
|
+
group: "shiftleft",
|
|
56
|
+
name: "scan"
|
|
42
57
|
});
|
|
43
58
|
expect(parseImageName("localhost:5000/shiftleft/scan:v1.15.6")).toEqual({
|
|
44
59
|
registry: "localhost:5000",
|
|
45
60
|
repo: "shiftleft/scan",
|
|
46
61
|
tag: "v1.15.6",
|
|
47
62
|
digest: "",
|
|
48
|
-
platform: ""
|
|
63
|
+
platform: "",
|
|
64
|
+
group: "shiftleft",
|
|
65
|
+
name: "scan"
|
|
49
66
|
});
|
|
50
67
|
expect(parseImageName("localhost:5000/shiftleft/scan")).toEqual({
|
|
51
68
|
registry: "localhost:5000",
|
|
52
69
|
repo: "shiftleft/scan",
|
|
53
70
|
tag: "",
|
|
54
71
|
digest: "",
|
|
55
|
-
platform: ""
|
|
72
|
+
platform: "",
|
|
73
|
+
group: "shiftleft",
|
|
74
|
+
name: "scan"
|
|
56
75
|
});
|
|
57
76
|
expect(
|
|
58
77
|
parseImageName("foocorp.jfrog.io/docker/library/eclipse-temurin:latest")
|
|
@@ -61,7 +80,9 @@ test("parseImageName tests", () => {
|
|
|
61
80
|
repo: "docker/library/eclipse-temurin",
|
|
62
81
|
tag: "latest",
|
|
63
82
|
digest: "",
|
|
64
|
-
platform: ""
|
|
83
|
+
platform: "",
|
|
84
|
+
group: "docker/library",
|
|
85
|
+
name: "eclipse-temurin"
|
|
65
86
|
});
|
|
66
87
|
expect(
|
|
67
88
|
parseImageName(
|
|
@@ -72,7 +93,9 @@ test("parseImageName tests", () => {
|
|
|
72
93
|
repo: "shiftleft/scan-java",
|
|
73
94
|
tag: "",
|
|
74
95
|
digest: "5d008306a7c5d09ba0161a3408fa3839dc2c9dd991ffb68adecc1040399fe9e1",
|
|
75
|
-
platform: ""
|
|
96
|
+
platform: "",
|
|
97
|
+
group: "shiftleft",
|
|
98
|
+
name: "scan-java"
|
|
76
99
|
});
|
|
77
100
|
}, 120000);
|
|
78
101
|
|
package/evinser.js
CHANGED
|
@@ -322,6 +322,29 @@ export const analyzeProject = async (dbObjMap, options) => {
|
|
|
322
322
|
// Load any existing purl-location information from the sbom.
|
|
323
323
|
// For eg: cdxgen populates this information for javascript projects
|
|
324
324
|
let { purlLocationMap, purlImportsMap } = initFromSbom(components);
|
|
325
|
+
// Do reachables first so that usages slicing can reuse the atom file
|
|
326
|
+
if (options.withReachables) {
|
|
327
|
+
if (
|
|
328
|
+
options.reachablesSlicesFile &&
|
|
329
|
+
fs.existsSync(options.reachablesSlicesFile)
|
|
330
|
+
) {
|
|
331
|
+
reachablesSlicesFile = options.reachablesSlicesFile;
|
|
332
|
+
reachablesSlice = JSON.parse(
|
|
333
|
+
fs.readFileSync(options.reachablesSlicesFile, "utf-8")
|
|
334
|
+
);
|
|
335
|
+
} else {
|
|
336
|
+
retMap = createSlice(language, dirPath, "reachables", options);
|
|
337
|
+
if (retMap && retMap.slicesFile && fs.existsSync(retMap.slicesFile)) {
|
|
338
|
+
reachablesSlicesFile = retMap.slicesFile;
|
|
339
|
+
reachablesSlice = JSON.parse(
|
|
340
|
+
fs.readFileSync(retMap.slicesFile, "utf-8")
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (reachablesSlice && Object.keys(reachablesSlice).length) {
|
|
346
|
+
dataFlowFrames = await collectReachableFrames(language, reachablesSlice);
|
|
347
|
+
}
|
|
325
348
|
// Reuse existing usages slices
|
|
326
349
|
if (options.usagesSlicesFile && fs.existsSync(options.usagesSlicesFile)) {
|
|
327
350
|
usageSlice = JSON.parse(fs.readFileSync(options.usagesSlicesFile, "utf-8"));
|
|
@@ -374,28 +397,6 @@ export const analyzeProject = async (dbObjMap, options) => {
|
|
|
374
397
|
purlImportsMap
|
|
375
398
|
);
|
|
376
399
|
}
|
|
377
|
-
if (options.withReachables) {
|
|
378
|
-
if (
|
|
379
|
-
options.reachablesSlicesFile &&
|
|
380
|
-
fs.existsSync(options.reachablesSlicesFile)
|
|
381
|
-
) {
|
|
382
|
-
reachablesSlicesFile = options.reachablesSlicesFile;
|
|
383
|
-
reachablesSlice = JSON.parse(
|
|
384
|
-
fs.readFileSync(options.reachablesSlicesFile, "utf-8")
|
|
385
|
-
);
|
|
386
|
-
} else {
|
|
387
|
-
retMap = createSlice(language, dirPath, "reachables", options);
|
|
388
|
-
if (retMap && retMap.slicesFile && fs.existsSync(retMap.slicesFile)) {
|
|
389
|
-
reachablesSlicesFile = retMap.slicesFile;
|
|
390
|
-
reachablesSlice = JSON.parse(
|
|
391
|
-
fs.readFileSync(retMap.slicesFile, "utf-8")
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
if (reachablesSlice && Object.keys(reachablesSlice).length) {
|
|
397
|
-
dataFlowFrames = await collectReachableFrames(language, reachablesSlice);
|
|
398
|
-
}
|
|
399
400
|
return {
|
|
400
401
|
atomFile: retMap.atomFile,
|
|
401
402
|
usagesSlicesFile,
|
|
@@ -776,15 +777,19 @@ export const detectServicesFromUDT = (
|
|
|
776
777
|
servicesMap
|
|
777
778
|
) => {
|
|
778
779
|
if (
|
|
779
|
-
["python", "py"].includes(language) &&
|
|
780
|
+
["python", "py", "c", "cpp", "c++"].includes(language) &&
|
|
780
781
|
userDefinedTypes &&
|
|
781
782
|
userDefinedTypes.length
|
|
782
783
|
) {
|
|
783
784
|
for (const audt of userDefinedTypes) {
|
|
784
785
|
if (
|
|
785
|
-
audt.name.includes("route") ||
|
|
786
|
-
audt.name.includes("path") ||
|
|
787
|
-
audt.name.includes("url")
|
|
786
|
+
audt.name.toLowerCase().includes("route") ||
|
|
787
|
+
audt.name.toLowerCase().includes("path") ||
|
|
788
|
+
audt.name.toLowerCase().includes("url") ||
|
|
789
|
+
audt.name.toLowerCase().includes("registerhandler") ||
|
|
790
|
+
audt.name.toLowerCase().includes("endpoint") ||
|
|
791
|
+
audt.name.toLowerCase().includes("api") ||
|
|
792
|
+
audt.name.toLowerCase().includes("add_method")
|
|
788
793
|
) {
|
|
789
794
|
const fields = audt.fields || [];
|
|
790
795
|
if (
|
|
@@ -875,14 +880,11 @@ export const extractEndpoints = (language, code) => {
|
|
|
875
880
|
);
|
|
876
881
|
}
|
|
877
882
|
break;
|
|
878
|
-
|
|
879
|
-
case "python":
|
|
883
|
+
default:
|
|
880
884
|
endpoints = (code.match(/['"](.*?)['"]/gi) || [])
|
|
881
885
|
.map((v) => v.replace(/["']/g, "").replace("\n", ""))
|
|
882
886
|
.filter((v) => v.length > 2);
|
|
883
887
|
break;
|
|
884
|
-
default:
|
|
885
|
-
break;
|
|
886
888
|
}
|
|
887
889
|
return endpoints;
|
|
888
890
|
};
|
|
@@ -910,6 +912,7 @@ export const createEvinseFile = (sliceArtefacts, options) => {
|
|
|
910
912
|
const components = bomJson.components || [];
|
|
911
913
|
let occEvidencePresent = false;
|
|
912
914
|
let csEvidencePresent = false;
|
|
915
|
+
let servicesPresent = false;
|
|
913
916
|
for (const comp of components) {
|
|
914
917
|
if (!comp.purl) {
|
|
915
918
|
continue;
|
|
@@ -957,6 +960,7 @@ export const createEvinseFile = (sliceArtefacts, options) => {
|
|
|
957
960
|
}
|
|
958
961
|
// Add to existing services
|
|
959
962
|
bomJson.services = (bomJson.services || []).concat(services);
|
|
963
|
+
servicesPresent = true;
|
|
960
964
|
}
|
|
961
965
|
if (options.annotate) {
|
|
962
966
|
if (!bomJson.annotations) {
|
|
@@ -993,7 +997,7 @@ export const createEvinseFile = (sliceArtefacts, options) => {
|
|
|
993
997
|
bomJson.metadata.timestamp = new Date().toISOString();
|
|
994
998
|
delete bomJson.signature;
|
|
995
999
|
fs.writeFileSync(evinseOutFile, JSON.stringify(bomJson, null, 2));
|
|
996
|
-
if (occEvidencePresent || csEvidencePresent) {
|
|
1000
|
+
if (occEvidencePresent || csEvidencePresent || servicesPresent) {
|
|
997
1001
|
console.log(evinseOutFile, "created successfully.");
|
|
998
1002
|
} else {
|
|
999
1003
|
console.log(
|
package/index.js
CHANGED
|
@@ -105,7 +105,9 @@ import {
|
|
|
105
105
|
MAX_BUFFER,
|
|
106
106
|
getNugetMetadata,
|
|
107
107
|
frameworksList,
|
|
108
|
-
parseContainerFile
|
|
108
|
+
parseContainerFile,
|
|
109
|
+
parseBitbucketPipelinesFile,
|
|
110
|
+
getPyMetadata
|
|
109
111
|
} from "./utils.js";
|
|
110
112
|
import { spawnSync } from "node:child_process";
|
|
111
113
|
import { fileURLToPath } from "node:url";
|
|
@@ -2574,6 +2576,9 @@ export const createPythonBom = async (path, options) => {
|
|
|
2574
2576
|
if (tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
|
|
2575
2577
|
rmSync(tempDir, { recursive: true, force: true });
|
|
2576
2578
|
}
|
|
2579
|
+
if (FETCH_LICENSE) {
|
|
2580
|
+
pkgList = await getPyMetadata(pkgList, false);
|
|
2581
|
+
}
|
|
2577
2582
|
return buildBomNSData(options, pkgList, "pypi", {
|
|
2578
2583
|
allImports,
|
|
2579
2584
|
src: path,
|
|
@@ -3148,6 +3153,18 @@ export const createCppBom = (path, options) => {
|
|
|
3148
3153
|
retMap.parentComponent.type = "library";
|
|
3149
3154
|
pkgList.push(retMap.parentComponent);
|
|
3150
3155
|
}
|
|
3156
|
+
// Retain the dependency tree from cmake
|
|
3157
|
+
if (retMap.dependenciesList) {
|
|
3158
|
+
if (dependencies.length) {
|
|
3159
|
+
dependencies = mergeDependencies(
|
|
3160
|
+
dependencies,
|
|
3161
|
+
retMap.dependenciesList,
|
|
3162
|
+
parentComponent
|
|
3163
|
+
);
|
|
3164
|
+
} else {
|
|
3165
|
+
dependencies = retMap.dependenciesList;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3151
3168
|
}
|
|
3152
3169
|
}
|
|
3153
3170
|
// The need for java >= 17 with atom is causing confusions since there could be C projects
|
|
@@ -3719,6 +3736,11 @@ export const createContainerSpecLikeBom = async (path, options) => {
|
|
|
3719
3736
|
(options.multiProject ? "**/" : "") + "*Dockerfile*",
|
|
3720
3737
|
options
|
|
3721
3738
|
);
|
|
3739
|
+
const bbPipelineFiles = getAllFiles(
|
|
3740
|
+
path,
|
|
3741
|
+
(options.multiProject ? "**/" : "") + "bitbucket-pipelines.yml",
|
|
3742
|
+
options
|
|
3743
|
+
);
|
|
3722
3744
|
const cfFiles = getAllFiles(
|
|
3723
3745
|
path,
|
|
3724
3746
|
(options.multiProject ? "**/" : "") + "*Containerfile*",
|
|
@@ -3747,27 +3769,35 @@ export const createContainerSpecLikeBom = async (path, options) => {
|
|
|
3747
3769
|
}
|
|
3748
3770
|
// Privado.ai json files
|
|
3749
3771
|
const privadoFiles = getAllFiles(path, ".privado/" + "*.json", options);
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3772
|
+
|
|
3773
|
+
// Parse yaml manifest files, dockerfiles, containerfiles or bitbucket pipeline files
|
|
3774
|
+
if (
|
|
3775
|
+
dcFiles.length ||
|
|
3776
|
+
dfFiles.length ||
|
|
3777
|
+
cfFiles.length ||
|
|
3778
|
+
bbPipelineFiles.length
|
|
3779
|
+
) {
|
|
3780
|
+
for (const f of [...dcFiles, ...dfFiles, ...cfFiles, ...bbPipelineFiles]) {
|
|
3753
3781
|
if (DEBUG_MODE) {
|
|
3754
3782
|
console.log(`Parsing ${f}`);
|
|
3755
3783
|
}
|
|
3756
3784
|
|
|
3757
3785
|
const dData = readFileSync(f, { encoding: "utf-8" });
|
|
3758
|
-
let
|
|
3786
|
+
let imgList = [];
|
|
3759
3787
|
// parse yaml manifest files
|
|
3760
|
-
if (f.endsWith(".yml")
|
|
3761
|
-
|
|
3788
|
+
if (f.endsWith("bitbucket-pipelines.yml")) {
|
|
3789
|
+
imgList = parseBitbucketPipelinesFile(dData);
|
|
3790
|
+
} else if (f.endsWith(".yml") || f.endsWith(".yaml")) {
|
|
3791
|
+
imgList = parseContainerSpecData(dData);
|
|
3762
3792
|
} else {
|
|
3763
|
-
|
|
3793
|
+
imgList = parseContainerFile(dData);
|
|
3764
3794
|
}
|
|
3765
3795
|
|
|
3766
|
-
if (
|
|
3796
|
+
if (imgList && imgList.length) {
|
|
3767
3797
|
if (DEBUG_MODE) {
|
|
3768
|
-
console.log("Images identified in", f, "are",
|
|
3798
|
+
console.log("Images identified in", f, "are", imgList);
|
|
3769
3799
|
}
|
|
3770
|
-
for (const img of
|
|
3800
|
+
for (const img of imgList) {
|
|
3771
3801
|
const commonProperties = [
|
|
3772
3802
|
{
|
|
3773
3803
|
name: "SrcFile",
|
|
@@ -3832,20 +3862,26 @@ export const createContainerSpecLikeBom = async (path, options) => {
|
|
|
3832
3862
|
console.log(`Parsing image ${img.image}`);
|
|
3833
3863
|
}
|
|
3834
3864
|
const imageObj = parseImageName(img.image);
|
|
3865
|
+
|
|
3835
3866
|
const pkg = {
|
|
3836
|
-
name: imageObj.
|
|
3867
|
+
name: imageObj.name,
|
|
3868
|
+
group: imageObj.group,
|
|
3837
3869
|
version:
|
|
3838
3870
|
imageObj.tag ||
|
|
3839
3871
|
(imageObj.digest ? "sha256:" + imageObj.digest : "latest"),
|
|
3840
3872
|
qualifiers: {},
|
|
3841
|
-
properties: commonProperties
|
|
3873
|
+
properties: commonProperties,
|
|
3874
|
+
type: "container"
|
|
3842
3875
|
};
|
|
3843
3876
|
if (imageObj.registry) {
|
|
3844
|
-
pkg["qualifiers"]["repository_url"] =
|
|
3877
|
+
pkg["qualifiers"]["repository_url"] = img.image;
|
|
3845
3878
|
}
|
|
3846
3879
|
if (imageObj.platform) {
|
|
3847
3880
|
pkg["qualifiers"]["platform"] = imageObj.platform;
|
|
3848
3881
|
}
|
|
3882
|
+
if (imageObj.tag) {
|
|
3883
|
+
pkg["qualifiers"]["tag"] = imageObj.tag;
|
|
3884
|
+
}
|
|
3849
3885
|
// Create an entry for the oci image
|
|
3850
3886
|
const imageBomData = buildBomNSData(options, [pkg], "oci", {
|
|
3851
3887
|
src: img.image,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "9.9.
|
|
3
|
+
"version": "9.9.7",
|
|
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>",
|
|
@@ -55,13 +55,13 @@
|
|
|
55
55
|
"url": "https://github.com/cyclonedx/cdxgen/issues"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@babel/parser": "^7.23.
|
|
59
|
-
"@babel/traverse": "^7.23.
|
|
58
|
+
"@babel/parser": "^7.23.5",
|
|
59
|
+
"@babel/traverse": "^7.23.5",
|
|
60
60
|
"@npmcli/arborist": "7.2.0",
|
|
61
61
|
"ajv": "^8.12.0",
|
|
62
62
|
"ajv-formats": "^2.1.1",
|
|
63
63
|
"cheerio": "^1.0.0-rc.12",
|
|
64
|
-
"edn-data": "
|
|
64
|
+
"edn-data": "1.1.1",
|
|
65
65
|
"find-up": "^6.3.0",
|
|
66
66
|
"glob": "^10.3.10",
|
|
67
67
|
"global-agent": "^3.0.0",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"yargs": "^17.7.2"
|
|
84
84
|
},
|
|
85
85
|
"optionalDependencies": {
|
|
86
|
-
"@appthreat/atom": "1.
|
|
86
|
+
"@appthreat/atom": "1.7.2",
|
|
87
87
|
"@cyclonedx/cdxgen-plugins-bin": "^1.4.0",
|
|
88
88
|
"@cyclonedx/cdxgen-plugins-bin-arm64": "^1.4.0",
|
|
89
89
|
"@cyclonedx/cdxgen-plugins-bin-ppc64": "^1.4.0",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"compression": "^1.7.4",
|
|
92
92
|
"connect": "^3.7.0",
|
|
93
93
|
"jsonata": "^2.0.3",
|
|
94
|
-
"sequelize": "^6.35.
|
|
94
|
+
"sequelize": "^6.35.1",
|
|
95
95
|
"sqlite3": "^5.1.6"
|
|
96
96
|
},
|
|
97
97
|
"files": [
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"devDependencies": {
|
|
103
103
|
"caxa": "^3.0.1",
|
|
104
104
|
"docsify-cli": "^4.4.4",
|
|
105
|
-
"eslint": "^8.
|
|
105
|
+
"eslint": "^8.54.0",
|
|
106
106
|
"eslint-config-prettier": "^9.0.0",
|
|
107
107
|
"eslint-plugin-prettier": "^5.0.1",
|
|
108
108
|
"jest": "^29.7.0",
|