@cyclonedx/cdxgen 10.11.0 → 11.0.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 CHANGED
@@ -33,6 +33,7 @@ Our philosophy:
33
33
  - Precision: Try using multiple techniques to improve precision, even if it takes extra time.
34
34
  - Personas: Cater to the needs of a range of personas such as security researchers, compliance auditors, developers, and SOC.
35
35
  - Lifecycle: Support BOM generation for various product lifecycles.
36
+ - Machine Learning: Optimize the generated data for Machine Learning (ML) purposes by considering the various model properties.
36
37
 
37
38
  ## Documentation
38
39
 
@@ -92,7 +93,7 @@ docker run --rm -e CDXGEN_DEBUG_MODE=debug -v /tmp:/tmp -v $(pwd):/app:rw -t ghc
92
93
  In deno applications, cdxgen could be directly imported without any conversion. Please see the section on [integration as a library](#integration-as-library)
93
94
 
94
95
  ```ts
95
- import { createBom, submitBom } from "npm:@cyclonedx/cdxgen@^10.9.6";
96
+ import { createBom, submitBom } from "npm:@cyclonedx/cdxgen@^11.0.0";
96
97
  ```
97
98
 
98
99
  ## Getting Help
@@ -116,6 +117,7 @@ Options:
116
117
  --deep Perform deep searches for components. Useful while scanning C/C++ apps, live OS and oci i
117
118
  mages. [boolean]
118
119
  --server-url Dependency track url. Eg: https://deptrack.cyclonedx.io
120
+ --skip-dt-tls-check Skip TLS certificate check when calling Dependency-Track. [boolean] [default: false]
119
121
  --api-key Dependency track api key
120
122
  --project-group Dependency track project group
121
123
  --project-name Dependency track project name. Default use the directory name
@@ -137,7 +139,7 @@ Options:
137
139
  --validate Validate the generated SBOM using json schema. Defaults to true. Pass --no-validate to di
138
140
  sable. [boolean] [default: true]
139
141
  --evidence Generate SBOM with evidence for supported languages. [boolean] [default: false]
140
- --spec-version CycloneDX Specification version to use. Defaults to 1.5 [number] [default: 1.5]
142
+ --spec-version CycloneDX Specification version to use. Defaults to 1.6 [number] [default: 1.6]
141
143
  --filter Filter components containing this word in purl or component.properties.value. Multiple va
142
144
  lues allowed. [array]
143
145
  --only Include components only containing this word in purl. Useful to generate BOM with first p
@@ -145,17 +147,22 @@ Options:
145
147
  --author The person(s) who created the BOM. Set this value if you're intending the modify the BOM
146
148
  and claim authorship. [array] [default: "OWASP Foundation"]
147
149
  --profile BOM profile to use for generation. Default generic.
148
- [choices: "appsec", "research", "operational", "threat-modeling", "license-compliance", "generic"] [default: "generic"
149
- ]
150
+ [choices: "appsec", "research", "operational", "threat-modeling", "license-compliance", "generic", "machine-learning",
151
+ "ml", "deep-learning", "ml-deep", "ml-tiny"] [default: "generic"]
150
152
  --exclude Additional glob pattern(s) to ignore [array]
151
- --include-formulation Generate formulation section with git metadata and build tools. Defaults to true. Invoke
152
- with --no-include-formulation to disable. [boolean] [default: true]
153
+ --include-formulation Generate formulation section with git metadata and build tools. Defaults to false.
154
+ [boolean] [default: false]
153
155
  --include-crypto Include crypto libraries as components. [boolean] [default: false]
154
156
  --standard The list of standards which may consist of regulations, industry or organizational-specif
155
157
  ic standards, maturity models, best practices, or any other requirements which can be eva
156
158
  luated against or attested to.
157
159
  [array] [choices: "asvs-4.0.3", "bsimm-v13", "masvs-2.0.0", "nist_ssdf-1.1", "pcissc-secure-slc-1.1", "scvs-1.0.0", "s
158
160
  saf-DRAFT-2023-11"]
161
+ --min-confidence Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% con
162
+ fidence. [number] [default: 0]
163
+ --technique Analysis technique to use
164
+ [array] [choices: "auto", "source-code-analysis", "binary-analysis", "manifest-analysis", "hash-comparison", "instrume
165
+ ntation", "filename"]
159
166
  --auto-compositions Automatically set compositions when the BOM was filtered. Defaults to true
160
167
  [boolean] [default: true]
161
168
  -h, --help Show help [boolean]
package/bin/cdxgen.js CHANGED
@@ -235,6 +235,11 @@ const args = yargs(hideBin(process.argv))
235
235
  "threat-modeling",
236
236
  "license-compliance",
237
237
  "generic",
238
+ "machine-learning",
239
+ "ml",
240
+ "deep-learning",
241
+ "ml-deep",
242
+ "ml-tiny",
238
243
  ],
239
244
  })
240
245
  .option("lifecycle", {
@@ -292,6 +297,24 @@ const args = yargs(hideBin(process.argv))
292
297
  hidden: true,
293
298
  choices: ["safe-pip-install", "suggest-build-tools"],
294
299
  })
300
+ .option("min-confidence", {
301
+ description:
302
+ "Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% confidence.",
303
+ default: 0,
304
+ type: "number",
305
+ })
306
+ .option("technique", {
307
+ description: "Analysis technique to use",
308
+ choices: [
309
+ "auto",
310
+ "source-code-analysis",
311
+ "binary-analysis",
312
+ "manifest-analysis",
313
+ "hash-comparison",
314
+ "instrumentation",
315
+ "filename",
316
+ ],
317
+ })
295
318
  .completion("completion", "Generate bash/zsh completion")
296
319
  .array("type")
297
320
  .array("excludeType")
@@ -301,6 +324,7 @@ const args = yargs(hideBin(process.argv))
301
324
  .array("exclude")
302
325
  .array("standard")
303
326
  .array("feature-flags")
327
+ .array("technique")
304
328
  .option("auto-compositions", {
305
329
  type: "boolean",
306
330
  default: true,
@@ -313,6 +337,14 @@ const args = yargs(hideBin(process.argv))
313
337
  "$0 -t java -t js .",
314
338
  "Generate a SBOM for Java and JavaScript in the current directory",
315
339
  ],
340
+ [
341
+ "$0 -t java --profile ml .",
342
+ "Generate a Java SBOM for machine learning purposes.",
343
+ ],
344
+ [
345
+ "$0 -t python --profile research .",
346
+ "Generate a Python SBOM for appsec research.",
347
+ ],
316
348
  ["$0 --server", "Run cdxgen as a server"],
317
349
  ])
318
350
  .epilogue("for documentation, visit https://cyclonedx.github.io/cdxgen")
@@ -414,6 +446,29 @@ const applyAdvancedOptions = (options) => {
414
446
  case "license-compliance":
415
447
  process.env.FETCH_LICENSE = "true";
416
448
  break;
449
+ case "ml-tiny":
450
+ process.env.FETCH_LICENSE = "true";
451
+ options.deep = false;
452
+ options.evidence = false;
453
+ options.includeCrypto = false;
454
+ options.installDeps = false;
455
+ break;
456
+ case "machine-learning":
457
+ case "ml":
458
+ process.env.FETCH_LICENSE = "true";
459
+ options.deep = true;
460
+ options.evidence = false;
461
+ options.includeCrypto = false;
462
+ options.installDeps = true;
463
+ break;
464
+ case "deep-learning":
465
+ case "ml-deep":
466
+ process.env.FETCH_LICENSE = "true";
467
+ options.deep = true;
468
+ options.evidence = true;
469
+ options.includeCrypto = true;
470
+ options.installDeps = true;
471
+ break;
417
472
  default:
418
473
  break;
419
474
  }
@@ -685,8 +740,10 @@ const checkPermissions = (filePath) => {
685
740
  usagesSlicesFile: options.usagesSlicesFile,
686
741
  dataFlowSlicesFile: options.dataFlowSlicesFile,
687
742
  reachablesSlicesFile: options.reachablesSlicesFile,
743
+ semanticsSlicesFile: options.semanticsSlicesFile,
688
744
  includeCrypto: options.includeCrypto,
689
745
  specVersion: options.specVersion,
746
+ profile: options.profile,
690
747
  };
691
748
  const dbObjMap = await evinserModule.prepareDB(evinseOptions);
692
749
  if (dbObjMap) {
@@ -736,7 +793,6 @@ const checkPermissions = (filePath) => {
736
793
  printTable(bomNSData.bomJson);
737
794
  // CBOM related print
738
795
  if (options.includeCrypto) {
739
- console.log("\n*** Cryptography BOM ***");
740
796
  printTable(bomNSData.bomJson, ["cryptographic-asset"]);
741
797
  printDependencyTree(bomNSData.bomJson, "provides");
742
798
  }
package/bin/repl.js CHANGED
@@ -161,7 +161,7 @@ cdxgenRepl.defineCommand("search", {
161
161
  let dependenciesSearchStr = searchStr;
162
162
  if (!searchStr.includes("~>")) {
163
163
  dependenciesSearchStr = `dependencies[ref ~> /${searchStr}/i or dependsOn ~> /${searchStr}/i or provides ~> /${searchStr}/i]`;
164
- searchStr = `components[group ~> /${searchStr}/i or name ~> /${searchStr}/i or description ~> /${searchStr}/i or publisher ~> /${searchStr}/i or purl ~> /${searchStr}/i]`;
164
+ searchStr = `components[group ~> /${searchStr}/i or name ~> /${searchStr}/i or description ~> /${searchStr}/i or publisher ~> /${searchStr}/i or purl ~> /${searchStr}/i or tags ~> /${searchStr}/i]`;
165
165
  }
166
166
  const expression = jsonata(searchStr);
167
167
  let components = await expression.evaluate(sbom);
package/data/README.md CHANGED
@@ -22,4 +22,5 @@ Contents of data directory and their purpose.
22
22
  | wrapdb-releases.json | Database of all available meson wraps. Generated using contrib/wrapdb.py. |
23
23
  | frameworks-list.json | List of string fragments to categorize components into frameworks |
24
24
  | crypto-oid.json | Peter Gutmann's crypto oid [mapping](https://www.cs.auckland.ac.nz/~pgut001). GPL, BSD, or CC BY license |
25
- | glibc-stdlib.json | Standard libraries that can be filtered out in C++ |
25
+ | glibc-stdlib.json | Standard libraries that can be filtered out in C++ |
26
+ | component-tags.json | List of tags to extract from component description text for easy classification. |
@@ -0,0 +1,325 @@
1
+ {
2
+ "description": {
3
+ "all": [
4
+ "sql",
5
+ "xml",
6
+ "web",
7
+ "security",
8
+ "database",
9
+ "json",
10
+ "yaml",
11
+ "validation",
12
+ "sanitization",
13
+ "cloud",
14
+ "iam",
15
+ "auth",
16
+ "middleware",
17
+ "serialization",
18
+ "event",
19
+ "stream",
20
+ "rpc",
21
+ "socket",
22
+ "proto",
23
+ "resource",
24
+ "sensitive",
25
+ "template",
26
+ "log",
27
+ "logging",
28
+ "service",
29
+ "api",
30
+ "slf4j",
31
+ "parse",
32
+ "emit",
33
+ "jdbc",
34
+ "connect",
35
+ "pool",
36
+ "beans",
37
+ "transaction",
38
+ "mysql",
39
+ "postgres",
40
+ "oracle",
41
+ "mongo",
42
+ "redis",
43
+ "splunk",
44
+ "stripe",
45
+ "payment",
46
+ "finance",
47
+ "currency",
48
+ "coin",
49
+ "monero",
50
+ "ssl",
51
+ "traffic",
52
+ "mvc",
53
+ "html",
54
+ "escape",
55
+ "unescape",
56
+ "rest",
57
+ "tomcat",
58
+ "hibernate",
59
+ "orm",
60
+ "aop",
61
+ "jwt",
62
+ "saml",
63
+ "token",
64
+ "tls",
65
+ "codec",
66
+ "cron",
67
+ "crypto",
68
+ "jce",
69
+ "certificate",
70
+ "developer",
71
+ "tools",
72
+ "autoconfigure",
73
+ "test",
74
+ "jsonpath",
75
+ "bytecode",
76
+ "mock",
77
+ "injection",
78
+ "comparators",
79
+ "transform",
80
+ "encode",
81
+ "decode",
82
+ "ldap",
83
+ "owasp",
84
+ "fileupload",
85
+ "beanshell",
86
+ "spel",
87
+ "mail",
88
+ "apacheds",
89
+ "jndi",
90
+ "ldif",
91
+ "jdbm",
92
+ "kerberos",
93
+ "oidc",
94
+ "oauth2",
95
+ "cli",
96
+ "binary",
97
+ "ml",
98
+ "ai",
99
+ "azure",
100
+ "gcp",
101
+ "terraform",
102
+ "redis",
103
+ "valkey",
104
+ "lint",
105
+ "bundle",
106
+ "object-persistence",
107
+ "text-to-image",
108
+ "translat",
109
+ "object-detect",
110
+ "mvc",
111
+ "framework",
112
+ "graph",
113
+ "templates",
114
+ "fastjson",
115
+ "simd",
116
+ "event-driven",
117
+ "productivity",
118
+ "typesafe",
119
+ "projections",
120
+ "performance",
121
+ "plugins",
122
+ "non-block",
123
+ "microsoft"
124
+ ]
125
+ },
126
+ "properties": {
127
+ "all": [
128
+ "sql",
129
+ "http",
130
+ "xml",
131
+ "cloud",
132
+ "middleware",
133
+ "framework",
134
+ "bluetooth",
135
+ "wifi",
136
+ "wireless",
137
+ "driver",
138
+ "graphics",
139
+ "firmware",
140
+ "gyroscope",
141
+ "accelerometer",
142
+ "mobile",
143
+ "network",
144
+ "battery",
145
+ "matrix",
146
+ "thunderbolt",
147
+ "crypto",
148
+ "algorithm",
149
+ "encrypt",
150
+ "decrypt",
151
+ "registry",
152
+ "payment",
153
+ "stripe",
154
+ "apple-pay",
155
+ "icloud"
156
+ ],
157
+ "obom": [
158
+ "windows_drivers",
159
+ "windows_patches",
160
+ "windows_programs",
161
+ "processor",
162
+ "services_snapshot",
163
+ "apt_sources",
164
+ "behavioral_reverse_shell",
165
+ "certificates",
166
+ "chrome_extensions",
167
+ "crontab_snapshot",
168
+ "deb_packages",
169
+ "docker_container_ports",
170
+ "docker_containers",
171
+ "docker_networks",
172
+ "docker_volumes",
173
+ "etc_hosts",
174
+ "firefox_addons",
175
+ "vscode_extensions",
176
+ "homebrew_packages",
177
+ "installed_applications",
178
+ "interface_addresses",
179
+ "kernel_info",
180
+ "kernel_integrity",
181
+ "kernel_modules",
182
+ "ld_preload",
183
+ "listening_ports",
184
+ "os_version",
185
+ "pipes",
186
+ "pipes_snapshot",
187
+ "portage_packages",
188
+ "process_events",
189
+ "processes",
190
+ "python_packages",
191
+ "rpm_packages",
192
+ "scheduled_tasks",
193
+ "services_snapshot",
194
+ "startup_items",
195
+ "system_info_snapshot",
196
+ "windows_drivers",
197
+ "windows_patches",
198
+ "windows_programs",
199
+ "windows_shared_resources",
200
+ "yum_sources",
201
+ "appcompat_shims",
202
+ "browser_plugins",
203
+ "certificates",
204
+ "chocolatey_packages",
205
+ "chrome_extensions",
206
+ "etc_hosts",
207
+ "firefox_addons",
208
+ "ie_extensions",
209
+ "kernel_info",
210
+ "npm_packages",
211
+ "opera_extensions",
212
+ "pipes_snapshot",
213
+ "process_open_sockets",
214
+ "safari_extensions",
215
+ "scheduled_tasks",
216
+ "services_snapshot",
217
+ "startup_items",
218
+ "routes",
219
+ "system_info_snapshot",
220
+ "win_version",
221
+ "windows_firewall_rules",
222
+ "windows_optional_features",
223
+ "windows_programs",
224
+ "windows_shared_resources",
225
+ "windows_update_history",
226
+ "wmi_cli_event_consumers",
227
+ "wmi_cli_event_consumers_snapshot",
228
+ "wmi_event_filters",
229
+ "wmi_filter_consumer_binding"
230
+ ]
231
+ },
232
+ "name": {
233
+ "sbom": [
234
+ { "test": ["(junit|xmlunit|testng|chai|mocha|jest|test4j)"] },
235
+ {
236
+ "security": ["(boringssl|openssl|libressl|libssl|gnutls|jose|keyutils)"]
237
+ },
238
+ { "native": ["(ffi|native)"] },
239
+ { "parse": ["(parser)"] },
240
+ { "transform": ["(transformer)"] }
241
+ ],
242
+ "obom": [
243
+ {
244
+ "devel": [
245
+ "-(dev|devel|headers|sdk|libs|extension|headers+x86|headers+x64|headers+arm64)$",
246
+ "^(git)[-]?",
247
+ "^(sdk|windows+sdk)"
248
+ ]
249
+ },
250
+ {
251
+ "bin": [
252
+ "(-bin|redistributable|clickonce|bootstrappermsi|bootstrappermsires|clickoncesigntoolmsi|codecoveragemsi|msires|sharedmsi|x64msi|arm64msi|sharedmsi|x64vmsi|filehandler_amd64|filehandler_x86|protocolhandlermsi|interopmsi|interopx64msi|shellmsires|shellx64msi)$"
253
+ ]
254
+ },
255
+ { "kernel": ["^(linux|kernel|os-image)"] },
256
+ {
257
+ "security": [
258
+ "(selinux|apparmor|security|boringssl|openssl|libressl|gnutls|jose|keyutils|passwd|libssl|libaudit|gcrypt|libpam|libseccomp)"
259
+ ]
260
+ },
261
+ {
262
+ "container": [
263
+ "(container|podman|docker|runc|nerdctl|crun|libvirt|qemu)"
264
+ ]
265
+ },
266
+ {
267
+ "build": [
268
+ "(cpp|fortran|gcc|make|meson|bazel|maven|gradle|sbt|ant|gdb|boost|compiler|kotlin|cargo|rustc|llvm|libstdc)"
269
+ ]
270
+ },
271
+ {
272
+ "network": [
273
+ "(tailscale|wireguard|openvpn|dns|cockpit|cups|dhcp|network|iproute|iptables|mosh|netavark|openssh|rsync|tcpdump|libssh)"
274
+ ]
275
+ },
276
+ { "webserver": ["(httpd|http2|tomcat|jboss)"] },
277
+ {
278
+ "crypto": [
279
+ "(crypt|gpg|keys|certificates|gnupg|certifi|pubkey|keyutils|nss|keyring)"
280
+ ]
281
+ },
282
+ { "repository": ["(-repos|-release|ostree|appstream)"] },
283
+ { "shell": ["(bash|zsh|csh|fish|binsh|dash|oilsh)"] },
284
+ { "bluetooth": ["(bluez|bluetooth)"] },
285
+ { "sound": ["(alsa|pulseaudio|wireplumber|flac|codecs|ldac|sound)"] },
286
+ {
287
+ "compression": [
288
+ "(brotli|xz-utils|zstd|lz4|zlib|bz2|lzma5|bzip2|libarchive)",
289
+ "(tar|zip|webp|zchunk)$"
290
+ ]
291
+ },
292
+ {
293
+ "runtime": [
294
+ "(perl|lua|php|python|ruby|dotnet|java|swift|runtime|glibc|libc6|musl|wasm|\\.net|asp\\.net|node.js|node|groovy)"
295
+ ]
296
+ },
297
+ { "editor": ["(vim|emacs|nano|hexedit)"] },
298
+ { "xml": ["(xml|expat)"] },
299
+ { "boot": ["(grub|systemd-boot|syslinux|init-system|sysvinit)"] },
300
+ {
301
+ "gui": [
302
+ "(wayland|xorg|X11|mesa|vulkan|tk|wkhtmltox|electron|Xrender|glib2)"
303
+ ]
304
+ },
305
+ {
306
+ "package": [
307
+ "(rpm|dnf|yum|apt|zypper|apk|conda|dpkg|dnf5)$",
308
+ "^(conda_package_|conda-package-|libapt|dnf5-|libdnf5)"
309
+ ]
310
+ },
311
+ {
312
+ "browser": [
313
+ "^(edge)",
314
+ "(firefox|chrome|opera|brave|mullvad|tor|chromium)",
315
+ "(microsoft+edge|microsoft+edge+webview2|microsoft+html)"
316
+ ]
317
+ },
318
+ {
319
+ "chat": ["(webex|teams|slack|discord|vesktop|matrix|signal|whatsapp)"]
320
+ },
321
+ { "logging": ["(log4j|logging|slf4j)"] },
322
+ { "root": ["^(sudo|systemd|pam|shadow)$"] }
323
+ ]
324
+ }
325
+ }
@@ -17,6 +17,7 @@ import {
17
17
  getMavenCommand,
18
18
  getTimestamp,
19
19
  } from "../helpers/utils.js";
20
+ import { postProcess } from "../stages/postgen/postgen.js";
20
21
  import { createSemanticsSlices } from "./swiftsem.js";
21
22
 
22
23
  const DB_NAME = "evinser.db";
@@ -1179,6 +1180,8 @@ export const createEvinseFile = (sliceArtefacts, options) => {
1179
1180
  const evinseOutFile = options.output;
1180
1181
  const bomJson = JSON.parse(fs.readFileSync(bomFile, "utf8"));
1181
1182
  const components = bomJson.components || [];
1183
+ // Clear existing annotations
1184
+ bomJson.annotations = [];
1182
1185
  let occEvidencePresent = false;
1183
1186
  let csEvidencePresent = false;
1184
1187
  let servicesPresent = false;
@@ -1261,9 +1264,6 @@ export const createEvinseFile = (sliceArtefacts, options) => {
1261
1264
  bomJson.dependencies = newDependencies;
1262
1265
  }
1263
1266
  if (options.annotate) {
1264
- if (!bomJson.annotations) {
1265
- bomJson.annotations = [];
1266
- }
1267
1267
  if (usagesSlicesFile && fs.existsSync(usagesSlicesFile)) {
1268
1268
  bomJson.annotations.push({
1269
1269
  subjects: [bomJson.serialNumber],
@@ -1294,7 +1294,11 @@ export const createEvinseFile = (sliceArtefacts, options) => {
1294
1294
  // Set the current timestamp to indicate this is newer
1295
1295
  bomJson.metadata.timestamp = getTimestamp();
1296
1296
  delete bomJson.signature;
1297
- fs.writeFileSync(evinseOutFile, JSON.stringify(bomJson, null, null));
1297
+ const bomNSData = postProcess({ bomJson }, options);
1298
+ fs.writeFileSync(
1299
+ evinseOutFile,
1300
+ JSON.stringify(bomNSData.bomJson, null, null),
1301
+ );
1298
1302
  if (occEvidencePresent || csEvidencePresent || servicesPresent) {
1299
1303
  console.log(evinseOutFile, "created successfully.");
1300
1304
  } else {
@@ -1305,7 +1309,8 @@ export const createEvinseFile = (sliceArtefacts, options) => {
1305
1309
  if (tempDir?.startsWith(tmpdir())) {
1306
1310
  fs.rmSync(tempDir, { recursive: true, force: true });
1307
1311
  }
1308
- return bomJson;
1312
+ // Redo post processing with evinse data
1313
+ return bomNSData?.bomJson;
1309
1314
  };
1310
1315
 
1311
1316
  /**
@@ -36,12 +36,13 @@ export function printTable(
36
36
  columnDefault: {
37
37
  width: 30,
38
38
  },
39
- columnCount: 4,
39
+ columnCount: 5,
40
40
  columns: [
41
41
  { width: 25 },
42
42
  { width: 35 },
43
43
  { width: 25, alignment: "right" },
44
44
  { width: 15 },
45
+ { width: 25 },
45
46
  ],
46
47
  };
47
48
  const stream = createStream(config);
@@ -52,6 +53,7 @@ export function printTable(
52
53
  "Name",
53
54
  filterTypes?.includes("cryptographic-asset") ? "Version / oid" : "Version",
54
55
  "Scope",
56
+ "Tags",
55
57
  ]);
56
58
  for (const comp of bomJson.components) {
57
59
  if (filterTypes && !filterTypes.includes(comp.type)) {
@@ -63,6 +65,7 @@ export function printTable(
63
65
  comp.name,
64
66
  `\x1b[1;35m${comp.cryptoProperties?.oid || ""}\x1b[0m`,
65
67
  comp.scope || "",
68
+ (comp.tags || []).join(", "),
66
69
  ]);
67
70
  } else {
68
71
  stream.write([
@@ -70,6 +73,7 @@ export function printTable(
70
73
  highlightStr(comp.name, highlight),
71
74
  `\x1b[1;35m${comp.version || ""}\x1b[0m`,
72
75
  comp.scope || "",
76
+ (comp.tags || []).join(", "),
73
77
  ]);
74
78
  }
75
79
  }
@@ -98,16 +102,17 @@ export function printOSTable(bomJson) {
98
102
  columnDefault: {
99
103
  width: 50,
100
104
  },
101
- columnCount: 3,
102
- columns: [{ width: 20 }, { width: 40 }, { width: 50 }],
105
+ columnCount: 4,
106
+ columns: [{ width: 20 }, { width: 40 }, { width: 50 }, { width: 25 }],
103
107
  };
104
108
  const stream = createStream(config);
105
- stream.write(["Type", "Title", "Properties"]);
109
+ stream.write(["Type", "Title", "Properties", "Tags"]);
106
110
  for (const comp of bomJson.components) {
107
111
  stream.write([
108
112
  comp.type,
109
113
  `\x1b[1;35m${comp.name.replace(/\+/g, " ").replace(/--/g, "::")}\x1b[0m`,
110
114
  formatProps(comp.properties || []),
115
+ (comp.tags || []).join(", "),
111
116
  ]);
112
117
  }
113
118
  console.log();
@@ -436,17 +441,23 @@ export function printSummary(bomJson) {
436
441
  alignment: "center",
437
442
  content: "BOM summary",
438
443
  },
444
+ columns: [{ wrapWord: true, width: 100 }],
439
445
  };
440
446
  const metadataProperties = bomJson?.metadata?.properties;
441
447
  if (!metadataProperties) {
442
448
  return;
443
449
  }
444
- const tools = bomJson?.metadata?.tools?.components;
445
450
  let message = "";
446
451
  let bomPkgTypes = [];
447
452
  let bomPkgNamespaces = [];
453
+ // Print any annotations found
454
+ const annotations = bomJson?.annotations || [];
455
+ for (const annot of annotations) {
456
+ message = `${message}\n${annot.text}`;
457
+ }
458
+ const tools = bomJson?.metadata?.tools?.components;
448
459
  if (tools) {
449
- message = "** Generator Tools **";
460
+ message = `${message}\n\n** Generator Tools **`;
450
461
  for (const atool of tools) {
451
462
  if (atool.name && atool.version) {
452
463
  message = `${message}\n${atool.name} (${atool.version})`;