@cyclonedx/cdxgen 9.9.0 → 9.9.1

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![cdxgen logo](cdxgen.png)
4
4
 
5
- cdxgen is a cli tool, library, [REPL](./ADVANCED.md), and server 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.
5
+ cdxgen is a CLI tool, library, [REPL](./ADVANCED.md), and server 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 OBOM for Linux docker images and even VMs running Linux or Windows operating systems. cdxgen also includes an evinse tool to generate component evidence and SaaSBOM for some languages.
8
8
 
@@ -20,21 +20,21 @@ Most SBOM tools are like barcode scanners. They can scan a few package manifest
20
20
 
21
21
  | Language/Platform | Package format | Transitive dependencies | Evidence |
22
22
  | ------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -------- |
23
- | node.js | npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js | Yes except .min.js | Yes |
24
- | java | maven (pom.xml [1]), gradle (build.gradle, .kts), scala (sbt), bazel | Yes unless pom.xml is manually parsed due to unavailability of maven or errors | Yes |
25
- | php | composer.lock | Yes | |
26
- | python | pyproject.toml, setup.py, requirements.txt [2], Pipfile.lock, poetry.lock, pdm.lock, bdist_wheel, .whl, .egg-info | Yes using the automatic pip install/freeze. When disabled, only with Pipfile.lock and poetry.lock | Yes |
27
- | go | binary, go.mod, go.sum, Gopkg.lock | Yes except binary | Yes |
28
- | ruby | Gemfile.lock, gemspec | Only for Gemfile.lock | |
29
- | rust | binary, Cargo.toml, Cargo.lock | Only for Cargo.lock | |
23
+ | Node.js | npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js | Yes except .min.js | Yes |
24
+ | Java | maven (pom.xml [1]), gradle (build.gradle, .kts), scala (sbt), bazel | Yes unless pom.xml is manually parsed due to unavailability of maven or errors | Yes |
25
+ | PHP | composer.lock | Yes | |
26
+ | Python | pyproject.toml, setup.py, requirements.txt [2], Pipfile.lock, poetry.lock, pdm.lock, bdist_wheel, .whl, .egg-info | Yes using the automatic pip install/freeze. When disabled, only with Pipfile.lock and poetry.lock | Yes |
27
+ | Go | binary, go.mod, go.sum, Gopkg.lock | Yes except binary | Yes |
28
+ | Ruby | Gemfile.lock, gemspec | Only for Gemfile.lock | |
29
+ | Rust | binary, Cargo.toml, Cargo.lock | Only for Cargo.lock | |
30
30
  | .Net | .csproj, packages.config, project.assets.json [3], packages.lock.json, .nupkg, paket.lock | Only for project.assets.json, packages.lock.json, paket.lock | |
31
- | dart | pubspec.lock, pubspec.yaml | Only for pubspec.lock | |
32
- | haskell | cabal.project.freeze | Yes | |
33
- | elixir | mix.lock | Yes | |
34
- | c/c++/Objective C/c++11 | conan.lock, conanfile.txt, \*.cmake, CMakeLists.txt, meson.build, codebase without package managers! | Yes only for conan.lock. Best effort basis for cmake without version numbers. | Yes |
35
- | clojure | Clojure CLI (deps.edn), Leiningen (project.clj) | Yes unless the files are parsed manually due to lack of clojure cli or leiningen command | |
36
- | swift | Package.resolved, Package.swift (swiftpm) | Yes | |
37
- | docker / oci image | All supported languages. Linux OS packages with plugins [4] | Best effort based on lock files | Yes |
31
+ | Dart | pubspec.lock, pubspec.yaml | Only for pubspec.lock | |
32
+ | Haskell | cabal.project.freeze | Yes | |
33
+ | Elixir | mix.lock | Yes | |
34
+ | C/C++/Objective C/C++11 | conan.lock, conanfile.txt, \*.cmake, CMakeLists.txt, meson.build, codebase without package managers! | Yes only for conan.lock. Best effort basis for cmake without version numbers. | Yes |
35
+ | Clojure | Clojure CLI (deps.edn), Leiningen (project.clj) | Yes unless the files are parsed manually due to lack of clojure cli or leiningen command | |
36
+ | Swift | Package.resolved, Package.swift (swiftpm) | Yes | |
37
+ | Docker / oci image | All supported languages. Linux OS packages with plugins [4] | Best effort based on lock files | Yes |
38
38
  | GitHub Actions | .github/workflows/\*.yml | N/A | Yes |
39
39
  | Linux | All supported languages. Linux OS packages with plugins [5] | Best effort based on lock files | Yes |
40
40
  | Windows | All supported languages. OS packages with best effort [5] | Best effort based on lock files | Yes |
@@ -170,10 +170,7 @@ Options:
170
170
  faults to true. Pass --no-validate to disable.
171
171
  [boolean] [default: true]
172
172
  --evidence Generate SBOM with evidence for supported languag
173
- es. WIP [boolean] [default: false]
174
- --usages-slices-file Path for the usages slice file created by atom.
175
- --data-flow-slices-file Path for the data-flow slice file created by atom
176
- .
173
+ es. [boolean] [default: false]
177
174
  --spec-version CycloneDX Specification version to use. Defaults
178
175
  to 1.5 [default: 1.5]
179
176
  --filter Filter components containining this word in purl.
@@ -399,7 +396,7 @@ sudo npm install -g @cyclonedx/cdxgen-plugins-bin
399
396
  cdxgen odoo@sha256:4e1e147f0e6714e8f8c5806d2b484075b4076ca50490577cdf9162566086d15e -o /tmp/bom.json
400
397
  ```
401
398
 
402
- You can also pass `-t docker` for simple labels. Only the `latest` tag would be pulled if none was specified.
399
+ You can also pass `-t docker` with repository names. Only the `latest` tag would be pulled if none was specified.
403
400
 
404
401
  ```shell
405
402
  cdxgen shiftleft/scan-slim -o /tmp/bom.json -t docker
package/bin/cdxgen.js CHANGED
@@ -10,10 +10,19 @@ import crypto from "node:crypto";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import globalAgent from "global-agent";
12
12
  import process from "node:process";
13
- import { printTable, printDependencyTree } from "../display.js";
13
+ import {
14
+ printCallStack,
15
+ printOccurrences,
16
+ printServices,
17
+ printReachables,
18
+ printTable,
19
+ printDependencyTree
20
+ } from "../display.js";
14
21
  import { findUpSync } from "find-up";
15
22
  import { load as _load } from "js-yaml";
16
23
  import { postProcess } from "../postgen.js";
24
+ import { analyzeProject, createEvinseFile, prepareDB } from "../evinser.js";
25
+ import { ATOM_DB } from "../utils.js";
17
26
 
18
27
  // Support for config files
19
28
  const configPath = findUpSync([
@@ -48,7 +57,14 @@ const args = yargs(hideBin(process.argv))
48
57
  .env("CDXGEN")
49
58
  .option("output", {
50
59
  alias: "o",
51
- description: "Output file for bom.xml or bom.json. Default bom.json"
60
+ description: "Output file for bom.xml or bom.json. Default bom.json",
61
+ default: "bom.json"
62
+ })
63
+ .option("evinse-output", {
64
+ description:
65
+ "Create bom with evidence as a separate file. Default bom.json",
66
+ default: "bom.json",
67
+ hidden: true
52
68
  })
53
69
  .option("type", {
54
70
  alias: "t",
@@ -90,14 +106,17 @@ const args = yargs(hideBin(process.argv))
90
106
  })
91
107
  .option("project-version", {
92
108
  description: "Dependency track project version",
93
- default: ""
109
+ default: "",
110
+ type: "string"
94
111
  })
95
112
  .option("project-id", {
96
113
  description:
97
- "Dependency track project id. Either provide the id or the project name and version together"
114
+ "Dependency track project id. Either provide the id or the project name and version together",
115
+ type: "string"
98
116
  })
99
117
  .option("parent-project-id", {
100
- description: "Dependency track parent project id"
118
+ description: "Dependency track parent project id",
119
+ type: "string"
101
120
  })
102
121
  .option("required-only", {
103
122
  type: "boolean",
@@ -143,20 +162,34 @@ const args = yargs(hideBin(process.argv))
143
162
  "Validate the generated SBOM using json schema. Defaults to true. Pass --no-validate to disable."
144
163
  })
145
164
  .option("evidence", {
146
- hidden: true,
147
165
  type: "boolean",
148
166
  default: false,
149
- description: "Generate SBOM with evidence for supported languages. WIP"
167
+ description: "Generate SBOM with evidence for supported languages."
168
+ })
169
+ .option("deps-slices-file", {
170
+ description: "Path for the parsedeps slice file created by atom.",
171
+ default: "deps.slices.json",
172
+ hidden: true
150
173
  })
151
174
  .option("usages-slices-file", {
152
- description: "Path for the usages slice file created by atom."
175
+ description: "Path for the usages slices file created by atom.",
176
+ default: "usages.slices.json",
177
+ hidden: true
153
178
  })
154
179
  .option("data-flow-slices-file", {
155
- description: "Path for the data-flow slice file created by atom."
180
+ description: "Path for the data-flow slices file created by atom.",
181
+ default: "data-flow.slices.json",
182
+ hidden: true
183
+ })
184
+ .option("reachables-slices-file", {
185
+ description: "Path for the reachables slices file created by atom.",
186
+ default: "reachables.slices.json",
187
+ hidden: true
156
188
  })
157
189
  .option("spec-version", {
158
190
  description: "CycloneDX Specification version to use. Defaults to 1.5",
159
- default: 1.5
191
+ default: 1.5,
192
+ type: "number"
160
193
  })
161
194
  .option("filter", {
162
195
  description:
@@ -289,9 +322,6 @@ const checkPermissions = (filePath) => {
289
322
  if (options.requiredOnly || options["filter"] || options["only"]) {
290
323
  bomNSData = postProcess(bomNSData, options);
291
324
  }
292
- if (!args.output) {
293
- args.output = "bom.json";
294
- }
295
325
  if (
296
326
  args.output &&
297
327
  (typeof args.output === "string" || args.output instanceof String)
@@ -458,6 +488,34 @@ const checkPermissions = (filePath) => {
458
488
  console.log("Try running the command with -t <type> or -r argument");
459
489
  }
460
490
  }
491
+ // Evidence generation
492
+ if (args.evidence) {
493
+ const evinseOptions = {
494
+ _: args._,
495
+ input: options.output,
496
+ output: options.evinseOutput,
497
+ language: options.projectType || "java",
498
+ dbPath: process.env.ATOM_DB || ATOM_DB,
499
+ skipMavenCollector: false,
500
+ force: false,
501
+ withReachables: options.deep,
502
+ usagesSlicesFile: options.usagesSlicesFile,
503
+ dataFlowSlicesFile: options.dataFlowSlicesFile,
504
+ reachablesSlicesFile: options.reachablesSlicesFile
505
+ };
506
+ const dbObjMap = await prepareDB(evinseOptions);
507
+ if (dbObjMap) {
508
+ const sliceArtefacts = await analyzeProject(dbObjMap, evinseOptions);
509
+ const evinseJson = createEvinseFile(sliceArtefacts, evinseOptions);
510
+ bomNSData.bomJson = evinseJson;
511
+ if (args.print && evinseJson) {
512
+ printOccurrences(evinseJson);
513
+ printCallStack(evinseJson);
514
+ printReachables(sliceArtefacts);
515
+ printServices(evinseJson);
516
+ }
517
+ }
518
+ }
461
519
  // Perform automatic validation
462
520
  if (args.validate) {
463
521
  if (!validateBom(bomNSData.bomJson)) {
package/bin/evinse.js CHANGED
@@ -3,9 +3,7 @@
3
3
  // Evinse (Evinse Verification Is Nearly SBOM Evidence)
4
4
  import yargs from "yargs";
5
5
  import { hideBin } from "yargs/helpers";
6
- import { join } from "node:path";
7
6
  import fs from "node:fs";
8
- import { homedir, platform as _platform } from "node:os";
9
7
  import process from "node:process";
10
8
  import { analyzeProject, createEvinseFile, prepareDB } from "../evinser.js";
11
9
  import { validateBom } from "../validator.js";
@@ -15,6 +13,7 @@ import {
15
13
  printServices,
16
14
  printReachables
17
15
  } from "../display.js";
16
+ import { ATOM_DB } from "../utils.js";
18
17
  import { findUpSync } from "find-up";
19
18
  import { load as _load } from "js-yaml";
20
19
 
@@ -38,22 +37,6 @@ if (configPath) {
38
37
  }
39
38
  }
40
39
 
41
- const isWin = _platform() === "win32";
42
- const isMac = _platform() === "darwin";
43
- let ATOM_DB = join(homedir(), ".local", "share", ".atomdb");
44
- if (isWin) {
45
- ATOM_DB = join(homedir(), "AppData", "Local", ".atomdb");
46
- } else if (isMac) {
47
- ATOM_DB = join(homedir(), "Library", "Application Support", ".atomdb");
48
- }
49
-
50
- if (!process.env.ATOM_DB && !fs.existsSync(ATOM_DB)) {
51
- try {
52
- fs.mkdirSync(ATOM_DB, { recursive: true });
53
- } catch (e) {
54
- // ignore
55
- }
56
- }
57
40
  const args = yargs(hideBin(process.argv))
58
41
  .env("EVINSE")
59
42
  .option("input", {
@@ -4,8 +4,8 @@
4
4
  "System.ServiceModel",
5
5
  "System.Data",
6
6
  "spring",
7
- "flask",
8
- "django",
7
+ "pkg:pypi/flask",
8
+ "pkg:pypi/django",
9
9
  "beego",
10
10
  "chi",
11
11
  "echo",
@@ -30,15 +30,33 @@
30
30
  "express",
31
31
  "knex",
32
32
  "vue",
33
- "aiohttp",
34
- "bottle",
35
- "cherrypy",
36
- "drt",
37
- "falcon",
38
- "hug",
39
- "pyramid",
40
- "sanic",
41
- "tornado",
33
+ "pkg:pypi/aiohttp",
34
+ "pkg:pypi/bottle",
35
+ "pkg:pypi/cherrypy",
36
+ "pkg:pypi/drt",
37
+ "pkg:pypi/falcon",
38
+ "pkg:pypi/hug",
39
+ "pkg:pypi/pyramid",
40
+ "pkg:pypi/sanic",
41
+ "pkg:pypi/tornado",
42
+ "pkg:pypi/fastapi",
43
+ "pkg:pypi/pyqt",
44
+ "pkg:pypi/tkinter",
45
+ "pkg:pypi/kivy",
46
+ "pkg:pypi/pyside",
47
+ "pkg:pypi/scikit",
48
+ "pkg:pypi/tensorflow",
49
+ "pkg:pypi/pytorch",
50
+ "pkg:pypi/keras",
51
+ "pkg:pypi/numpy",
52
+ "pkg:pypi/scipy",
53
+ "pkg:pypi/pandas",
54
+ "pkg:pypi/matplotlib",
55
+ "pkg:pypi/google-api-core",
56
+ "pkg:pypi/google-cloud",
57
+ "pkg:pypi/botocore",
58
+ "pkg:pypi/boto3",
59
+ "pkg:pypi/azure",
42
60
  "vibora",
43
61
  "koa",
44
62
  "-sdk",
@@ -2,12 +2,14 @@
2
2
  {
3
3
  "exp": "Apache-2.0",
4
4
  "names": [
5
+ "Apache2",
5
6
  "Apache 2",
6
7
  "Apache 2.0",
7
8
  "Apache Version 2.0",
8
9
  "Apache 2.0 License",
9
10
  "Apache Software License, Version 2.0",
10
11
  "The Apache Software License, Version 2.0",
12
+ "Apache License v2.0",
11
13
  "Apache License (v2.0)",
12
14
  "Apache License 2.0",
13
15
  "Apache License Version 2.0",
@@ -20,6 +22,9 @@
20
22
  "Apache-2.0 OR MIT",
21
23
  "Apache2.0",
22
24
  "apache-2-0",
25
+ "APL2",
26
+ "the Apache License, ASL Version 2.0",
27
+ "Apache Publich License 2.0",
23
28
  "https://opensource.org/licenses/Apache2.0",
24
29
  "https://opensource.org/license/apache-2-0",
25
30
  "http://www.apache.org/licenses/LICENSE-2.0.html"
@@ -27,7 +32,14 @@
27
32
  },
28
33
  {
29
34
  "exp": "0BSD",
30
- "names": ["Zero-Clause BSD", "BSD", "BSD License", "BSD-like"]
35
+ "names": [
36
+ "Zero-Clause BSD",
37
+ "BSD",
38
+ "BSD License",
39
+ "BSD-like",
40
+ "new BSD License",
41
+ "new BSD"
42
+ ]
31
43
  },
32
44
  {
33
45
  "exp": "BSD-2-Clause",
@@ -37,7 +49,8 @@
37
49
  "BSD-2-Clause",
38
50
  "BSD 2-Clause License",
39
51
  "The BSD 2-Clause License",
40
- "The 2-Clause BSD License"
52
+ "The 2-Clause BSD License",
53
+ "The BSD License"
41
54
  ]
42
55
  },
43
56
  {
@@ -46,6 +59,7 @@
46
59
  "BSD 3 Clause",
47
60
  "BSD 3-Clause",
48
61
  "BSD-3-Clause",
62
+ "BSD 3-clause",
49
63
  "BSD 3-Clause License",
50
64
  "The BSD 3-Clause License",
51
65
  "BSD 3-Clause \"New\" or \"Revised\" License (BSD-3-Clause)",
@@ -55,7 +69,8 @@
55
69
  "Revised BSD",
56
70
  "Revised BSD License",
57
71
  "The New BSD License",
58
- "BSD (3-clause)"
72
+ "BSD (3-clause)",
73
+ "3-Clause BSD License"
59
74
  ]
60
75
  },
61
76
  {
@@ -70,6 +85,10 @@
70
85
  "BSD (4-clause)"
71
86
  ]
72
87
  },
88
+ {
89
+ "exp": "CC0-1.0",
90
+ "names": ["CC0"]
91
+ },
73
92
  {
74
93
  "exp": "CDDL-1.0",
75
94
  "names": [
@@ -151,6 +170,7 @@
151
170
  "LGPL v2.1",
152
171
  "LGPL-2.1",
153
172
  "LGPL2.1",
173
+ "LGPL, version 2.1",
154
174
  "GNU Lesser General Public License",
155
175
  "GNU Lesser General Public License Version 2.1",
156
176
  "GNU Lesser General Public License Version 2.1, February 1999",
@@ -270,7 +290,13 @@
270
290
  },
271
291
  {
272
292
  "exp": "MPL-2.0",
273
- "names": ["MPL 2.0", "Mozilla Public License 2.0"]
293
+ "names": [
294
+ "MPL 2.0",
295
+ "Mozilla Public License 2.0",
296
+ "Mozilla Public License version 2.0",
297
+ "Mozilla Public License, version 2.0",
298
+ "Mozilla Public License 2.0 (MPL 2.0)"
299
+ ]
274
300
  },
275
301
  {
276
302
  "exp": "NetCDF",
@@ -282,6 +308,19 @@
282
308
  },
283
309
  {
284
310
  "exp": "ISC",
285
- "names": ["ISC license"]
311
+ "names": ["ISC license", "ISC License (ISCL)"]
312
+ },
313
+ {
314
+ "exp": "ICU",
315
+ "names": ["Unicode/ICU License"]
316
+ },
317
+ {
318
+ "exp": "PSF-2.0",
319
+ "names": [
320
+ "Python Software Foundation License",
321
+ "Python Software Foundation License (PSFL)",
322
+ "Python Software Foundation License 2.0",
323
+ "PSFL"
324
+ ]
286
325
  }
287
326
  ]
@@ -553,6 +553,7 @@
553
553
  "creole": "python-creole",
554
554
  "creoleparser": "creoleparser",
555
555
  "crispy-forms": "django-crispy-forms",
556
+ "crum": "django-crum",
556
557
  "cronlog": "python-crontab",
557
558
  "crontab": "python-crontab",
558
559
  "crypto": "pycryptodome",
@@ -589,6 +590,7 @@
589
590
  "djcelery": "django-celery",
590
591
  "djkombu": "django-kombu",
591
592
  "djorm-pgarray": "djorm-ext-pgarray",
593
+ "django-filters": "filters-django",
592
594
  "dns": "dnspython",
593
595
  "docgen": "ansible-docgenerator",
594
596
  "docker": "docker-py",
@@ -631,6 +633,7 @@
631
633
  "fdpexpect": "pexpect",
632
634
  "fedora": "python-fedora",
633
635
  "fias": "ailove-django-fias",
636
+ "fieldsignals": "django-fieldsignals",
634
637
  "fiftyone-degrees": "51degrees-mobile-detector",
635
638
  "fiftyonedegrees": "51degrees-mobile-detector-v3-wrapper",
636
639
  "five": "five.customerize",
@@ -709,6 +712,7 @@
709
712
  "igraph": "python-igraph",
710
713
  "imdb": "imdbpy",
711
714
  "impala": "impyla",
715
+ "imagekit": "django-imagekit",
712
716
  "impersonate": "django-impersonate",
713
717
  "inmemorystorage": "ambition-inmemorystorage",
714
718
  "ipaddress": "backport-ipaddress",
@@ -845,6 +849,7 @@
845
849
  "path": "path.py",
846
850
  "patricia": "patricia-trie",
847
851
  "paver": "paver",
852
+ "packageurl": "packageurl-python",
848
853
  "peak": "proxytypes",
849
854
  "picasso": "anderson.picasso",
850
855
  "picklefield": "django-picklefield",
@@ -1057,6 +1062,7 @@
1057
1062
  "slugify": "unicode-slugify",
1058
1063
  "smarkets": "smk-python-sdk",
1059
1064
  "snappy": "ctypes-snappy",
1065
+ "social-core": "social-auth-core",
1060
1066
  "social-django": "social-auth-app-django",
1061
1067
  "socketio": "python-socketio",
1062
1068
  "socketserver": "pies2overrides",