@cyclonedx/cdxgen 11.3.2 → 11.4.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 +1 -1
- package/bin/cdxgen.js +38 -16
- package/bin/evinse.js +1 -25
- package/bin/verify.js +4 -1
- package/data/bom-1.6.schema.json +87 -65
- package/data/bom-1.7.schema.json +5915 -0
- package/data/component-tags.json +1 -1
- package/data/spdx-licenses.json +209 -4
- package/data/spdx.schema.json +29 -1
- package/lib/cli/index.js +35 -31
- package/lib/helpers/envcontext.js +15 -15
- package/lib/helpers/utils.js +79 -41
- package/lib/helpers/utils.test.js +9 -5
- package/lib/managers/binary.js +19 -9
- package/lib/managers/docker.js +13 -13
- package/lib/managers/oci.js +24 -20
- package/lib/managers/piptree.js +113 -20
- package/lib/server/openapi.yaml +21 -3
- package/lib/server/server.js +38 -38
- package/lib/stages/postgen/annotator.js +4 -0
- package/lib/stages/postgen/postgen.js +27 -6
- package/lib/stages/pregen/pregen.js +3 -3
- package/package.json +64 -23
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +10 -3
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +1 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/managers/piptree.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
package/lib/managers/piptree.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
/**
|
|
3
2
|
* The idea behind this plugin came from the excellent pipdeptree package
|
|
4
3
|
* https://github.com/tox-dev/pipdeptree
|
|
@@ -13,7 +12,7 @@ import {
|
|
|
13
12
|
writeFileSync,
|
|
14
13
|
} from "node:fs";
|
|
15
14
|
import { delimiter, join } from "node:path";
|
|
16
|
-
import { getTmpDir } from "../helpers/utils.js";
|
|
15
|
+
import { getTmpDir, safeSpawnSync } from "../helpers/utils.js";
|
|
17
16
|
|
|
18
17
|
const PIP_TREE_PLUGIN_CONTENT = `
|
|
19
18
|
import importlib.metadata as importlib_metadata
|
|
@@ -22,6 +21,16 @@ import sys
|
|
|
22
21
|
|
|
23
22
|
from pip._internal.metadata import pkg_resources
|
|
24
23
|
|
|
24
|
+
REQUIREMENT_MODULE_FOUND = False
|
|
25
|
+
try:
|
|
26
|
+
from packaging.requirements import Requirement
|
|
27
|
+
REQUIREMENT_MODULE_FOUND = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
try:
|
|
30
|
+
from pip._vendor.packaging.requirements import Requirement
|
|
31
|
+
REQUIREMENT_MODULE_FOUND = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
pass
|
|
25
34
|
|
|
26
35
|
def frozen_req_from_dist(dist):
|
|
27
36
|
try:
|
|
@@ -41,8 +50,8 @@ def frozen_req_from_dist(dist):
|
|
|
41
50
|
pass
|
|
42
51
|
|
|
43
52
|
|
|
44
|
-
def get_installed_distributions():
|
|
45
|
-
dists = pkg_resources.Environment.from_paths(
|
|
53
|
+
def get_installed_distributions(python_path=None):
|
|
54
|
+
dists = pkg_resources.Environment.from_paths(python_path).iter_installed_distributions(
|
|
46
55
|
local_only=False,
|
|
47
56
|
skip=(),
|
|
48
57
|
user_only=False,
|
|
@@ -50,7 +59,81 @@ def get_installed_distributions():
|
|
|
50
59
|
return [d._dist for d in dists]
|
|
51
60
|
|
|
52
61
|
|
|
53
|
-
def
|
|
62
|
+
def _get_extra_deps_from_dist(dist):
|
|
63
|
+
extra_deps = {}
|
|
64
|
+
if not dist:
|
|
65
|
+
return extra_deps
|
|
66
|
+
# all requirements, some of which may be extra-only:
|
|
67
|
+
reqs = dist.metadata.get_all('Requires-Dist') or []
|
|
68
|
+
# extras this package defines:
|
|
69
|
+
extras = dist.metadata.get_all('Provides-Extra') or []
|
|
70
|
+
for req_str in reqs:
|
|
71
|
+
req = Requirement(req_str)
|
|
72
|
+
if req.marker and 'extra' in str(req.marker):
|
|
73
|
+
# evaluate marker for each declared extra
|
|
74
|
+
for extra in extras:
|
|
75
|
+
if req.marker.evaluate({'extra': extra}):
|
|
76
|
+
extra_deps.setdefault(extra, []).append({"name": str(req.name), "versionSpecifiers": str(req.specifier), "url": str(req.url) if req.url else None})
|
|
77
|
+
return extra_deps
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _get_deps_from_extras(name_version_cache, name_dist_cache, extra_deps):
|
|
81
|
+
dependencies = []
|
|
82
|
+
if not extra_deps:
|
|
83
|
+
return dependencies
|
|
84
|
+
# Treat an extra with the name all as dependencies
|
|
85
|
+
all_deps = extra_deps.get("all", [])
|
|
86
|
+
for dep in all_deps:
|
|
87
|
+
dversion = name_version_cache.get(dep["name"])
|
|
88
|
+
if not dversion:
|
|
89
|
+
continue
|
|
90
|
+
dversionSpecifiers = dep.get("versionSpecifiers")
|
|
91
|
+
dpurl = f"""pkg:pypi/{dep["name"].lower()}@{dversion}"""
|
|
92
|
+
dextra_deps = _get_extra_deps_from_dist(name_dist_cache.get(dep["name"]))
|
|
93
|
+
ddependencies = _get_deps_from_extras(name_version_cache, name_dist_cache, dextra_deps)
|
|
94
|
+
dependencies.append({
|
|
95
|
+
"name": dep["name"],
|
|
96
|
+
"version": dversion,
|
|
97
|
+
"versionSpecifiers": dversionSpecifiers,
|
|
98
|
+
"purl": dpurl,
|
|
99
|
+
"extra_deps": dextra_deps,
|
|
100
|
+
"dependencies": ddependencies
|
|
101
|
+
})
|
|
102
|
+
return dependencies
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_installed_with_extras():
|
|
106
|
+
result = {}
|
|
107
|
+
if not REQUIREMENT_MODULE_FOUND:
|
|
108
|
+
return result
|
|
109
|
+
name_version_cache = {}
|
|
110
|
+
name_dist_cache = {}
|
|
111
|
+
for dist in importlib_metadata.distributions():
|
|
112
|
+
name = dist.metadata['Name']
|
|
113
|
+
version = dist.version or ""
|
|
114
|
+
name_version_cache[name] = version
|
|
115
|
+
name_dist_cache[name] = dist
|
|
116
|
+
for dist in importlib_metadata.distributions():
|
|
117
|
+
name = dist.metadata['Name']
|
|
118
|
+
version = dist.version or ""
|
|
119
|
+
# extras this package defines:
|
|
120
|
+
extras = dist.metadata.get_all('Provides-Extra') or []
|
|
121
|
+
# map each extra → its extra-only dependencies
|
|
122
|
+
extra_deps = _get_extra_deps_from_dist(dist)
|
|
123
|
+
purl = f"pkg:pypi/{name.lower()}@{version}"
|
|
124
|
+
dependencies = _get_deps_from_extras(name_version_cache, name_dist_cache, extra_deps)
|
|
125
|
+
result[purl] = {
|
|
126
|
+
'name': name,
|
|
127
|
+
'version': version,
|
|
128
|
+
'extras': extras,
|
|
129
|
+
'purl': purl,
|
|
130
|
+
'extra_deps': extra_deps,
|
|
131
|
+
"dependencies": dependencies
|
|
132
|
+
}
|
|
133
|
+
return result
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def find_deps(idx, path, purl, reqs, global_installed, traverse_count):
|
|
54
137
|
freqs = []
|
|
55
138
|
for r in reqs:
|
|
56
139
|
d = idx.get(r.key)
|
|
@@ -58,17 +141,26 @@ def find_deps(idx, path, reqs, traverse_count):
|
|
|
58
141
|
continue
|
|
59
142
|
r.project_name = d.project_name if d is not None else r.project_name
|
|
60
143
|
if r.key in path:
|
|
144
|
+
print(f"Cycle detected: {' -> '.join(current_path)}")
|
|
61
145
|
continue
|
|
62
146
|
current_path = path + [r.key]
|
|
63
147
|
specs = sorted(r.specs, reverse=True)
|
|
64
148
|
specs_str = ",".join(["".join(sp) for sp in specs]) if specs else ""
|
|
65
149
|
dreqs = d.requires()
|
|
150
|
+
name = r.project_name
|
|
151
|
+
version = importlib_metadata.version(r.key)
|
|
152
|
+
purl = f"pkg:pypi/{name.lower()}@{version}"
|
|
153
|
+
extra_deps = global_installed.get(purl, {}).get("extra_deps", {})
|
|
154
|
+
dependencies = find_deps(idx, current_path, purl, dreqs, global_installed, traverse_count + 1) if dreqs and traverse_count < 200 else []
|
|
155
|
+
all_dependencies = global_installed.get(purl, {}).get("dependencies", [])
|
|
66
156
|
freqs.append(
|
|
67
157
|
{
|
|
68
|
-
"name":
|
|
69
|
-
"version":
|
|
158
|
+
"name": name,
|
|
159
|
+
"version": version,
|
|
70
160
|
"versionSpecifiers": specs_str,
|
|
71
|
-
|
|
161
|
+
'purl': purl,
|
|
162
|
+
"extra_deps": extra_deps,
|
|
163
|
+
"dependencies": dependencies + all_dependencies,
|
|
72
164
|
}
|
|
73
165
|
)
|
|
74
166
|
return freqs
|
|
@@ -77,7 +169,8 @@ def find_deps(idx, path, reqs, traverse_count):
|
|
|
77
169
|
def main(argv):
|
|
78
170
|
out_file = "piptree.json" if len(argv) < 2 else argv[-1]
|
|
79
171
|
tree = []
|
|
80
|
-
|
|
172
|
+
global_installed = get_installed_with_extras()
|
|
173
|
+
pkgs = get_installed_distributions(python_path=None)
|
|
81
174
|
idx = {p.key: p for p in pkgs}
|
|
82
175
|
traverse_count = 0
|
|
83
176
|
for p in pkgs:
|
|
@@ -93,22 +186,22 @@ def main(argv):
|
|
|
93
186
|
version = "latest"
|
|
94
187
|
if len(tmpA) == 2:
|
|
95
188
|
version = tmpA[1]
|
|
189
|
+
pkgName = name.split(" ")[0]
|
|
190
|
+
purl = f"pkg:pypi/{pkgName.lower()}@{version}"
|
|
191
|
+
extra_deps = global_installed.get(purl, {}).get("extra_deps", "")
|
|
192
|
+
all_dependencies = global_installed.get(purl, {}).get("dependencies", [])
|
|
193
|
+
dependencies = find_deps(idx, [p.key], purl, p.requires(), global_installed, traverse_count + 1)
|
|
96
194
|
tree.append(
|
|
97
195
|
{
|
|
98
|
-
"name":
|
|
196
|
+
"name": pkgName,
|
|
99
197
|
"version": version,
|
|
100
|
-
"
|
|
198
|
+
"purl": purl,
|
|
199
|
+
"extra_deps": extra_deps,
|
|
200
|
+
"dependencies": dependencies + all_dependencies,
|
|
101
201
|
}
|
|
102
202
|
)
|
|
103
|
-
all_deps = {}
|
|
104
|
-
for t in tree:
|
|
105
|
-
for d in t["dependencies"]:
|
|
106
|
-
all_deps[d["name"]] = True
|
|
107
|
-
trimmed_tree = [
|
|
108
|
-
t for t in tree if t["name"] not in all_deps
|
|
109
|
-
]
|
|
110
203
|
with open(out_file, mode="w", encoding="utf-8") as fp:
|
|
111
|
-
json.dump(
|
|
204
|
+
json.dump(tree, fp)
|
|
112
205
|
|
|
113
206
|
|
|
114
207
|
if __name__ == "__main__":
|
|
@@ -141,7 +234,7 @@ export const getTreeWithPlugin = (env, python_cmd, basePath) => {
|
|
|
141
234
|
env.PYTHONPATH = `${env.PYTHONPATH}${delimiter}${env.PIP_TARGET}`;
|
|
142
235
|
}
|
|
143
236
|
}
|
|
144
|
-
const result =
|
|
237
|
+
const result = safeSpawnSync(python_cmd, pipPluginArgs, {
|
|
145
238
|
cwd: basePath,
|
|
146
239
|
encoding: "utf-8",
|
|
147
240
|
env,
|
package/lib/server/openapi.yaml
CHANGED
|
@@ -216,12 +216,22 @@ components:
|
|
|
216
216
|
type: object
|
|
217
217
|
properties:
|
|
218
218
|
type:
|
|
219
|
-
type:
|
|
220
|
-
|
|
219
|
+
type: array
|
|
220
|
+
items:
|
|
221
|
+
type: string
|
|
222
|
+
description: Project Types
|
|
221
223
|
default: "universal"
|
|
222
224
|
externalDocs:
|
|
223
225
|
description: Single or comma separated values. See supported project types
|
|
224
226
|
url: https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES
|
|
227
|
+
excludeType:
|
|
228
|
+
type: array
|
|
229
|
+
items:
|
|
230
|
+
type: string
|
|
231
|
+
description: Exclude Types
|
|
232
|
+
externalDocs:
|
|
233
|
+
description: Project types to exclude
|
|
234
|
+
url: https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES
|
|
225
235
|
multiProject:
|
|
226
236
|
type: boolean
|
|
227
237
|
requiredOnly:
|
|
@@ -259,7 +269,7 @@ components:
|
|
|
259
269
|
specVersion:
|
|
260
270
|
type: string
|
|
261
271
|
description: CycloneDX Specification version to use
|
|
262
|
-
default: "1.
|
|
272
|
+
default: "1.6"
|
|
263
273
|
filter:
|
|
264
274
|
type: array
|
|
265
275
|
items:
|
|
@@ -304,6 +314,14 @@ components:
|
|
|
304
314
|
standard:
|
|
305
315
|
type: string
|
|
306
316
|
description: The list of standards which may consist of regulations, industry or organizational-specific standards, maturity models, best practices, or any other requirements which can be evaluated against or attested to. Choices are asvs-4.0.3, bsimm-v13, masvs-2.0.0, nist_ssdf-1.1, pcissc-secure-slc-1.1, scvs-1.0.0, ssaf-DRAFT-2023-11
|
|
317
|
+
minConfidence:
|
|
318
|
+
type: number
|
|
319
|
+
description: Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% confidence.
|
|
320
|
+
technique:
|
|
321
|
+
type: array
|
|
322
|
+
items:
|
|
323
|
+
type: string
|
|
324
|
+
description: Analysis technique to use
|
|
307
325
|
CycloneDXSBOM:
|
|
308
326
|
type: object
|
|
309
327
|
externalDocs:
|
package/lib/server/server.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import http from "node:http";
|
|
4
3
|
import path from "node:path";
|
|
@@ -7,7 +6,7 @@ import { URL } from "node:url";
|
|
|
7
6
|
import bodyParser from "body-parser";
|
|
8
7
|
import connect from "connect";
|
|
9
8
|
import { createBom, submitBom } from "../cli/index.js";
|
|
10
|
-
import { getTmpDir, isSecureMode } from "../helpers/utils.js";
|
|
9
|
+
import { getTmpDir, isSecureMode, safeSpawnSync } from "../helpers/utils.js";
|
|
11
10
|
import { postProcess } from "../stages/postgen/postgen.js";
|
|
12
11
|
|
|
13
12
|
import compression from "compression";
|
|
@@ -16,6 +15,37 @@ import compression from "compression";
|
|
|
16
15
|
const TIMEOUT_MS =
|
|
17
16
|
Number.parseInt(process.env.CDXGEN_SERVER_TIMEOUT_MS) || 10 * 60 * 1000;
|
|
18
17
|
|
|
18
|
+
const ALLOWED_PARAMS = [
|
|
19
|
+
"type",
|
|
20
|
+
"excludeType",
|
|
21
|
+
"multiProject",
|
|
22
|
+
"requiredOnly",
|
|
23
|
+
"noBabel",
|
|
24
|
+
"installDeps",
|
|
25
|
+
"projectId",
|
|
26
|
+
"projectName",
|
|
27
|
+
"projectGroup",
|
|
28
|
+
"projectVersion",
|
|
29
|
+
"parentUUID",
|
|
30
|
+
"serverUrl",
|
|
31
|
+
"apiKey",
|
|
32
|
+
"specVersion",
|
|
33
|
+
"filter",
|
|
34
|
+
"only",
|
|
35
|
+
"autoCompositions",
|
|
36
|
+
"gitBranch",
|
|
37
|
+
"lifecycle",
|
|
38
|
+
"deep",
|
|
39
|
+
"profile",
|
|
40
|
+
"exclude",
|
|
41
|
+
"includeFormulation",
|
|
42
|
+
"includeCrypto",
|
|
43
|
+
"standard",
|
|
44
|
+
"minConfidence",
|
|
45
|
+
"technique",
|
|
46
|
+
"tlpClassification",
|
|
47
|
+
];
|
|
48
|
+
|
|
19
49
|
const app = connect();
|
|
20
50
|
|
|
21
51
|
app.use(
|
|
@@ -58,7 +88,7 @@ const gitClone = (repoUrl, branch = null) => {
|
|
|
58
88
|
`Cloning Repo${branch ? ` with branch ${branch}` : ""} to ${tempDir}`,
|
|
59
89
|
);
|
|
60
90
|
|
|
61
|
-
const result =
|
|
91
|
+
const result = safeSpawnSync("git", gitArgs, {
|
|
62
92
|
encoding: "utf-8",
|
|
63
93
|
shell: false,
|
|
64
94
|
});
|
|
@@ -69,41 +99,11 @@ const gitClone = (repoUrl, branch = null) => {
|
|
|
69
99
|
return tempDir;
|
|
70
100
|
};
|
|
71
101
|
|
|
72
|
-
const parseQueryString = (q, body, options = {}) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const queryParams = [
|
|
78
|
-
"type",
|
|
79
|
-
"multiProject",
|
|
80
|
-
"requiredOnly",
|
|
81
|
-
"noBabel",
|
|
82
|
-
"installDeps",
|
|
83
|
-
"projectId",
|
|
84
|
-
"projectName",
|
|
85
|
-
"projectGroup",
|
|
86
|
-
"projectVersion",
|
|
87
|
-
"parentUUID",
|
|
88
|
-
"serverUrl",
|
|
89
|
-
"apiKey",
|
|
90
|
-
"specVersion",
|
|
91
|
-
"filter",
|
|
92
|
-
"only",
|
|
93
|
-
"autoCompositions",
|
|
94
|
-
"gitBranch",
|
|
95
|
-
"lifecycle",
|
|
96
|
-
"deep",
|
|
97
|
-
"profile",
|
|
98
|
-
"exclude",
|
|
99
|
-
"includeFormulation",
|
|
100
|
-
"includeCrypto",
|
|
101
|
-
"standard",
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
for (const param of queryParams) {
|
|
105
|
-
if (q[param]) {
|
|
106
|
-
let value = q[param];
|
|
102
|
+
const parseQueryString = (q, body = {}, options = {}) => {
|
|
103
|
+
// Priority is query params followed by body
|
|
104
|
+
for (const param of ALLOWED_PARAMS) {
|
|
105
|
+
if (q[param] || body[param]) {
|
|
106
|
+
let value = q[param] || body[param];
|
|
107
107
|
// Convert string to boolean
|
|
108
108
|
if (value === "true") {
|
|
109
109
|
value = true;
|
|
@@ -99,6 +99,7 @@ export function textualMetadata(bomJson) {
|
|
|
99
99
|
const { bomType, bomTypeDescription } = findBomType(bomJson);
|
|
100
100
|
const metadata = bomJson.metadata;
|
|
101
101
|
const lifecycles = metadata?.lifecycles || [];
|
|
102
|
+
const tlpClassification = metadata.distribution;
|
|
102
103
|
const cryptoAssetsCount = bomJson?.components?.filter(
|
|
103
104
|
(c) => c.type === "cryptographic-asset",
|
|
104
105
|
).length;
|
|
@@ -122,6 +123,9 @@ export function textualMetadata(bomJson) {
|
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
}
|
|
126
|
+
if (tlpClassification) {
|
|
127
|
+
text = `${text} The Traffic Light Protocol (TLP) classification for this document is '${tlpClassification}'.`;
|
|
128
|
+
}
|
|
125
129
|
if (lifecycles && Array.isArray(lifecycles)) {
|
|
126
130
|
if (lifecycles.length === 1) {
|
|
127
131
|
const thePhase = lifecycles[0].phase;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { PackageURL } from "packageurl-js";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getTimestamp,
|
|
10
10
|
getTmpDir,
|
|
11
11
|
hasAnyProjectType,
|
|
12
|
+
safeExistsSync,
|
|
12
13
|
} from "../../helpers/utils.js";
|
|
13
14
|
import { extractTags, findBomType, textualMetadata } from "./annotator.js";
|
|
14
15
|
|
|
@@ -31,7 +32,7 @@ function relativeDir(d, options) {
|
|
|
31
32
|
return rd.includes("all-layers") ? rd.split("all-layers").pop() : rd;
|
|
32
33
|
}
|
|
33
34
|
const baseDir = options.filePath || process.cwd();
|
|
34
|
-
if (
|
|
35
|
+
if (safeExistsSync(baseDir)) {
|
|
35
36
|
const rdir = relative(baseDir, d);
|
|
36
37
|
return rdir.startsWith(join("..", "..")) ? d : rdir;
|
|
37
38
|
}
|
|
@@ -200,7 +201,7 @@ export function applyStandards(bomJson, options) {
|
|
|
200
201
|
"templates",
|
|
201
202
|
`${astandard}.cdx.json`,
|
|
202
203
|
);
|
|
203
|
-
if (
|
|
204
|
+
if (safeExistsSync(templateFile)) {
|
|
204
205
|
const templateData = JSON.parse(readFileSync(templateFile, "utf-8"));
|
|
205
206
|
if (templateData?.metadata?.licenses) {
|
|
206
207
|
if (!bomJson.metadata.licenses) {
|
|
@@ -224,6 +225,26 @@ export function applyStandards(bomJson, options) {
|
|
|
224
225
|
return bomJson;
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Method to normalize the identity field from a component's evidence block.
|
|
230
|
+
*
|
|
231
|
+
* In different versions of CycloneDX, the `identity` field can be either a single object or an array of objects.
|
|
232
|
+
* This function ensures that the result is always an array for consistent processing.
|
|
233
|
+
*
|
|
234
|
+
* @param {Object} comp - The component object potentially containing evidence.identity.
|
|
235
|
+
* @returns {Array} An array of identity objects (empty if none are present).
|
|
236
|
+
*/
|
|
237
|
+
function normalizeIdentities(comp) {
|
|
238
|
+
const identity = comp?.evidence?.identity;
|
|
239
|
+
if (Array.isArray(identity)) {
|
|
240
|
+
return identity;
|
|
241
|
+
}
|
|
242
|
+
if (identity) {
|
|
243
|
+
return [identity];
|
|
244
|
+
}
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
|
|
227
248
|
/**
|
|
228
249
|
* Method to get the purl identity confidence.
|
|
229
250
|
*
|
|
@@ -235,7 +256,7 @@ function getIdentityConfidence(comp) {
|
|
|
235
256
|
return undefined;
|
|
236
257
|
}
|
|
237
258
|
let confidence;
|
|
238
|
-
for (const aidentity of comp
|
|
259
|
+
for (const aidentity of normalizeIdentities(comp)) {
|
|
239
260
|
if (aidentity?.field === "purl") {
|
|
240
261
|
if (confidence === undefined) {
|
|
241
262
|
confidence = aidentity.confidence || 0;
|
|
@@ -258,7 +279,7 @@ function getIdentityTechniques(comp) {
|
|
|
258
279
|
return undefined;
|
|
259
280
|
}
|
|
260
281
|
const techniques = new Set();
|
|
261
|
-
for (const aidentity of comp
|
|
282
|
+
for (const aidentity of normalizeIdentities(comp)) {
|
|
262
283
|
if (aidentity?.field === "purl") {
|
|
263
284
|
for (const amethod of aidentity.methods || []) {
|
|
264
285
|
techniques.add(amethod?.technique);
|
|
@@ -304,7 +325,7 @@ export function filterBom(bomJson, options) {
|
|
|
304
325
|
// Set.intersection is only available in node >= 22. See Bug# 1651
|
|
305
326
|
if (
|
|
306
327
|
usedTechniques &&
|
|
307
|
-
!
|
|
328
|
+
![...usedTechniques].some((i) => allowedTechniques.has(i))
|
|
308
329
|
) {
|
|
309
330
|
filtered = true;
|
|
310
331
|
continue;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
1
|
import { existsSync, mkdtempSync, readFileSync, readdirSync } from "node:fs";
|
|
3
2
|
import { arch, platform } from "node:os";
|
|
4
3
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
@@ -27,6 +26,7 @@ import {
|
|
|
27
26
|
isMac,
|
|
28
27
|
isSecureMode,
|
|
29
28
|
isWin,
|
|
29
|
+
safeSpawnSync,
|
|
30
30
|
} from "../../helpers/utils.js";
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -207,7 +207,7 @@ export function tryLoadNvmAndInstallTool(nodeVersion) {
|
|
|
207
207
|
fi
|
|
208
208
|
`;
|
|
209
209
|
|
|
210
|
-
const result =
|
|
210
|
+
const result = safeSpawnSync(process.env.SHELL || "bash", ["-c", command], {
|
|
211
211
|
encoding: "utf-8",
|
|
212
212
|
shell: process.env.SHELL || true,
|
|
213
213
|
});
|
|
@@ -232,7 +232,7 @@ export function doNpmInstall(filePath, nvmNodePath) {
|
|
|
232
232
|
if (isSecureMode) {
|
|
233
233
|
installArgs = `${installArgs} --ignore-scripts --no-audit`;
|
|
234
234
|
}
|
|
235
|
-
const resultNpmInstall =
|
|
235
|
+
const resultNpmInstall = safeSpawnSync(
|
|
236
236
|
process.env.SHELL || "bash",
|
|
237
237
|
[
|
|
238
238
|
"-i",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.4.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>",
|
|
@@ -71,16 +71,15 @@
|
|
|
71
71
|
"*": "biome check --fix --no-errors-on-unmatched"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@babel/parser": "^7.27.
|
|
75
|
-
"@babel/traverse": "^7.27.
|
|
74
|
+
"@babel/parser": "^7.27.4",
|
|
75
|
+
"@babel/traverse": "^7.27.4",
|
|
76
76
|
"@iarna/toml": "2.2.5",
|
|
77
|
-
"@npmcli/arborist": "^9.1.
|
|
77
|
+
"@npmcli/arborist": "^9.1.2",
|
|
78
78
|
"ajv": "^8.17.1",
|
|
79
79
|
"ajv-formats": "^3.0.1",
|
|
80
|
-
"cheerio": "^1.
|
|
80
|
+
"cheerio": "^1.1.0",
|
|
81
81
|
"edn-data": "1.1.2",
|
|
82
|
-
"
|
|
83
|
-
"glob": "^11.0.2",
|
|
82
|
+
"glob": "^11.0.3",
|
|
84
83
|
"global-agent": "^3.0.0",
|
|
85
84
|
"got": "^14.4.7",
|
|
86
85
|
"iconv-lite": "^0.6.3",
|
|
@@ -89,11 +88,11 @@
|
|
|
89
88
|
"packageurl-js": "1.0.2",
|
|
90
89
|
"prettify-xml": "^1.2.0",
|
|
91
90
|
"properties-reader": "^2.3.0",
|
|
92
|
-
"semver": "^7.7.
|
|
91
|
+
"semver": "^7.7.2",
|
|
93
92
|
"ssri": "^12.0.0",
|
|
94
93
|
"table": "^6.9.0",
|
|
95
94
|
"tar": "^7.4.3",
|
|
96
|
-
"uuid": "^11.0
|
|
95
|
+
"uuid": "^11.1.0",
|
|
97
96
|
"validate-iri": "^1.0.1",
|
|
98
97
|
"xml-js": "^1.6.11",
|
|
99
98
|
"yaml": "^2.8.0",
|
|
@@ -101,23 +100,25 @@
|
|
|
101
100
|
"yoctocolors": "^2.1.1"
|
|
102
101
|
},
|
|
103
102
|
"optionalDependencies": {
|
|
104
|
-
"@appthreat/atom": "2.2.
|
|
103
|
+
"@appthreat/atom": "2.2.5",
|
|
105
104
|
"@appthreat/cdx-proto": "1.0.1",
|
|
106
|
-
"@cyclonedx/cdxgen-plugins-bin": "1.6.
|
|
107
|
-
"@cyclonedx/cdxgen-plugins-bin-
|
|
108
|
-
"@cyclonedx/cdxgen-plugins-bin-
|
|
109
|
-
"@cyclonedx/cdxgen-plugins-bin-linux-
|
|
110
|
-
"@cyclonedx/cdxgen-plugins-bin-
|
|
111
|
-
"@cyclonedx/cdxgen-plugins-bin-
|
|
112
|
-
"@cyclonedx/cdxgen-plugins-bin-linux-ppc64": "1.6.
|
|
113
|
-
"@cyclonedx/cdxgen-plugins-bin-
|
|
114
|
-
"@cyclonedx/cdxgen-plugins-bin-
|
|
105
|
+
"@cyclonedx/cdxgen-plugins-bin": "1.6.12",
|
|
106
|
+
"@cyclonedx/cdxgen-plugins-bin-darwin-amd64": "1.6.12",
|
|
107
|
+
"@cyclonedx/cdxgen-plugins-bin-darwin-arm64": "1.6.12",
|
|
108
|
+
"@cyclonedx/cdxgen-plugins-bin-linux-amd64": "1.6.12",
|
|
109
|
+
"@cyclonedx/cdxgen-plugins-bin-linux-arm": "1.6.12",
|
|
110
|
+
"@cyclonedx/cdxgen-plugins-bin-linux-arm64": "1.6.12",
|
|
111
|
+
"@cyclonedx/cdxgen-plugins-bin-linux-ppc64": "1.6.12",
|
|
112
|
+
"@cyclonedx/cdxgen-plugins-bin-linuxmusl-amd64": "1.6.12",
|
|
113
|
+
"@cyclonedx/cdxgen-plugins-bin-linuxmusl-arm64": "1.6.12",
|
|
114
|
+
"@cyclonedx/cdxgen-plugins-bin-windows-amd64": "1.6.12",
|
|
115
|
+
"@cyclonedx/cdxgen-plugins-bin-windows-arm64": "1.6.12",
|
|
115
116
|
"body-parser": "^2.2.0",
|
|
116
117
|
"compression": "^1.7.5",
|
|
117
118
|
"connect": "^3.7.0",
|
|
118
119
|
"jsonata": "^2.0.6",
|
|
119
120
|
"sequelize": "^6.37.7",
|
|
120
|
-
"sqlite3": "
|
|
121
|
+
"sqlite3": "npm:@appthreat/sqlite3@^6.0.6"
|
|
121
122
|
},
|
|
122
123
|
"files": [
|
|
123
124
|
"*.js",
|
|
@@ -129,16 +130,56 @@
|
|
|
129
130
|
],
|
|
130
131
|
"devDependencies": {
|
|
131
132
|
"@biomejs/biome": "1.9.4",
|
|
132
|
-
"jest": "^
|
|
133
|
+
"jest": "^30.0.0",
|
|
133
134
|
"typescript": "^5.8.3"
|
|
134
135
|
},
|
|
135
136
|
"overrides": {
|
|
137
|
+
"babel-plugin-istanbul": "^7.0.0",
|
|
138
|
+
"brace-expansion": "2.0.2",
|
|
136
139
|
"jwa": "^2.0.1",
|
|
137
|
-
"glob": "^11.0.
|
|
140
|
+
"glob": "^11.0.3",
|
|
138
141
|
"node-gyp": "^10.2.0",
|
|
139
142
|
"prebuild": "^13.0.0",
|
|
140
143
|
"pacote": "^20.0.0",
|
|
141
|
-
"negotiator": "^0.6.4"
|
|
144
|
+
"negotiator": "^0.6.4",
|
|
145
|
+
"@npmcli/agent": "^3.0.0",
|
|
146
|
+
"@npmcli/fs": "^4.0.0",
|
|
147
|
+
"abbrev": "^3.0.1",
|
|
148
|
+
"cacache": "^19.0.1",
|
|
149
|
+
"camelcase": "^6.3.0",
|
|
150
|
+
"chownr": "^3.0.0",
|
|
151
|
+
"debug": "^4.4.1",
|
|
152
|
+
"escape-string-regexp": "^4.0.0",
|
|
153
|
+
"ini": "^5.0.0",
|
|
154
|
+
"is-stream": "^4.0.1",
|
|
155
|
+
"isexe": "^3.1.1",
|
|
156
|
+
"istanbul-lib-instrument": "^6.0.3",
|
|
157
|
+
"json-parse-even-better-errors": "^4.0.0",
|
|
158
|
+
"lru-cache": "^11.1.0",
|
|
159
|
+
"minimatch": "^10.0.3",
|
|
160
|
+
"minizlib": "^3.0.2",
|
|
161
|
+
"make-fetch-happen": "14.0.3",
|
|
162
|
+
"mkdirp": "^3.0.1",
|
|
163
|
+
"ms": "^2.1.3",
|
|
164
|
+
"nopt": "^8.1.0",
|
|
165
|
+
"on-finished": "^2.4.1",
|
|
166
|
+
"proc-log": "^5.0.0",
|
|
167
|
+
"semver": "^7.7.2",
|
|
168
|
+
"signal-exit": "^4.1.0",
|
|
169
|
+
"sprintf-js": "^1.1.3",
|
|
170
|
+
"ssri": "^12.0.0",
|
|
171
|
+
"statuses": "^2.0.1",
|
|
172
|
+
"strip-json-comments": "^3.1.1",
|
|
173
|
+
"supports-color": "^8.1.1",
|
|
174
|
+
"tar": "^7.4.3",
|
|
175
|
+
"type-fest": "^4.41.0",
|
|
176
|
+
"unique-filename": "^4.0.0",
|
|
177
|
+
"unique-slug": "^5.0.0",
|
|
178
|
+
"uuid": "^11.1.0",
|
|
179
|
+
"which": "^5.0.0",
|
|
180
|
+
"write-file-atomic": "^6.0.0",
|
|
181
|
+
"yallist": "^5.0.0",
|
|
182
|
+
"yargs": "^17.7.2"
|
|
142
183
|
},
|
|
143
184
|
"scripts": {
|
|
144
185
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --inject-globals false lib/managers/docker.test.js lib/helpers/utils.test.js lib/helpers/display.test.js lib/stages/postgen/postgen.test.js lib/evinser/swiftsem.test.js",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/cli/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/cli/index.js"],"names":[],"mappings":"AAg8BA;;;;;;;;GAQG;AACH,gFAFW,MAAM,SAchB;AAqYD;;;;;;;GAOG;AACH,mCALW,MAAM,qBAyEhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM;;;;EAKhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM;;;;EAkBhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAgvChB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAmvBhB;AAED;;;;;;;;;;GAUG;AACH,+DAsEC;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BAkehB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,8BAqZhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAuIhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAkEhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBAkLhB;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,mCAHW,MAAM,8BAqDhB;AAED;;;;;GAKG;AACH,uCAHW,MAAM,8BA4ChB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,qBA2BhB;AAED;;;;;GAKG;AACH,qCAHW,MAAM,8BA8IhB;AAED;;;;;GAKG;AACH,qCAHW,MAAM,8BAmJhB;AAED;;;;;GAKG;AACH,iDAHW,MAAM,qBAmUhB;AAED;;;;;GAKG;AACH,mCAHW,MAAM,qBAiJhB;AAED;;;;;GAKG;AACH,oCAHW,MAAM,8BAwNhB;AAED;;;;;GAKG;AACH,sCAHW,MAAM,8BA8ZhB;AAED;;;;;GAKG;AACH,2CAHW,MAAM;;;;;;;;;;;;;;;;;;;;GAoChB;AAED;;;;;;;;KA+DC;AAED;;;;;;GAMG;AACH,yDAiGC;AAED;;;;;;;;;GASG;AACH,2GAuCC;AAED;;;;;GAKG;AACH,0CAHW,MAAM,EAAE,8BA6vBlB;AAED;;;;;GAKG;AACH,iCAHW,MAAM,8BAqUhB;AAED;;;;;GAKG;AACH,gCAHW,MAAM,qBA2QhB;AAED;;;;;;;GAOG;AACH,wDAHY,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CA2HjD"}
|