@cyclonedx/cdxgen 9.9.2 → 9.9.4
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 -2
- package/bin/cdxgen.js +8 -3
- package/docker.js +2 -2
- package/evinser.js +51 -28
- package/index.js +256 -121
- package/package.json +8 -8
- package/utils.js +124 -59
- package/utils.test.js +27 -1
package/README.md
CHANGED
|
@@ -49,6 +49,8 @@ Most SBOM tools are like barcode scanners. They can scan a few package manifest
|
|
|
49
49
|
| Gradle Cache | $HOME/caches/modules-2/files-2.1/\*\*/\*.jar | N/A | |
|
|
50
50
|
| Helm Index | $HOME/.cache/helm/repository/\*\*/\*.yaml | N/A | |
|
|
51
51
|
| Docker compose | docker-compose\*.yml. Images would also be scanned. | N/A | |
|
|
52
|
+
| Dockerfile | `*Dockerfile*` Images would also be scanned. | N/A | |
|
|
53
|
+
| Containerfile | `*Containerfile*`. Images would also be scanned. | N/A | |
|
|
52
54
|
| Google CloudBuild configuration | cloudbuild.yaml | N/A | |
|
|
53
55
|
| OpenAPI | openapi\*.json, openapi\*.yaml | N/A | |
|
|
54
56
|
|
|
@@ -173,9 +175,9 @@ Options:
|
|
|
173
175
|
es. [boolean] [default: false]
|
|
174
176
|
--spec-version CycloneDX Specification version to use. Defaults
|
|
175
177
|
to 1.5 [default: 1.5]
|
|
176
|
-
--filter Filter components
|
|
178
|
+
--filter Filter components containing this word in purl.
|
|
177
179
|
Multiple values allowed. [array]
|
|
178
|
-
--only Include components only
|
|
180
|
+
--only Include components only containing this word in
|
|
179
181
|
purl. Useful to generate BOM with first party co
|
|
180
182
|
mponents alone. Multiple values allowed. [array]
|
|
181
183
|
--author The person(s) who created the BOM. Set this value
|
package/bin/cdxgen.js
CHANGED
|
@@ -150,6 +150,7 @@ const args = yargs(hideBin(process.argv))
|
|
|
150
150
|
})
|
|
151
151
|
.option("install-deps", {
|
|
152
152
|
type: "boolean",
|
|
153
|
+
hidden: true,
|
|
153
154
|
default: true,
|
|
154
155
|
description:
|
|
155
156
|
"Install dependencies automatically for some projects. Defaults to true but disabled for containers and oci scans. Use --no-install-deps to disable this feature."
|
|
@@ -192,11 +193,11 @@ const args = yargs(hideBin(process.argv))
|
|
|
192
193
|
})
|
|
193
194
|
.option("filter", {
|
|
194
195
|
description:
|
|
195
|
-
"Filter components
|
|
196
|
+
"Filter components containing this word in purl. Multiple values allowed."
|
|
196
197
|
})
|
|
197
198
|
.option("only", {
|
|
198
199
|
description:
|
|
199
|
-
"Include components only
|
|
200
|
+
"Include components only containing this word in purl. Useful to generate BOM with first party components alone. Multiple values allowed."
|
|
200
201
|
})
|
|
201
202
|
.option("author", {
|
|
202
203
|
description:
|
|
@@ -215,10 +216,15 @@ const args = yargs(hideBin(process.argv))
|
|
|
215
216
|
"generic"
|
|
216
217
|
]
|
|
217
218
|
})
|
|
219
|
+
.option("exclude", {
|
|
220
|
+
description: "Additional glob pattern(s) to ignore",
|
|
221
|
+
hidden: true
|
|
222
|
+
})
|
|
218
223
|
.completion("completion", "Generate bash/zsh completion")
|
|
219
224
|
.array("filter")
|
|
220
225
|
.array("only")
|
|
221
226
|
.array("author")
|
|
227
|
+
.array("exclude")
|
|
222
228
|
.option("auto-compositions", {
|
|
223
229
|
type: "boolean",
|
|
224
230
|
default: true,
|
|
@@ -514,7 +520,6 @@ const checkPermissions = (filePath) => {
|
|
|
514
520
|
if (bomNSData.nsMapping && Object.keys(bomNSData.nsMapping).length) {
|
|
515
521
|
const nsFile = jsonFile + ".map";
|
|
516
522
|
fs.writeFileSync(nsFile, JSON.stringify(bomNSData.nsMapping));
|
|
517
|
-
console.log("Namespace mapping file written to", nsFile);
|
|
518
523
|
}
|
|
519
524
|
}
|
|
520
525
|
} else if (!options.print) {
|
package/docker.js
CHANGED
|
@@ -126,8 +126,8 @@ const getDefaultOptions = () => {
|
|
|
126
126
|
opts.prefixUrl = isWin
|
|
127
127
|
? WIN_LOCAL_TLS
|
|
128
128
|
: isDockerRootless
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
? `http://unix:${homedir()}/.docker/run/docker.sock:`
|
|
130
|
+
: "http://unix:/var/run/docker.sock:";
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
} else {
|
package/evinser.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
collectMvnDependencies
|
|
8
8
|
} from "./utils.js";
|
|
9
9
|
import { tmpdir } from "node:os";
|
|
10
|
-
import path
|
|
10
|
+
import path from "node:path";
|
|
11
11
|
import fs from "node:fs";
|
|
12
12
|
import * as db from "./db.js";
|
|
13
13
|
import { PackageURL } from "packageurl-js";
|
|
@@ -94,15 +94,30 @@ export const catalogMavenDeps = async (
|
|
|
94
94
|
Namespaces,
|
|
95
95
|
options = {}
|
|
96
96
|
) => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
let jarNSMapping = undefined;
|
|
98
|
+
if (fs.existsSync(path.join(dirPath, "bom.json.map"))) {
|
|
99
|
+
try {
|
|
100
|
+
const mapData = JSON.parse(
|
|
101
|
+
fs.readFileSync(path.join(dirPath, "bom.json.map"))
|
|
102
|
+
);
|
|
103
|
+
if (mapData && Object.keys(mapData).length) {
|
|
104
|
+
jarNSMapping = mapData;
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
// ignore
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (!jarNSMapping) {
|
|
111
|
+
console.log("About to collect jar dependencies for the path", dirPath);
|
|
112
|
+
const mavenCmd = getMavenCommand(dirPath, dirPath);
|
|
113
|
+
// collect all jars including from the cache if data-flow mode is enabled
|
|
114
|
+
jarNSMapping = collectMvnDependencies(
|
|
115
|
+
mavenCmd,
|
|
116
|
+
dirPath,
|
|
117
|
+
false,
|
|
118
|
+
options.withDeepJarCollector
|
|
119
|
+
);
|
|
120
|
+
}
|
|
106
121
|
if (jarNSMapping) {
|
|
107
122
|
for (const purl of Object.keys(jarNSMapping)) {
|
|
108
123
|
purlsJars[purl] = jarNSMapping[purl].jarFile;
|
|
@@ -317,9 +332,6 @@ export const analyzeProject = async (dbObjMap, options) => {
|
|
|
317
332
|
if (retMap && retMap.slicesFile && fs.existsSync(retMap.slicesFile)) {
|
|
318
333
|
usageSlice = JSON.parse(fs.readFileSync(retMap.slicesFile, "utf-8"));
|
|
319
334
|
usagesSlicesFile = retMap.slicesFile;
|
|
320
|
-
console.log(
|
|
321
|
-
`To speed up this step, cache ${usagesSlicesFile} and invoke evinse with the --usages-slices-file argument.`
|
|
322
|
-
);
|
|
323
335
|
}
|
|
324
336
|
}
|
|
325
337
|
if (usageSlice && Object.keys(usageSlice).length) {
|
|
@@ -349,9 +361,6 @@ export const analyzeProject = async (dbObjMap, options) => {
|
|
|
349
361
|
if (retMap && retMap.slicesFile && fs.existsSync(retMap.slicesFile)) {
|
|
350
362
|
dataFlowSlicesFile = retMap.slicesFile;
|
|
351
363
|
dataFlowSlice = JSON.parse(fs.readFileSync(retMap.slicesFile, "utf-8"));
|
|
352
|
-
console.log(
|
|
353
|
-
`To speed up this step, cache ${dataFlowSlicesFile} and invoke evinse with the --data-flow-slices-file argument.`
|
|
354
|
-
);
|
|
355
364
|
}
|
|
356
365
|
}
|
|
357
366
|
}
|
|
@@ -381,9 +390,6 @@ export const analyzeProject = async (dbObjMap, options) => {
|
|
|
381
390
|
reachablesSlice = JSON.parse(
|
|
382
391
|
fs.readFileSync(retMap.slicesFile, "utf-8")
|
|
383
392
|
);
|
|
384
|
-
console.log(
|
|
385
|
-
`To speed up this step, cache ${reachablesSlicesFile} and invoke evinse with the --reachables-slices-file argument.`
|
|
386
|
-
);
|
|
387
393
|
}
|
|
388
394
|
}
|
|
389
395
|
}
|
|
@@ -492,14 +498,16 @@ export const parseSliceUsages = async (
|
|
|
492
498
|
!isFilterableType(language, userDefinedTypesMap, atype[1])
|
|
493
499
|
) {
|
|
494
500
|
if (!atype[1].includes("(") && !atype[1].includes(".py")) {
|
|
495
|
-
typesToLookup.add(atype[1]);
|
|
501
|
+
typesToLookup.add(simplifyType(atype[1]));
|
|
496
502
|
// Javascript calls can be resolved to a precise line number only from the call nodes
|
|
497
503
|
if (
|
|
498
504
|
["javascript", "js", "ts", "typescript"].includes(language) &&
|
|
499
505
|
ausageLine
|
|
500
506
|
) {
|
|
501
507
|
if (atype[1].includes(":")) {
|
|
502
|
-
typesToLookup.add(
|
|
508
|
+
typesToLookup.add(
|
|
509
|
+
simplifyType(atype[1].split("::")[0].replace(/:/g, "/"))
|
|
510
|
+
);
|
|
503
511
|
}
|
|
504
512
|
addToOverrides(lKeyOverrides, atype[1], fileName, ausageLine);
|
|
505
513
|
}
|
|
@@ -526,7 +534,7 @@ export const parseSliceUsages = async (
|
|
|
526
534
|
!acall?.resolvedMethod.includes("(") &&
|
|
527
535
|
!acall?.resolvedMethod.includes(".py")
|
|
528
536
|
) {
|
|
529
|
-
typesToLookup.add(acall?.resolvedMethod);
|
|
537
|
+
typesToLookup.add(simplifyType(acall?.resolvedMethod));
|
|
530
538
|
// Javascript calls can be resolved to a precise line number only from the call nodes
|
|
531
539
|
if (acall.lineNumber) {
|
|
532
540
|
addToOverrides(
|
|
@@ -554,10 +562,12 @@ export const parseSliceUsages = async (
|
|
|
554
562
|
for (const aparamType of acall?.paramTypes || []) {
|
|
555
563
|
if (!isFilterableType(language, userDefinedTypesMap, aparamType)) {
|
|
556
564
|
if (!aparamType.includes("(") && !aparamType.includes(".py")) {
|
|
557
|
-
typesToLookup.add(aparamType);
|
|
565
|
+
typesToLookup.add(simplifyType(aparamType));
|
|
558
566
|
if (acall.lineNumber) {
|
|
559
567
|
if (aparamType.includes(":")) {
|
|
560
|
-
typesToLookup.add(
|
|
568
|
+
typesToLookup.add(
|
|
569
|
+
simplifyType(aparamType.split("::")[0].replace(/:/g, "/"))
|
|
570
|
+
);
|
|
561
571
|
}
|
|
562
572
|
addToOverrides(
|
|
563
573
|
lKeyOverrides,
|
|
@@ -603,7 +613,7 @@ export const parseSliceUsages = async (
|
|
|
603
613
|
} else {
|
|
604
614
|
// Check the namespaces db
|
|
605
615
|
let nsHits = typePurlsCache[atype];
|
|
606
|
-
if (["java", "jar"].includes(language)) {
|
|
616
|
+
if (!nsHits && ["java", "jar"].includes(language)) {
|
|
607
617
|
nsHits = await dbObjMap.Namespaces.findAll({
|
|
608
618
|
attributes: ["purl"],
|
|
609
619
|
where: {
|
|
@@ -623,6 +633,9 @@ export const parseSliceUsages = async (
|
|
|
623
633
|
}
|
|
624
634
|
}
|
|
625
635
|
typePurlsCache[atype] = nsHits;
|
|
636
|
+
} else {
|
|
637
|
+
// Avoid persistent lookups
|
|
638
|
+
typePurlsCache[atype] = [];
|
|
626
639
|
}
|
|
627
640
|
}
|
|
628
641
|
}
|
|
@@ -783,7 +796,7 @@ export const detectServicesFromUDT = (
|
|
|
783
796
|
const endpoints = extractEndpoints(language, fields[0].name);
|
|
784
797
|
let serviceName = "service";
|
|
785
798
|
if (audt.fileName) {
|
|
786
|
-
serviceName = `${basename(
|
|
799
|
+
serviceName = `${path.basename(
|
|
787
800
|
audt.fileName.replace(".py", "")
|
|
788
801
|
)}-service`;
|
|
789
802
|
}
|
|
@@ -828,7 +841,7 @@ export const extractEndpoints = (language, code) => {
|
|
|
828
841
|
case "jar":
|
|
829
842
|
if (
|
|
830
843
|
code.startsWith("@") &&
|
|
831
|
-
code.includes("Mapping") &&
|
|
844
|
+
(code.includes("Mapping") || code.includes("Path")) &&
|
|
832
845
|
code.includes("(")
|
|
833
846
|
) {
|
|
834
847
|
const matches = code.match(/['"](.*?)['"]/gi) || [];
|
|
@@ -1184,6 +1197,16 @@ export const framePicker = (dfFrames) => {
|
|
|
1184
1197
|
return aframe;
|
|
1185
1198
|
};
|
|
1186
1199
|
|
|
1200
|
+
/**
|
|
1201
|
+
* Method to simplify types. For example, arrays ending with [] could be simplified.
|
|
1202
|
+
*
|
|
1203
|
+
* @param {string} typeFullName Full name of the type to simplify
|
|
1204
|
+
* @returns Simplified type string
|
|
1205
|
+
*/
|
|
1206
|
+
export const simplifyType = (typeFullName) => {
|
|
1207
|
+
return typeFullName.replace("[]", "");
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1187
1210
|
export const getClassTypeFromSignature = (language, typeFullName) => {
|
|
1188
1211
|
if (["java", "jar"].includes(language) && typeFullName.includes(":")) {
|
|
1189
1212
|
typeFullName = typeFullName.split(":")[0];
|
|
@@ -1219,7 +1242,7 @@ export const getClassTypeFromSignature = (language, typeFullName) => {
|
|
|
1219
1242
|
if (typeFullName.includes("$")) {
|
|
1220
1243
|
typeFullName = typeFullName.split("$")[0];
|
|
1221
1244
|
}
|
|
1222
|
-
return typeFullName;
|
|
1245
|
+
return simplifyType(typeFullName);
|
|
1223
1246
|
};
|
|
1224
1247
|
|
|
1225
1248
|
const addToOverrides = (lKeyOverrides, atype, fileName, ausageLineNumber) => {
|