@cyclonedx/cdxgen 9.0.1 → 9.1.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 +64 -25
- package/analyzer.js +1 -1
- package/bin/cdxgen.js +18 -24
- package/binary.js +7 -7
- package/data/bom-1.5.schema.json +3660 -0
- package/data/jsf-0.82.schema.json +211 -0
- package/data/pypi-pkg-aliases.json +84 -77
- package/data/spdx.schema.json +621 -0
- package/display.js +102 -0
- package/display.test.js +10 -0
- package/docker.js +12 -24
- package/docker.test.js +1 -1
- package/index.js +306 -294
- package/package.json +5 -3
- package/piptree.js +136 -0
- package/server.js +2 -2
- package/utils.js +500 -214
- package/utils.test.js +301 -35
package/README.md
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
cdxgen is a cli tool and a library to create a valid and compliant [CycloneDX][cyclonedx-homepage] Software Bill-of-Materials (SBOM) containing an aggregate of all project dependencies for c/c++, node.js, php, python, ruby, rust, java, .Net, dart, haskell, elixir, and Go projects in JSON format. CycloneDX 1.5 is a lightweight SBOM specification that is easily created, human and machine-readable, and simple to parse.
|
|
6
6
|
|
|
7
7
|
When used with plugins, cdxgen could generate an SBoM for Linux docker images and even VMs running Linux or Windows operating system.
|
|
8
8
|
|
|
9
|
+
NOTE:
|
|
10
|
+
|
|
11
|
+
CycloneDX 1.5 specification is brand new and unsupported by many downstream tools. Use version 8.6.0 for 1.4 compatibility.
|
|
12
|
+
|
|
9
13
|
## Supported languages and package format
|
|
10
14
|
|
|
11
15
|
| Language/Platform | Package format | Transitive dependencies |
|
|
@@ -57,6 +61,8 @@ Footnotes:
|
|
|
57
61
|
- [4] - See section on plugins
|
|
58
62
|
- [5] - Powered by osquery. See section on plugins
|
|
59
63
|
|
|
64
|
+

|
|
65
|
+
|
|
60
66
|
### Automatic usage detection
|
|
61
67
|
|
|
62
68
|
For node.js projects, lock files are parsed initially so the SBoM would include all dependencies including dev dependencies. An AST parser powered by babel-parser is then used to detect packages that are imported and used by non-test code. Such imported packages would automatically have their `scope` property set to `required` in the resulting SBoM. By passing the argument `--no-babel`, you can disable this analysis. Scope property would then be set based on the `dev` attribute in the lock file.
|
|
@@ -73,12 +79,35 @@ For go, `go mod why` command is used to identify required packages. For php, com
|
|
|
73
79
|
|
|
74
80
|
```shell
|
|
75
81
|
sudo npm install -g @cyclonedx/cdxgen
|
|
82
|
+
|
|
83
|
+
# For CycloneDX 1.4 compatibility use version 8.6.0
|
|
84
|
+
sudo npm install -g @cyclonedx/cdxgen@8.6.0
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Deno install is also supported.
|
|
88
|
+
|
|
89
|
+
```shell
|
|
90
|
+
deno install --allow-read --allow-env --allow-run --allow-sys=uid,systemMemoryInfo --allow-write --allow-net -n cdxgen "npm:@cyclonedx/cdxgen"
|
|
76
91
|
```
|
|
77
92
|
|
|
78
93
|
You can also use the cdxgen container image
|
|
79
94
|
|
|
80
95
|
```bash
|
|
81
96
|
docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen -r /app -o /app/bom.json
|
|
97
|
+
|
|
98
|
+
docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen:v8.6.0 -r /app -o /app/bom.json
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
To use the deno version, use `ghcr.io/cyclonedx/cdxgen-deno` as the image name.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen-deno -r /app -o /app/bom.json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
In deno applications, cdxgen could be directly imported without any conversion. Please see the section on [integration as library](#integration-as-library)
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { createBom, submitBom } from "npm:@cyclonedx/cdxgen@^9.0.1";
|
|
82
111
|
```
|
|
83
112
|
|
|
84
113
|
## Getting Help
|
|
@@ -86,40 +115,43 @@ docker run --rm -v /tmp:/tmp -v $(pwd):/app:rw -t ghcr.io/cyclonedx/cdxgen -r /a
|
|
|
86
115
|
```text
|
|
87
116
|
$ cdxgen -h
|
|
88
117
|
Options:
|
|
89
|
-
-o, --output Output file for bom.xml or bom.json. Default
|
|
90
|
-
|
|
118
|
+
-o, --output Output file for bom.xml or bom.json. Default bom.
|
|
119
|
+
json
|
|
91
120
|
-t, --type Project type
|
|
92
121
|
-r, --recurse Recurse mode suitable for mono-repos [boolean]
|
|
93
|
-
-p, --print Print the SBoM as a table. Defaults to
|
|
94
|
-
output file is not specified with -o
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
-p, --print Print the SBoM as a table with tree. Defaults to
|
|
123
|
+
true if output file is not specified with -o
|
|
124
|
+
[boolean]
|
|
125
|
+
-c, --resolve-class Resolve class names for packages. jars only for n
|
|
126
|
+
ow. [boolean]
|
|
127
|
+
--deep Perform deep searches for components. Useful whil
|
|
128
|
+
e scanning live OS and oci images. [boolean]
|
|
129
|
+
--server-url Dependency track url. Eg: https://deptrack.cyclon
|
|
130
|
+
edx.io
|
|
101
131
|
--api-key Dependency track api key
|
|
102
132
|
--project-group Dependency track project group
|
|
103
|
-
--project-name Dependency track project name. Default use the
|
|
104
|
-
|
|
133
|
+
--project-name Dependency track project name. Default use the di
|
|
134
|
+
rectory name
|
|
105
135
|
--project-version Dependency track project version [default: ""]
|
|
106
|
-
--project-id Dependency track project id. Either provide the
|
|
107
|
-
|
|
136
|
+
--project-id Dependency track project id. Either provide the i
|
|
137
|
+
d or the project name and version together
|
|
108
138
|
--required-only Include only the packages with required scope on
|
|
109
139
|
the SBoM. [boolean]
|
|
110
140
|
--fail-on-error Fail if any dependency extractor fails. [boolean]
|
|
111
|
-
--no-babel Do not use babel to perform usage analysis for
|
|
112
|
-
|
|
141
|
+
--no-babel Do not use babel to perform usage analysis for Ja
|
|
142
|
+
vaScript/TypeScript projects. [boolean]
|
|
113
143
|
--generate-key-and-sign Generate an RSA public/private key pair and then
|
|
114
|
-
sign the generated SBoM using JSON Web
|
|
115
|
-
|
|
144
|
+
sign the generated SBoM using JSON Web Signatures
|
|
145
|
+
. [boolean]
|
|
116
146
|
--server Run cdxgen as a server [boolean]
|
|
117
147
|
--server-host Listen address [default: "127.0.0.1"]
|
|
118
148
|
--server-port Listen port [default: "9090"]
|
|
119
|
-
--install-deps Install dependencies automatically for some
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
149
|
+
--install-deps Install dependencies automatically for some proje
|
|
150
|
+
cts. Defaults to true but disabled for containers
|
|
151
|
+
and oci scans. Use --no-install-deps to disable
|
|
152
|
+
this feature. [boolean] [default: true]
|
|
153
|
+
--validate Validate the generated SBoM using json schema.
|
|
154
|
+
[boolean] [default: false]
|
|
123
155
|
--version Show version number [boolean]
|
|
124
156
|
-h Show help [boolean]
|
|
125
157
|
```
|
|
@@ -231,6 +263,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
|
|
|
231
263
|
- pnpm-lock.yaml
|
|
232
264
|
- Maven (pom.xml)
|
|
233
265
|
- Gradle
|
|
266
|
+
- Python (requirements.txt, setup.py, pyproject.toml)
|
|
234
267
|
|
|
235
268
|
## Environment variables
|
|
236
269
|
|
|
@@ -348,16 +381,22 @@ Permission to modify and redistribute is granted under the terms of the Apache 2
|
|
|
348
381
|
|
|
349
382
|
## Integration as library
|
|
350
383
|
|
|
351
|
-
|
|
384
|
+
cdxgen is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) and could be imported and used with both deno and Node.js >= 16
|
|
352
385
|
|
|
353
386
|
Minimal example:
|
|
354
387
|
|
|
388
|
+
```ts
|
|
389
|
+
import { createBom, submitBom } from "npm:@cyclonedx/cdxgen@^9.0.1";
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
See the [Deno Readme](./contrib/deno/README.md) for detailed instruction.
|
|
393
|
+
|
|
355
394
|
```javascript
|
|
356
395
|
import { createBom, submitBom } from "@cyclonedx/cdxgen";
|
|
357
396
|
// bomNSData would contain bomJson, bomXml
|
|
358
397
|
const bomNSData = await createBom(filePath, options);
|
|
359
398
|
// Submission to dependency track server
|
|
360
|
-
const dbody = await submitBom(args, bomNSData.
|
|
399
|
+
const dbody = await submitBom(args, bomNSData.bomJson);
|
|
361
400
|
```
|
|
362
401
|
|
|
363
402
|
## Node.js >= 20 permission model
|
package/analyzer.js
CHANGED
|
@@ -36,7 +36,7 @@ const getAllFiles = (dir, extn, files, result, regex) => {
|
|
|
36
36
|
if (IGNORE_FILE_PATTERN.test(files[i])) {
|
|
37
37
|
continue;
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
const file = join(dir, files[i]);
|
|
40
40
|
if (statSync(file).isDirectory()) {
|
|
41
41
|
// Ignore directories
|
|
42
42
|
const dirName = basename(file);
|
package/bin/cdxgen.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { createBom, submitBom } from "../index.js";
|
|
3
|
+
import { createBom, submitBom, validateBom } from "../index.js";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { basename, dirname, join, resolve } from "node:path";
|
|
@@ -9,8 +9,8 @@ import crypto from "crypto";
|
|
|
9
9
|
import { start as _serverStart } from "../server.js";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import globalAgent from "global-agent";
|
|
12
|
-
import { table } from "table";
|
|
13
12
|
import process from "node:process";
|
|
13
|
+
import { printTable, printDependencyTree } from "../display.js";
|
|
14
14
|
|
|
15
15
|
let url = import.meta.url;
|
|
16
16
|
if (!url.startsWith("file://")) {
|
|
@@ -39,7 +39,7 @@ const args = yargs(hideBin(process.argv))
|
|
|
39
39
|
alias: "p",
|
|
40
40
|
type: "boolean",
|
|
41
41
|
description:
|
|
42
|
-
"Print the SBoM as a table. Defaults to true if output file is not specified with -o"
|
|
42
|
+
"Print the SBoM as a table with tree. Defaults to true if output file is not specified with -o"
|
|
43
43
|
})
|
|
44
44
|
.option("resolve-class", {
|
|
45
45
|
alias: "c",
|
|
@@ -107,6 +107,11 @@ const args = yargs(hideBin(process.argv))
|
|
|
107
107
|
description:
|
|
108
108
|
"Install dependencies automatically for some projects. Defaults to true but disabled for containers and oci scans. Use --no-install-deps to disable this feature."
|
|
109
109
|
})
|
|
110
|
+
.option("validate", {
|
|
111
|
+
type: "boolean",
|
|
112
|
+
default: false,
|
|
113
|
+
description: "Validate the generated SBoM using json schema."
|
|
114
|
+
})
|
|
110
115
|
.scriptName("cdxgen")
|
|
111
116
|
.version()
|
|
112
117
|
.help("h").argv;
|
|
@@ -130,7 +135,7 @@ if (process.env.GLOBAL_AGENT_HTTP_PROXY || process.env.HTTP_PROXY) {
|
|
|
130
135
|
globalAgent.bootstrap();
|
|
131
136
|
}
|
|
132
137
|
|
|
133
|
-
|
|
138
|
+
const filePath = args._[0] || ".";
|
|
134
139
|
if (!args.projectName) {
|
|
135
140
|
if (filePath !== ".") {
|
|
136
141
|
args.projectName = basename(filePath);
|
|
@@ -143,7 +148,7 @@ if (!args.projectName) {
|
|
|
143
148
|
* projectType: python, nodejs, java, golang
|
|
144
149
|
* multiProject: Boolean to indicate monorepo or multi-module projects
|
|
145
150
|
*/
|
|
146
|
-
|
|
151
|
+
const options = {
|
|
147
152
|
projectType: args.type,
|
|
148
153
|
multiProject: args.recurse,
|
|
149
154
|
output: args.output,
|
|
@@ -340,7 +345,12 @@ const checkPermissions = (filePath) => {
|
|
|
340
345
|
console.log("Try running the command with -t <type> or -r argument");
|
|
341
346
|
}
|
|
342
347
|
}
|
|
343
|
-
|
|
348
|
+
// Perform automatic validation
|
|
349
|
+
if (args.validate) {
|
|
350
|
+
if (!validateBom(bomNSData.bomJson)) {
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
344
354
|
// Automatically submit the bom data
|
|
345
355
|
if (args.serverUrl && args.serverUrl != true && args.apiKey) {
|
|
346
356
|
try {
|
|
@@ -352,23 +362,7 @@ const checkPermissions = (filePath) => {
|
|
|
352
362
|
}
|
|
353
363
|
|
|
354
364
|
if (args.print && bomNSData.bomJson && bomNSData.bomJson.components) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
data.push([comp.group || "", comp.name, comp.version, comp.scope || ""]);
|
|
358
|
-
}
|
|
359
|
-
const config = {
|
|
360
|
-
header: {
|
|
361
|
-
alignment: "center",
|
|
362
|
-
content: "Software Bill-of-Materials\nGenerated by @cyclonedx/cdxgen"
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
console.log(table(data, config));
|
|
366
|
-
console.log(
|
|
367
|
-
"BOM includes",
|
|
368
|
-
bomNSData.bomJson.components.length,
|
|
369
|
-
"components and",
|
|
370
|
-
bomNSData.bomJson.dependencies.length,
|
|
371
|
-
"dependencies"
|
|
372
|
-
);
|
|
365
|
+
printDependencyTree(bomNSData.bomJson);
|
|
366
|
+
printTable(bomNSData.bomJson);
|
|
373
367
|
}
|
|
374
368
|
})();
|
package/binary.js
CHANGED
|
@@ -78,7 +78,7 @@ if (
|
|
|
78
78
|
if (!CDXGEN_PLUGINS_DIR) {
|
|
79
79
|
let globalNodePath = process.env.GLOBAL_NODE_MODULES_PATH || undefined;
|
|
80
80
|
if (!globalNodePath) {
|
|
81
|
-
|
|
81
|
+
const result = spawnSync(
|
|
82
82
|
isWin ? "npm.cmd" : "npm",
|
|
83
83
|
["root", "--quiet", "-g"],
|
|
84
84
|
{
|
|
@@ -251,7 +251,7 @@ export const getGoBuildInfo = (src) => {
|
|
|
251
251
|
|
|
252
252
|
export const getCargoAuditableInfo = (src) => {
|
|
253
253
|
if (CARGO_AUDITABLE_BIN) {
|
|
254
|
-
|
|
254
|
+
const result = spawnSync(CARGO_AUDITABLE_BIN, [src], {
|
|
255
255
|
encoding: "utf-8"
|
|
256
256
|
});
|
|
257
257
|
if (result.status !== 0 || result.error) {
|
|
@@ -278,7 +278,7 @@ export const getOSPackages = (src) => {
|
|
|
278
278
|
if (existsSync(src)) {
|
|
279
279
|
imageType = "rootfs";
|
|
280
280
|
}
|
|
281
|
-
|
|
281
|
+
const tempDir = mkdtempSync(join(tmpdir(), "trivy-cdxgen-"));
|
|
282
282
|
const bomJsonFile = join(tempDir, "trivy-bom.json");
|
|
283
283
|
const args = [
|
|
284
284
|
imageType,
|
|
@@ -299,7 +299,7 @@ export const getOSPackages = (src) => {
|
|
|
299
299
|
if (DEBUG_MODE) {
|
|
300
300
|
console.log("Executing", TRIVY_BIN, args.join(" "));
|
|
301
301
|
}
|
|
302
|
-
|
|
302
|
+
const result = spawnSync(TRIVY_BIN, args, {
|
|
303
303
|
encoding: "utf-8"
|
|
304
304
|
});
|
|
305
305
|
if (result.status !== 0 || result.error) {
|
|
@@ -350,7 +350,7 @@ export const getOSPackages = (src) => {
|
|
|
350
350
|
}
|
|
351
351
|
// Fix the group
|
|
352
352
|
let group = dirname(comp.name);
|
|
353
|
-
|
|
353
|
+
const name = basename(comp.name);
|
|
354
354
|
let purlObj = undefined;
|
|
355
355
|
let distro_codename = "";
|
|
356
356
|
if (group === ".") {
|
|
@@ -461,7 +461,7 @@ export const getOSPackages = (src) => {
|
|
|
461
461
|
pkgList.push(comp);
|
|
462
462
|
// If there is a source package defined include it as well
|
|
463
463
|
if (srcName && srcVersion && srcName !== comp.name) {
|
|
464
|
-
|
|
464
|
+
const newComp = Object.assign({}, comp);
|
|
465
465
|
newComp.name = srcName;
|
|
466
466
|
newComp.version = srcVersion;
|
|
467
467
|
if (purlObj) {
|
|
@@ -495,7 +495,7 @@ export const executeOsQuery = (query) => {
|
|
|
495
495
|
if (DEBUG_MODE) {
|
|
496
496
|
console.log("Execuing", OSQUERY_BIN, args.join(" "));
|
|
497
497
|
}
|
|
498
|
-
|
|
498
|
+
const result = spawnSync(OSQUERY_BIN, args, {
|
|
499
499
|
encoding: "utf-8"
|
|
500
500
|
});
|
|
501
501
|
if (result.status !== 0 || result.error) {
|