@herodevs/cli 1.0.0-beta.1 → 1.1.0-beta.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 +343 -21
- package/bin/dev.js +0 -1
- package/dist/commands/report/committers.d.ts +3 -2
- package/dist/commands/report/committers.js +40 -15
- package/dist/commands/report/purls.d.ts +4 -2
- package/dist/commands/report/purls.js +19 -14
- package/dist/commands/scan/eol.js +3 -0
- package/dist/commands/scan/sbom.d.ts +3 -1
- package/dist/commands/scan/sbom.js +46 -14
- package/dist/hooks/prerun.js +8 -0
- package/dist/service/eol/cdx.svc.d.ts +52 -0
- package/dist/service/eol/cdx.svc.js +57 -62
- package/dist/service/eol/eol.svc.js +3 -3
- package/dist/service/eol/sbom.worker.d.ts +1 -0
- package/dist/service/eol/sbom.worker.js +25 -0
- package/dist/service/log.svc.d.ts +5 -8
- package/dist/service/log.svc.js +5 -18
- package/dist/service/nes/nes.svc.js +3 -3
- package/dist/service/purls.svc.js +1 -1
- package/package.json +13 -11
- package/dist/hooks/init/update.d.ts +0 -2
- package/dist/hooks/init/update.js +0 -5
- package/dist/hooks/prerun/CommandContextHook.js +0 -8
- /package/dist/hooks/{prerun/CommandContextHook.d.ts → prerun.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ $ npm install -g @herodevs/cli
|
|
|
16
16
|
$ hd COMMAND
|
|
17
17
|
running command...
|
|
18
18
|
$ hd (--version)
|
|
19
|
-
@herodevs/cli/1.
|
|
19
|
+
@herodevs/cli/1.1.0-beta.1 linux-x64 node-v22.14.0
|
|
20
20
|
$ hd --help [COMMAND]
|
|
21
21
|
USAGE
|
|
22
22
|
$ hd COMMAND
|
|
@@ -25,24 +25,344 @@ USAGE
|
|
|
25
25
|
<!-- usagestop -->
|
|
26
26
|
## Commands
|
|
27
27
|
<!-- commands -->
|
|
28
|
+
* [`hd help [COMMAND]`](#hd-help-command)
|
|
29
|
+
* [`hd plugins`](#hd-plugins)
|
|
30
|
+
* [`hd plugins add PLUGIN`](#hd-plugins-add-plugin)
|
|
31
|
+
* [`hd plugins:inspect PLUGIN...`](#hd-pluginsinspect-plugin)
|
|
32
|
+
* [`hd plugins install PLUGIN`](#hd-plugins-install-plugin)
|
|
33
|
+
* [`hd plugins link PATH`](#hd-plugins-link-path)
|
|
34
|
+
* [`hd plugins remove [PLUGIN]`](#hd-plugins-remove-plugin)
|
|
35
|
+
* [`hd plugins reset`](#hd-plugins-reset)
|
|
36
|
+
* [`hd plugins uninstall [PLUGIN]`](#hd-plugins-uninstall-plugin)
|
|
37
|
+
* [`hd plugins unlink [PLUGIN]`](#hd-plugins-unlink-plugin)
|
|
38
|
+
* [`hd plugins update`](#hd-plugins-update)
|
|
28
39
|
* [`hd report committers`](#hd-report-committers)
|
|
29
40
|
* [`hd report purls`](#hd-report-purls)
|
|
30
41
|
* [`hd scan eol`](#hd-scan-eol)
|
|
31
42
|
* [`hd scan sbom`](#hd-scan-sbom)
|
|
32
43
|
|
|
44
|
+
## `hd help [COMMAND]`
|
|
45
|
+
|
|
46
|
+
Display help for hd.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
USAGE
|
|
50
|
+
$ hd help [COMMAND...] [-n]
|
|
51
|
+
|
|
52
|
+
ARGUMENTS
|
|
53
|
+
COMMAND... Command to show help for.
|
|
54
|
+
|
|
55
|
+
FLAGS
|
|
56
|
+
-n, --nested-commands Include all nested commands in the output.
|
|
57
|
+
|
|
58
|
+
DESCRIPTION
|
|
59
|
+
Display help for hd.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v6.2.27/src/commands/help.ts)_
|
|
63
|
+
|
|
64
|
+
## `hd plugins`
|
|
65
|
+
|
|
66
|
+
List installed plugins.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
USAGE
|
|
70
|
+
$ hd plugins [--json] [--core]
|
|
71
|
+
|
|
72
|
+
FLAGS
|
|
73
|
+
--core Show core plugins.
|
|
74
|
+
|
|
75
|
+
GLOBAL FLAGS
|
|
76
|
+
--json Format output as json.
|
|
77
|
+
|
|
78
|
+
DESCRIPTION
|
|
79
|
+
List installed plugins.
|
|
80
|
+
|
|
81
|
+
EXAMPLES
|
|
82
|
+
$ hd plugins
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.36/src/commands/plugins/index.ts)_
|
|
86
|
+
|
|
87
|
+
## `hd plugins add PLUGIN`
|
|
88
|
+
|
|
89
|
+
Installs a plugin into hd.
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
USAGE
|
|
93
|
+
$ hd plugins add PLUGIN... [--json] [-f] [-h] [-s | -v]
|
|
94
|
+
|
|
95
|
+
ARGUMENTS
|
|
96
|
+
PLUGIN... Plugin to install.
|
|
97
|
+
|
|
98
|
+
FLAGS
|
|
99
|
+
-f, --force Force npm to fetch remote resources even if a local copy exists on disk.
|
|
100
|
+
-h, --help Show CLI help.
|
|
101
|
+
-s, --silent Silences npm output.
|
|
102
|
+
-v, --verbose Show verbose npm output.
|
|
103
|
+
|
|
104
|
+
GLOBAL FLAGS
|
|
105
|
+
--json Format output as json.
|
|
106
|
+
|
|
107
|
+
DESCRIPTION
|
|
108
|
+
Installs a plugin into hd.
|
|
109
|
+
|
|
110
|
+
Uses npm to install plugins.
|
|
111
|
+
|
|
112
|
+
Installation of a user-installed plugin will override a core plugin.
|
|
113
|
+
|
|
114
|
+
Use the HD_NPM_LOG_LEVEL environment variable to set the npm loglevel.
|
|
115
|
+
Use the HD_NPM_REGISTRY environment variable to set the npm registry.
|
|
116
|
+
|
|
117
|
+
ALIASES
|
|
118
|
+
$ hd plugins add
|
|
119
|
+
|
|
120
|
+
EXAMPLES
|
|
121
|
+
Install a plugin from npm registry.
|
|
122
|
+
|
|
123
|
+
$ hd plugins add myplugin
|
|
124
|
+
|
|
125
|
+
Install a plugin from a github url.
|
|
126
|
+
|
|
127
|
+
$ hd plugins add https://github.com/someuser/someplugin
|
|
128
|
+
|
|
129
|
+
Install a plugin from a github slug.
|
|
130
|
+
|
|
131
|
+
$ hd plugins add someuser/someplugin
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## `hd plugins:inspect PLUGIN...`
|
|
135
|
+
|
|
136
|
+
Displays installation properties of a plugin.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
USAGE
|
|
140
|
+
$ hd plugins inspect PLUGIN...
|
|
141
|
+
|
|
142
|
+
ARGUMENTS
|
|
143
|
+
PLUGIN... [default: .] Plugin to inspect.
|
|
144
|
+
|
|
145
|
+
FLAGS
|
|
146
|
+
-h, --help Show CLI help.
|
|
147
|
+
-v, --verbose
|
|
148
|
+
|
|
149
|
+
GLOBAL FLAGS
|
|
150
|
+
--json Format output as json.
|
|
151
|
+
|
|
152
|
+
DESCRIPTION
|
|
153
|
+
Displays installation properties of a plugin.
|
|
154
|
+
|
|
155
|
+
EXAMPLES
|
|
156
|
+
$ hd plugins inspect myplugin
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.36/src/commands/plugins/inspect.ts)_
|
|
160
|
+
|
|
161
|
+
## `hd plugins install PLUGIN`
|
|
162
|
+
|
|
163
|
+
Installs a plugin into hd.
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
USAGE
|
|
167
|
+
$ hd plugins install PLUGIN... [--json] [-f] [-h] [-s | -v]
|
|
168
|
+
|
|
169
|
+
ARGUMENTS
|
|
170
|
+
PLUGIN... Plugin to install.
|
|
171
|
+
|
|
172
|
+
FLAGS
|
|
173
|
+
-f, --force Force npm to fetch remote resources even if a local copy exists on disk.
|
|
174
|
+
-h, --help Show CLI help.
|
|
175
|
+
-s, --silent Silences npm output.
|
|
176
|
+
-v, --verbose Show verbose npm output.
|
|
177
|
+
|
|
178
|
+
GLOBAL FLAGS
|
|
179
|
+
--json Format output as json.
|
|
180
|
+
|
|
181
|
+
DESCRIPTION
|
|
182
|
+
Installs a plugin into hd.
|
|
183
|
+
|
|
184
|
+
Uses npm to install plugins.
|
|
185
|
+
|
|
186
|
+
Installation of a user-installed plugin will override a core plugin.
|
|
187
|
+
|
|
188
|
+
Use the HD_NPM_LOG_LEVEL environment variable to set the npm loglevel.
|
|
189
|
+
Use the HD_NPM_REGISTRY environment variable to set the npm registry.
|
|
190
|
+
|
|
191
|
+
ALIASES
|
|
192
|
+
$ hd plugins add
|
|
193
|
+
|
|
194
|
+
EXAMPLES
|
|
195
|
+
Install a plugin from npm registry.
|
|
196
|
+
|
|
197
|
+
$ hd plugins install myplugin
|
|
198
|
+
|
|
199
|
+
Install a plugin from a github url.
|
|
200
|
+
|
|
201
|
+
$ hd plugins install https://github.com/someuser/someplugin
|
|
202
|
+
|
|
203
|
+
Install a plugin from a github slug.
|
|
204
|
+
|
|
205
|
+
$ hd plugins install someuser/someplugin
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.36/src/commands/plugins/install.ts)_
|
|
209
|
+
|
|
210
|
+
## `hd plugins link PATH`
|
|
211
|
+
|
|
212
|
+
Links a plugin into the CLI for development.
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
USAGE
|
|
216
|
+
$ hd plugins link PATH [-h] [--install] [-v]
|
|
217
|
+
|
|
218
|
+
ARGUMENTS
|
|
219
|
+
PATH [default: .] path to plugin
|
|
220
|
+
|
|
221
|
+
FLAGS
|
|
222
|
+
-h, --help Show CLI help.
|
|
223
|
+
-v, --verbose
|
|
224
|
+
--[no-]install Install dependencies after linking the plugin.
|
|
225
|
+
|
|
226
|
+
DESCRIPTION
|
|
227
|
+
Links a plugin into the CLI for development.
|
|
228
|
+
|
|
229
|
+
Installation of a linked plugin will override a user-installed or core plugin.
|
|
230
|
+
|
|
231
|
+
e.g. If you have a user-installed or core plugin that has a 'hello' command, installing a linked plugin with a 'hello'
|
|
232
|
+
command will override the user-installed or core plugin implementation. This is useful for development work.
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
EXAMPLES
|
|
236
|
+
$ hd plugins link myplugin
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.36/src/commands/plugins/link.ts)_
|
|
240
|
+
|
|
241
|
+
## `hd plugins remove [PLUGIN]`
|
|
242
|
+
|
|
243
|
+
Removes a plugin from the CLI.
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
USAGE
|
|
247
|
+
$ hd plugins remove [PLUGIN...] [-h] [-v]
|
|
248
|
+
|
|
249
|
+
ARGUMENTS
|
|
250
|
+
PLUGIN... plugin to uninstall
|
|
251
|
+
|
|
252
|
+
FLAGS
|
|
253
|
+
-h, --help Show CLI help.
|
|
254
|
+
-v, --verbose
|
|
255
|
+
|
|
256
|
+
DESCRIPTION
|
|
257
|
+
Removes a plugin from the CLI.
|
|
258
|
+
|
|
259
|
+
ALIASES
|
|
260
|
+
$ hd plugins unlink
|
|
261
|
+
$ hd plugins remove
|
|
262
|
+
|
|
263
|
+
EXAMPLES
|
|
264
|
+
$ hd plugins remove myplugin
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## `hd plugins reset`
|
|
268
|
+
|
|
269
|
+
Remove all user-installed and linked plugins.
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
USAGE
|
|
273
|
+
$ hd plugins reset [--hard] [--reinstall]
|
|
274
|
+
|
|
275
|
+
FLAGS
|
|
276
|
+
--hard Delete node_modules and package manager related files in addition to uninstalling plugins.
|
|
277
|
+
--reinstall Reinstall all plugins after uninstalling.
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.36/src/commands/plugins/reset.ts)_
|
|
281
|
+
|
|
282
|
+
## `hd plugins uninstall [PLUGIN]`
|
|
283
|
+
|
|
284
|
+
Removes a plugin from the CLI.
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
USAGE
|
|
288
|
+
$ hd plugins uninstall [PLUGIN...] [-h] [-v]
|
|
289
|
+
|
|
290
|
+
ARGUMENTS
|
|
291
|
+
PLUGIN... plugin to uninstall
|
|
292
|
+
|
|
293
|
+
FLAGS
|
|
294
|
+
-h, --help Show CLI help.
|
|
295
|
+
-v, --verbose
|
|
296
|
+
|
|
297
|
+
DESCRIPTION
|
|
298
|
+
Removes a plugin from the CLI.
|
|
299
|
+
|
|
300
|
+
ALIASES
|
|
301
|
+
$ hd plugins unlink
|
|
302
|
+
$ hd plugins remove
|
|
303
|
+
|
|
304
|
+
EXAMPLES
|
|
305
|
+
$ hd plugins uninstall myplugin
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.36/src/commands/plugins/uninstall.ts)_
|
|
309
|
+
|
|
310
|
+
## `hd plugins unlink [PLUGIN]`
|
|
311
|
+
|
|
312
|
+
Removes a plugin from the CLI.
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
USAGE
|
|
316
|
+
$ hd plugins unlink [PLUGIN...] [-h] [-v]
|
|
317
|
+
|
|
318
|
+
ARGUMENTS
|
|
319
|
+
PLUGIN... plugin to uninstall
|
|
320
|
+
|
|
321
|
+
FLAGS
|
|
322
|
+
-h, --help Show CLI help.
|
|
323
|
+
-v, --verbose
|
|
324
|
+
|
|
325
|
+
DESCRIPTION
|
|
326
|
+
Removes a plugin from the CLI.
|
|
327
|
+
|
|
328
|
+
ALIASES
|
|
329
|
+
$ hd plugins unlink
|
|
330
|
+
$ hd plugins remove
|
|
331
|
+
|
|
332
|
+
EXAMPLES
|
|
333
|
+
$ hd plugins unlink myplugin
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## `hd plugins update`
|
|
337
|
+
|
|
338
|
+
Update installed plugins.
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
USAGE
|
|
342
|
+
$ hd plugins update [-h] [-v]
|
|
343
|
+
|
|
344
|
+
FLAGS
|
|
345
|
+
-h, --help Show CLI help.
|
|
346
|
+
-v, --verbose
|
|
347
|
+
|
|
348
|
+
DESCRIPTION
|
|
349
|
+
Update installed plugins.
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
_See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.36/src/commands/plugins/update.ts)_
|
|
353
|
+
|
|
33
354
|
## `hd report committers`
|
|
34
355
|
|
|
35
356
|
Generate report of committers to a git repository
|
|
36
357
|
|
|
37
358
|
```
|
|
38
359
|
USAGE
|
|
39
|
-
$ hd report committers [--json] [-m <value>] [-
|
|
360
|
+
$ hd report committers [--json] [-m <value>] [-c] [-s]
|
|
40
361
|
|
|
41
362
|
FLAGS
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
-s, --save Save the committers report as nes.committers.<output>
|
|
363
|
+
-c, --csv Output in CSV format
|
|
364
|
+
-m, --months=<value> [default: 12] The number of months of git history to review
|
|
365
|
+
-s, --save Save the committers report as nes.committers.<output>
|
|
46
366
|
|
|
47
367
|
GLOBAL FLAGS
|
|
48
368
|
--json Format output as json.
|
|
@@ -53,14 +373,14 @@ DESCRIPTION
|
|
|
53
373
|
EXAMPLES
|
|
54
374
|
$ hd report committers
|
|
55
375
|
|
|
56
|
-
$ hd report committers
|
|
376
|
+
$ hd report committers --csv -s
|
|
57
377
|
|
|
58
|
-
$ hd report committers --
|
|
378
|
+
$ hd report committers --json
|
|
59
379
|
|
|
60
|
-
$ hd report committers --
|
|
380
|
+
$ hd report committers --csv
|
|
61
381
|
```
|
|
62
382
|
|
|
63
|
-
_See code: [src/commands/report/committers.ts](https://github.com/herodevs/cli/blob/v1.
|
|
383
|
+
_See code: [src/commands/report/committers.ts](https://github.com/herodevs/cli/blob/v1.1.0-beta.1/src/commands/report/committers.ts)_
|
|
64
384
|
|
|
65
385
|
## `hd report purls`
|
|
66
386
|
|
|
@@ -68,14 +388,13 @@ Generate a list of purls from a sbom
|
|
|
68
388
|
|
|
69
389
|
```
|
|
70
390
|
USAGE
|
|
71
|
-
$ hd report purls [--json] [-f <value>] [-d <value>] [-s] [-
|
|
391
|
+
$ hd report purls [--json] [-f <value>] [-d <value>] [-s] [-c]
|
|
72
392
|
|
|
73
393
|
FLAGS
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
|
|
78
|
-
-s, --save Save the list of purls as nes.purls.<output>
|
|
394
|
+
-c, --csv Save output in CSV format (only applies when using --save)
|
|
395
|
+
-d, --dir=<value> The directory to scan in order to create a cyclonedx sbom
|
|
396
|
+
-f, --file=<value> The file path of an existing cyclonedx sbom to scan for EOL
|
|
397
|
+
-s, --save Save the list of purls as nes.purls.<output>
|
|
79
398
|
|
|
80
399
|
GLOBAL FLAGS
|
|
81
400
|
--json Format output as json.
|
|
@@ -84,16 +403,18 @@ DESCRIPTION
|
|
|
84
403
|
Generate a list of purls from a sbom
|
|
85
404
|
|
|
86
405
|
EXAMPLES
|
|
406
|
+
$ hd report purls --json -s
|
|
407
|
+
|
|
87
408
|
$ hd report purls --dir=./my-project
|
|
88
409
|
|
|
89
410
|
$ hd report purls --file=path/to/sbom.json
|
|
90
411
|
|
|
91
412
|
$ hd report purls --dir=./my-project --save
|
|
92
413
|
|
|
93
|
-
$ hd report purls --save --
|
|
414
|
+
$ hd report purls --save --csv
|
|
94
415
|
```
|
|
95
416
|
|
|
96
|
-
_See code: [src/commands/report/purls.ts](https://github.com/herodevs/cli/blob/v1.
|
|
417
|
+
_See code: [src/commands/report/purls.ts](https://github.com/herodevs/cli/blob/v1.1.0-beta.1/src/commands/report/purls.ts)_
|
|
97
418
|
|
|
98
419
|
## `hd scan eol`
|
|
99
420
|
|
|
@@ -120,7 +441,7 @@ EXAMPLES
|
|
|
120
441
|
$ hd scan eol --file=path/to/sbom.json
|
|
121
442
|
```
|
|
122
443
|
|
|
123
|
-
_See code: [src/commands/scan/eol.ts](https://github.com/herodevs/cli/blob/v1.
|
|
444
|
+
_See code: [src/commands/scan/eol.ts](https://github.com/herodevs/cli/blob/v1.1.0-beta.1/src/commands/scan/eol.ts)_
|
|
124
445
|
|
|
125
446
|
## `hd scan sbom`
|
|
126
447
|
|
|
@@ -128,9 +449,10 @@ Scan a SBOM for purls
|
|
|
128
449
|
|
|
129
450
|
```
|
|
130
451
|
USAGE
|
|
131
|
-
$ hd scan sbom [--json] [-f <value>] [-d <value>] [-s]
|
|
452
|
+
$ hd scan sbom [--json] [-f <value>] [-d <value>] [-s] [-b]
|
|
132
453
|
|
|
133
454
|
FLAGS
|
|
455
|
+
-b, --background Run the scan in the background
|
|
134
456
|
-d, --dir=<value> The directory to scan in order to create a cyclonedx sbom
|
|
135
457
|
-f, --file=<value> The file path of an existing cyclonedx sbom to scan for EOL
|
|
136
458
|
-s, --save Save the generated SBOM as nes.sbom.json in the scanned directory
|
|
@@ -147,5 +469,5 @@ EXAMPLES
|
|
|
147
469
|
$ hd scan sbom --file=path/to/sbom.json
|
|
148
470
|
```
|
|
149
471
|
|
|
150
|
-
_See code: [src/commands/scan/sbom.ts](https://github.com/herodevs/cli/blob/v1.
|
|
472
|
+
_See code: [src/commands/scan/sbom.ts](https://github.com/herodevs/cli/blob/v1.1.0-beta.1/src/commands/scan/sbom.ts)_
|
|
151
473
|
<!-- commandsstop -->
|
package/bin/dev.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
+
import { type ReportData } from '../../service/committers.svc.ts';
|
|
2
3
|
export default class Committers extends Command {
|
|
3
4
|
static description: string;
|
|
4
5
|
static enableJsonFlag: boolean;
|
|
5
6
|
static examples: string[];
|
|
6
7
|
static flags: {
|
|
7
8
|
months: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
|
|
9
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
};
|
|
11
|
-
run(): Promise<
|
|
12
|
+
run(): Promise<ReportData | string>;
|
|
12
13
|
/**
|
|
13
14
|
* Generates structured report data
|
|
14
15
|
* @param entries - parsed git log output for commits
|
|
@@ -2,15 +2,15 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { Command, Flags } from '@oclif/core';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { calculateOverallStats,
|
|
5
|
+
import { calculateOverallStats, formatAsCsv, formatAsText, groupCommitsByMonth, parseGitLogOutput, } from "../../service/committers.svc.js";
|
|
6
6
|
export default class Committers extends Command {
|
|
7
7
|
static description = 'Generate report of committers to a git repository';
|
|
8
8
|
static enableJsonFlag = true;
|
|
9
9
|
static examples = [
|
|
10
10
|
'<%= config.bin %> <%= command.id %>',
|
|
11
|
-
'<%= config.bin %> <%= command.id %>
|
|
12
|
-
'<%= config.bin %> <%= command.id %> --
|
|
13
|
-
'<%= config.bin %> <%= command.id %> --
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --csv -s',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
13
|
+
'<%= config.bin %> <%= command.id %> --csv',
|
|
14
14
|
];
|
|
15
15
|
static flags = {
|
|
16
16
|
months: Flags.integer({
|
|
@@ -18,11 +18,10 @@ export default class Committers extends Command {
|
|
|
18
18
|
description: 'The number of months of git history to review',
|
|
19
19
|
default: 12,
|
|
20
20
|
}),
|
|
21
|
-
|
|
22
|
-
char: '
|
|
23
|
-
description: 'Output
|
|
24
|
-
|
|
25
|
-
default: 'text',
|
|
21
|
+
csv: Flags.boolean({
|
|
22
|
+
char: 'c',
|
|
23
|
+
description: 'Output in CSV format',
|
|
24
|
+
default: false,
|
|
26
25
|
}),
|
|
27
26
|
save: Flags.boolean({
|
|
28
27
|
char: 's',
|
|
@@ -32,24 +31,50 @@ export default class Committers extends Command {
|
|
|
32
31
|
};
|
|
33
32
|
async run() {
|
|
34
33
|
const { flags } = await this.parse(Committers);
|
|
35
|
-
const { months,
|
|
34
|
+
const { months, csv, save } = flags;
|
|
35
|
+
const isJson = this.jsonEnabled();
|
|
36
36
|
const sinceDate = `${months} months ago`;
|
|
37
|
+
this.log('Starting committers report with flags: %O', flags);
|
|
37
38
|
try {
|
|
38
39
|
// Generate structured report data
|
|
39
40
|
const entries = this.fetchGitCommitData(sinceDate);
|
|
41
|
+
this.log('Fetched %d commit entries', entries.length);
|
|
40
42
|
const reportData = this.generateReportData(entries);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
// Handle different output scenarios
|
|
44
|
+
if (isJson) {
|
|
45
|
+
// JSON mode
|
|
46
|
+
if (save) {
|
|
47
|
+
fs.writeFileSync(path.resolve('nes.committers.json'), JSON.stringify(reportData, null, 2));
|
|
48
|
+
this.log('Report written to json');
|
|
49
|
+
}
|
|
50
|
+
return reportData;
|
|
51
|
+
}
|
|
52
|
+
if (csv) {
|
|
53
|
+
// CSV mode
|
|
54
|
+
const csvOutput = formatAsCsv(reportData);
|
|
55
|
+
if (save) {
|
|
56
|
+
fs.writeFileSync(path.resolve('nes.committers.csv'), csvOutput);
|
|
57
|
+
this.log('Report written to csv');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.log(csvOutput);
|
|
61
|
+
}
|
|
62
|
+
return csvOutput;
|
|
63
|
+
}
|
|
64
|
+
// Text mode
|
|
65
|
+
const textOutput = formatAsText(reportData);
|
|
43
66
|
if (save) {
|
|
44
|
-
fs.writeFileSync(path.resolve(
|
|
45
|
-
this.log(
|
|
67
|
+
fs.writeFileSync(path.resolve('nes.committers.txt'), textOutput);
|
|
68
|
+
this.log('Report written to txt');
|
|
46
69
|
}
|
|
47
70
|
else {
|
|
48
|
-
this.log(
|
|
71
|
+
this.log(textOutput);
|
|
49
72
|
}
|
|
73
|
+
return textOutput;
|
|
50
74
|
}
|
|
51
75
|
catch (error) {
|
|
52
76
|
this.error(`Failed to generate report: ${error.message}`);
|
|
77
|
+
throw error;
|
|
53
78
|
}
|
|
54
79
|
}
|
|
55
80
|
/**
|
|
@@ -7,7 +7,9 @@ export default class ReportPurls extends Command {
|
|
|
7
7
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
-
|
|
10
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
};
|
|
12
|
-
run(): Promise<
|
|
12
|
+
run(): Promise<{
|
|
13
|
+
purls: string[];
|
|
14
|
+
}>;
|
|
13
15
|
}
|
|
@@ -7,10 +7,11 @@ export default class ReportPurls extends Command {
|
|
|
7
7
|
static description = 'Generate a list of purls from a sbom';
|
|
8
8
|
static enableJsonFlag = true;
|
|
9
9
|
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %> --json -s',
|
|
10
11
|
'<%= config.bin %> <%= command.id %> --dir=./my-project',
|
|
11
12
|
'<%= config.bin %> <%= command.id %> --file=path/to/sbom.json',
|
|
12
13
|
'<%= config.bin %> <%= command.id %> --dir=./my-project --save',
|
|
13
|
-
'<%= config.bin %> <%= command.id %> --save --
|
|
14
|
+
'<%= config.bin %> <%= command.id %> --save --csv',
|
|
14
15
|
];
|
|
15
16
|
static flags = {
|
|
16
17
|
file: Flags.string({
|
|
@@ -26,41 +27,45 @@ export default class ReportPurls extends Command {
|
|
|
26
27
|
default: false,
|
|
27
28
|
description: 'Save the list of purls as nes.purls.<output>',
|
|
28
29
|
}),
|
|
29
|
-
|
|
30
|
-
char: '
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
description: 'The format of the saved file (when using --save)',
|
|
30
|
+
csv: Flags.boolean({
|
|
31
|
+
char: 'c',
|
|
32
|
+
default: false,
|
|
33
|
+
description: 'Save output in CSV format (only applies when using --save)',
|
|
34
34
|
}),
|
|
35
35
|
};
|
|
36
36
|
async run() {
|
|
37
37
|
const { flags } = await this.parse(ReportPurls);
|
|
38
|
-
const { dir: _dirFlag, file: _fileFlag, save,
|
|
39
|
-
// Load the SBOM: Only pass the file, dir, and save flags to SbomScan
|
|
38
|
+
const { dir: _dirFlag, file: _fileFlag, save, csv } = flags;
|
|
40
39
|
const sbomArgs = SbomScan.getSbomArgs(flags);
|
|
41
40
|
const sbomCommand = new SbomScan(sbomArgs, this.config);
|
|
42
41
|
const sbom = await sbomCommand.run();
|
|
43
|
-
|
|
42
|
+
if (!sbom) {
|
|
43
|
+
throw new Error('SBOM not generated');
|
|
44
|
+
}
|
|
44
45
|
const purls = await extractPurls(sbom);
|
|
46
|
+
this.log('Extracted %d purls from SBOM', purls.length);
|
|
45
47
|
ux.action.stop('Scan completed');
|
|
46
48
|
// Print the purls
|
|
47
|
-
this.log('Found purls:');
|
|
48
49
|
for (const purl of purls) {
|
|
49
50
|
this.log(purl);
|
|
50
51
|
}
|
|
51
52
|
// Save if requested
|
|
52
53
|
if (save) {
|
|
53
54
|
try {
|
|
54
|
-
const
|
|
55
|
-
const
|
|
55
|
+
const outputFile = csv && !this.jsonEnabled() ? 'csv' : 'json';
|
|
56
|
+
const outputPath = path.join(_dirFlag || process.cwd(), `nes.purls.${outputFile}`);
|
|
57
|
+
const purlOutput = getPurlOutput(purls, outputFile);
|
|
56
58
|
fs.writeFileSync(outputPath, purlOutput);
|
|
57
|
-
this.log(
|
|
59
|
+
this.log('Purls saved to %s', outputPath);
|
|
58
60
|
}
|
|
59
61
|
catch (error) {
|
|
60
62
|
const errorMessage = error && typeof error === 'object' && 'message' in error ? error.message : 'Unknown error';
|
|
61
63
|
this.warn(`Failed to save purls: ${errorMessage}`);
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
|
-
|
|
66
|
+
// Return wrapped object with metadata
|
|
67
|
+
return {
|
|
68
|
+
purls,
|
|
69
|
+
};
|
|
65
70
|
}
|
|
66
71
|
}
|
|
@@ -32,6 +32,9 @@ export default class ScanEol extends Command {
|
|
|
32
32
|
const sbomArgs = SbomScan.getSbomArgs(flags);
|
|
33
33
|
const sbomCommand = new SbomScan(sbomArgs, this.config);
|
|
34
34
|
const sbom = await sbomCommand.run();
|
|
35
|
+
if (!sbom) {
|
|
36
|
+
throw new Error('SBOM not generated');
|
|
37
|
+
}
|
|
35
38
|
// Scan the SBOM for EOL information
|
|
36
39
|
const { purls, scan } = await scanForEol(sbom);
|
|
37
40
|
ux.action.stop('Scan completed');
|
|
@@ -8,11 +8,13 @@ export default class ScanSbom extends Command {
|
|
|
8
8
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
background: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
};
|
|
12
13
|
static getSbomArgs(flags: Record<string, string>): string[];
|
|
13
14
|
getScanOptions(): {};
|
|
14
|
-
run(): Promise<Sbom>;
|
|
15
|
+
run(): Promise<Sbom | undefined>;
|
|
15
16
|
private _getSbomFromScan;
|
|
17
|
+
private _getSbomInBackground;
|
|
16
18
|
private _getSbomFromFile;
|
|
17
19
|
private _saveSbom;
|
|
18
20
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Command, Flags, ux } from '@oclif/core';
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
3
2
|
import fs from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { Command, Flags, ux } from '@oclif/core';
|
|
4
5
|
import { createSbom, validateIsCycloneDxSbom } from "../../service/eol/eol.svc.js";
|
|
5
6
|
export default class ScanSbom extends Command {
|
|
6
7
|
static description = 'Scan a SBOM for purls';
|
|
@@ -23,9 +24,14 @@ export default class ScanSbom extends Command {
|
|
|
23
24
|
default: false,
|
|
24
25
|
description: 'Save the generated SBOM as nes.sbom.json in the scanned directory',
|
|
25
26
|
}),
|
|
27
|
+
background: Flags.boolean({
|
|
28
|
+
char: 'b',
|
|
29
|
+
default: false,
|
|
30
|
+
description: 'Run the scan in the background',
|
|
31
|
+
}),
|
|
26
32
|
};
|
|
27
33
|
static getSbomArgs(flags) {
|
|
28
|
-
const { dir, file, save } = flags ?? {};
|
|
34
|
+
const { dir, file, save, json, background } = flags ?? {};
|
|
29
35
|
const sbomArgs = [];
|
|
30
36
|
if (file)
|
|
31
37
|
sbomArgs.push('--file', file);
|
|
@@ -33,6 +39,10 @@ export default class ScanSbom extends Command {
|
|
|
33
39
|
sbomArgs.push('--dir', dir);
|
|
34
40
|
if (save)
|
|
35
41
|
sbomArgs.push('--save');
|
|
42
|
+
if (json)
|
|
43
|
+
sbomArgs.push('--json');
|
|
44
|
+
if (background)
|
|
45
|
+
sbomArgs.push('--background');
|
|
36
46
|
return sbomArgs;
|
|
37
47
|
}
|
|
38
48
|
getScanOptions() {
|
|
@@ -41,26 +51,31 @@ export default class ScanSbom extends Command {
|
|
|
41
51
|
}
|
|
42
52
|
async run() {
|
|
43
53
|
const { flags } = await this.parse(ScanSbom);
|
|
44
|
-
const { dir
|
|
54
|
+
const { dir, save, file, background } = flags;
|
|
45
55
|
// Validate that exactly one of --file or --dir is provided
|
|
46
|
-
if (
|
|
56
|
+
if (file && dir) {
|
|
47
57
|
throw new Error('Cannot specify both --file and --dir flags. Please use one or the other.');
|
|
48
58
|
}
|
|
49
59
|
let sbom;
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
const path = dir || process.cwd();
|
|
61
|
+
if (file) {
|
|
62
|
+
sbom = this._getSbomFromFile(file);
|
|
63
|
+
}
|
|
64
|
+
else if (background) {
|
|
65
|
+
this._getSbomInBackground(path);
|
|
66
|
+
this.log(`The scan is running in the background. The file will be saved at ${path}/nes.sbom.json`);
|
|
67
|
+
return;
|
|
52
68
|
}
|
|
53
69
|
else {
|
|
54
|
-
|
|
55
|
-
sbom = await this._getSbomFromScan(_dir);
|
|
70
|
+
sbom = await this._getSbomFromScan(path);
|
|
56
71
|
if (save) {
|
|
57
|
-
this._saveSbom(
|
|
72
|
+
this._saveSbom(path, sbom);
|
|
58
73
|
}
|
|
59
74
|
}
|
|
60
75
|
return sbom;
|
|
61
76
|
}
|
|
62
77
|
async _getSbomFromScan(_dirFlag) {
|
|
63
|
-
const dir =
|
|
78
|
+
const dir = resolve(_dirFlag);
|
|
64
79
|
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
|
65
80
|
throw new Error(`Directory not found or not a directory: ${dir}`);
|
|
66
81
|
}
|
|
@@ -72,8 +87,23 @@ export default class ScanSbom extends Command {
|
|
|
72
87
|
}
|
|
73
88
|
return sbom;
|
|
74
89
|
}
|
|
90
|
+
_getSbomInBackground(path) {
|
|
91
|
+
const opts = this.getScanOptions();
|
|
92
|
+
const args = [
|
|
93
|
+
JSON.stringify({
|
|
94
|
+
opts,
|
|
95
|
+
path,
|
|
96
|
+
}),
|
|
97
|
+
];
|
|
98
|
+
const workerProcess = spawn('node', [join(import.meta.dirname, '../../service/eol/sbom.worker.js'), ...args], {
|
|
99
|
+
stdio: 'ignore',
|
|
100
|
+
detached: true,
|
|
101
|
+
env: { ...process.env },
|
|
102
|
+
});
|
|
103
|
+
workerProcess.unref();
|
|
104
|
+
}
|
|
75
105
|
_getSbomFromFile(_fileFlag) {
|
|
76
|
-
const file =
|
|
106
|
+
const file = resolve(_fileFlag);
|
|
77
107
|
if (!fs.existsSync(file)) {
|
|
78
108
|
throw new Error(`SBOM file not found: ${file}`);
|
|
79
109
|
}
|
|
@@ -94,9 +124,11 @@ export default class ScanSbom extends Command {
|
|
|
94
124
|
}
|
|
95
125
|
_saveSbom(dir, sbom) {
|
|
96
126
|
try {
|
|
97
|
-
const outputPath =
|
|
127
|
+
const outputPath = join(dir, 'nes.sbom.json');
|
|
98
128
|
fs.writeFileSync(outputPath, JSON.stringify(sbom, null, 2));
|
|
99
|
-
this.
|
|
129
|
+
if (!this.jsonEnabled()) {
|
|
130
|
+
this.log(`SBOM saved to ${outputPath}`);
|
|
131
|
+
}
|
|
100
132
|
}
|
|
101
133
|
catch (error) {
|
|
102
134
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
@@ -9,6 +9,58 @@ export interface Sbom {
|
|
|
9
9
|
components: SbomEntry[];
|
|
10
10
|
dependencies: SbomEntry[];
|
|
11
11
|
}
|
|
12
|
+
export declare const SBOM_DEFAULT__OPTIONS: {
|
|
13
|
+
$0: string;
|
|
14
|
+
_: never[];
|
|
15
|
+
'auto-compositions': boolean;
|
|
16
|
+
autoCompositions: boolean;
|
|
17
|
+
'data-flow-slices-file': string;
|
|
18
|
+
dataFlowSlicesFile: string;
|
|
19
|
+
deep: boolean;
|
|
20
|
+
'deps-slices-file': string;
|
|
21
|
+
depsSlicesFile: string;
|
|
22
|
+
evidence: boolean;
|
|
23
|
+
'export-proto': boolean;
|
|
24
|
+
exportProto: boolean;
|
|
25
|
+
'fail-on-error': boolean;
|
|
26
|
+
failOnError: boolean;
|
|
27
|
+
false: boolean;
|
|
28
|
+
'include-crypto': boolean;
|
|
29
|
+
'include-formulation': boolean;
|
|
30
|
+
includeCrypto: boolean;
|
|
31
|
+
includeFormulation: boolean;
|
|
32
|
+
'install-deps': boolean;
|
|
33
|
+
installDeps: boolean;
|
|
34
|
+
'min-confidence': number;
|
|
35
|
+
minConfidence: number;
|
|
36
|
+
multiProject: boolean;
|
|
37
|
+
'no-banner': boolean;
|
|
38
|
+
noBabel: boolean;
|
|
39
|
+
noBanner: boolean;
|
|
40
|
+
o: string;
|
|
41
|
+
output: string;
|
|
42
|
+
outputFormat: string;
|
|
43
|
+
profile: string;
|
|
44
|
+
project: undefined;
|
|
45
|
+
'project-version': string;
|
|
46
|
+
projectVersion: string;
|
|
47
|
+
'proto-bin-file': string;
|
|
48
|
+
protoBinFile: string;
|
|
49
|
+
r: boolean;
|
|
50
|
+
'reachables-slices-file': string;
|
|
51
|
+
reachablesSlicesFile: string;
|
|
52
|
+
recurse: boolean;
|
|
53
|
+
requiredOnly: boolean;
|
|
54
|
+
'semantics-slices-file': string;
|
|
55
|
+
semanticsSlicesFile: string;
|
|
56
|
+
'skip-dt-tls-check': boolean;
|
|
57
|
+
skipDtTlsCheck: boolean;
|
|
58
|
+
'spec-version': number;
|
|
59
|
+
specVersion: number;
|
|
60
|
+
'usages-slices-file': string;
|
|
61
|
+
usagesSlicesFile: string;
|
|
62
|
+
validate: boolean;
|
|
63
|
+
};
|
|
12
64
|
/**
|
|
13
65
|
* Lazy loads cdxgen (for ESM purposes), scans
|
|
14
66
|
* `directory`, and returns the `bomJson` property.
|
|
@@ -1,71 +1,66 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { debugLogger } from "../../service/log.svc.js";
|
|
2
|
+
export const SBOM_DEFAULT__OPTIONS = {
|
|
3
|
+
$0: 'cdxgen',
|
|
4
|
+
_: [],
|
|
5
|
+
'auto-compositions': true,
|
|
6
|
+
autoCompositions: true,
|
|
7
|
+
'data-flow-slices-file': 'data-flow.slices.json',
|
|
8
|
+
dataFlowSlicesFile: 'data-flow.slices.json',
|
|
9
|
+
deep: false, // TODO: you def want to check this out
|
|
10
|
+
'deps-slices-file': 'deps.slices.json',
|
|
11
|
+
depsSlicesFile: 'deps.slices.json',
|
|
12
|
+
evidence: false,
|
|
13
|
+
'export-proto': false,
|
|
14
|
+
exportProto: false,
|
|
15
|
+
// DON'T FAIL ON ERROR; you won't get hlepful logs
|
|
16
|
+
'fail-on-error': false,
|
|
17
|
+
failOnError: false,
|
|
18
|
+
false: true,
|
|
19
|
+
'include-crypto': false,
|
|
20
|
+
'include-formulation': false,
|
|
21
|
+
includeCrypto: false,
|
|
22
|
+
includeFormulation: false,
|
|
23
|
+
'install-deps': true,
|
|
24
|
+
installDeps: true,
|
|
25
|
+
'min-confidence': 0,
|
|
26
|
+
minConfidence: 0,
|
|
27
|
+
multiProject: true,
|
|
28
|
+
'no-banner': false,
|
|
29
|
+
noBabel: false,
|
|
30
|
+
noBanner: false,
|
|
31
|
+
o: 'bom.json',
|
|
32
|
+
output: 'bom.json',
|
|
33
|
+
outputFormat: 'json', // or "xml"
|
|
34
|
+
// author: ['OWASP Foundation'],
|
|
35
|
+
profile: 'generic',
|
|
36
|
+
project: undefined,
|
|
37
|
+
'project-version': '',
|
|
38
|
+
projectVersion: '',
|
|
39
|
+
'proto-bin-file': 'bom.cdx',
|
|
40
|
+
protoBinFile: 'bom.cdx',
|
|
41
|
+
r: false,
|
|
42
|
+
'reachables-slices-file': 'reachables.slices.json',
|
|
43
|
+
reachablesSlicesFile: 'reachables.slices.json',
|
|
44
|
+
recurse: false,
|
|
45
|
+
requiredOnly: false,
|
|
46
|
+
'semantics-slices-file': 'semantics.slices.json',
|
|
47
|
+
semanticsSlicesFile: 'semantics.slices.json',
|
|
48
|
+
'skip-dt-tls-check': true,
|
|
49
|
+
skipDtTlsCheck: true,
|
|
50
|
+
'spec-version': 1.6,
|
|
51
|
+
specVersion: 1.6,
|
|
52
|
+
'usages-slices-file': 'usages.slices.json',
|
|
53
|
+
usagesSlicesFile: 'usages.slices.json',
|
|
54
|
+
validate: true,
|
|
55
|
+
};
|
|
2
56
|
/**
|
|
3
57
|
* Lazy loads cdxgen (for ESM purposes), scans
|
|
4
58
|
* `directory`, and returns the `bomJson` property.
|
|
5
59
|
*/
|
|
6
60
|
export async function createBomFromDir(directory, opts = {}) {
|
|
7
|
-
const options = {
|
|
8
|
-
$0: 'cdxgen',
|
|
9
|
-
_: [],
|
|
10
|
-
'auto-compositions': true,
|
|
11
|
-
autoCompositions: true,
|
|
12
|
-
'data-flow-slices-file': 'data-flow.slices.json',
|
|
13
|
-
dataFlowSlicesFile: 'data-flow.slices.json',
|
|
14
|
-
deep: false, // TODO: you def want to check this out
|
|
15
|
-
'deps-slices-file': 'deps.slices.json',
|
|
16
|
-
depsSlicesFile: 'deps.slices.json',
|
|
17
|
-
evidence: false,
|
|
18
|
-
'export-proto': false,
|
|
19
|
-
exportProto: false,
|
|
20
|
-
// DON'T FAIL ON ERROR; you won't get hlepful logs
|
|
21
|
-
'fail-on-error': false,
|
|
22
|
-
failOnError: false,
|
|
23
|
-
false: true,
|
|
24
|
-
'include-crypto': false,
|
|
25
|
-
'include-formulation': false,
|
|
26
|
-
includeCrypto: false,
|
|
27
|
-
includeFormulation: false,
|
|
28
|
-
// 'server-host': '127.0.0.1',
|
|
29
|
-
// serverHost: '127.0.0.1',
|
|
30
|
-
// 'server-port': '9090',
|
|
31
|
-
// serverPort: '9090',
|
|
32
|
-
'install-deps': true,
|
|
33
|
-
installDeps: true,
|
|
34
|
-
'min-confidence': 0,
|
|
35
|
-
minConfidence: 0,
|
|
36
|
-
multiProject: true,
|
|
37
|
-
'no-banner': false,
|
|
38
|
-
noBabel: false,
|
|
39
|
-
noBanner: false,
|
|
40
|
-
o: 'bom.json',
|
|
41
|
-
output: 'bom.json',
|
|
42
|
-
outputFormat: 'json', // or "xml"
|
|
43
|
-
// author: ['OWASP Foundation'],
|
|
44
|
-
profile: 'generic',
|
|
45
|
-
project: undefined,
|
|
46
|
-
'project-version': '',
|
|
47
|
-
projectVersion: '',
|
|
48
|
-
'proto-bin-file': 'bom.cdx',
|
|
49
|
-
protoBinFile: 'bom.cdx',
|
|
50
|
-
r: false,
|
|
51
|
-
'reachables-slices-file': 'reachables.slices.json',
|
|
52
|
-
reachablesSlicesFile: 'reachables.slices.json',
|
|
53
|
-
recurse: false,
|
|
54
|
-
requiredOnly: false,
|
|
55
|
-
'semantics-slices-file': 'semantics.slices.json',
|
|
56
|
-
semanticsSlicesFile: 'semantics.slices.json',
|
|
57
|
-
'skip-dt-tls-check': true,
|
|
58
|
-
skipDtTlsCheck: true,
|
|
59
|
-
'spec-version': 1.6,
|
|
60
|
-
specVersion: 1.6,
|
|
61
|
-
'usages-slices-file': 'usages.slices.json',
|
|
62
|
-
usagesSlicesFile: 'usages.slices.json',
|
|
63
|
-
validate: true,
|
|
64
|
-
...opts,
|
|
65
|
-
};
|
|
66
61
|
const { createBom } = await getCdxGen();
|
|
67
|
-
const sbom = await createBom?.(directory,
|
|
68
|
-
|
|
62
|
+
const sbom = await createBom?.(directory, { ...SBOM_DEFAULT__OPTIONS, ...opts });
|
|
63
|
+
debugLogger('Successfully generated SBOM');
|
|
69
64
|
return sbom?.bomJson;
|
|
70
65
|
}
|
|
71
66
|
// use a value holder, for easier mocking
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NesApolloClient } from "../../api/nes/nes.client.js";
|
|
2
|
-
import {
|
|
2
|
+
import { debugLogger } from "../../service/log.svc.js";
|
|
3
3
|
import { getDaysEolFromEolAt, getStatusFromComponent } from "../line.svc.js";
|
|
4
4
|
import { extractPurls } from "../purls.svc.js";
|
|
5
5
|
import { createBomFromDir } from "./cdx.svc.js";
|
|
@@ -7,7 +7,7 @@ export async function createSbom(directory, opts = {}) {
|
|
|
7
7
|
const sbom = await createBomFromDir(directory, opts.cdxgen || {});
|
|
8
8
|
if (!sbom)
|
|
9
9
|
throw new Error('SBOM not generated');
|
|
10
|
-
|
|
10
|
+
debugLogger('SBOM generated');
|
|
11
11
|
return sbom;
|
|
12
12
|
}
|
|
13
13
|
export function validateIsCycloneDxSbom(sbom) {
|
|
@@ -60,7 +60,7 @@ export async function prepareRows(purls, scan) {
|
|
|
60
60
|
if (!details) {
|
|
61
61
|
// In this case, the purl string is in the generated sbom, but the NES/XEOL api has no data
|
|
62
62
|
// TODO: add UNKNOWN Component Status, create new line, and create flag to show/hide unknown results
|
|
63
|
-
|
|
63
|
+
debugLogger(`Unknown status: ${purl}.`);
|
|
64
64
|
continue;
|
|
65
65
|
}
|
|
66
66
|
const { info } = details;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { createBom } from '@cyclonedx/cdxgen';
|
|
4
|
+
import { SBOM_DEFAULT__OPTIONS } from "./cdx.svc.js";
|
|
5
|
+
process.on('uncaughtException', (err) => {
|
|
6
|
+
console.error('Uncaught exception:', err.message);
|
|
7
|
+
process.exit(1);
|
|
8
|
+
});
|
|
9
|
+
process.on('unhandledRejection', (reason) => {
|
|
10
|
+
console.error('Unhandled rejection:', reason);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
|
13
|
+
try {
|
|
14
|
+
console.log('Sbom worker started');
|
|
15
|
+
const options = JSON.parse(process.argv[2]);
|
|
16
|
+
const { path, opts } = options;
|
|
17
|
+
const { bomJson } = await createBom(path, { ...SBOM_DEFAULT__OPTIONS, ...opts });
|
|
18
|
+
const outputPath = join(path, 'nes.sbom.json');
|
|
19
|
+
writeFileSync(outputPath, JSON.stringify(bomJson, null, 2));
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('Error creating SBOM', error.message);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
1
2
|
/**
|
|
2
|
-
* A simple
|
|
3
|
-
*
|
|
3
|
+
* A simple debug logger for services.
|
|
4
|
+
* Services should only use debug logging for development/troubleshooting.
|
|
5
|
+
* All user-facing output should be handled by commands.
|
|
4
6
|
*/
|
|
5
|
-
export declare const
|
|
6
|
-
info: (_message?: unknown, ...args: unknown[]) => void;
|
|
7
|
-
warn: (_message?: unknown, ...args: unknown[]) => void;
|
|
8
|
-
debug: (_message?: unknown, ...args: unknown[]) => void;
|
|
9
|
-
};
|
|
10
|
-
export declare const initOclifLog: (info: (message?: unknown, ...args: unknown[]) => void, warn: (message?: unknown, ...args: unknown[]) => void, debug: (message?: unknown, ...args: unknown[]) => void) => void;
|
|
7
|
+
export declare const debugLogger: debug.Debugger;
|
package/dist/service/log.svc.js
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
|
+
import debug from 'debug';
|
|
1
2
|
/**
|
|
2
|
-
* A simple
|
|
3
|
-
*
|
|
3
|
+
* A simple debug logger for services.
|
|
4
|
+
* Services should only use debug logging for development/troubleshooting.
|
|
5
|
+
* All user-facing output should be handled by commands.
|
|
4
6
|
*/
|
|
5
|
-
export const
|
|
6
|
-
info: (_message, ...args) => {
|
|
7
|
-
console.log('[default_log]', ...args);
|
|
8
|
-
},
|
|
9
|
-
warn: (_message, ...args) => {
|
|
10
|
-
console.warn('[default_warn]', ...args);
|
|
11
|
-
},
|
|
12
|
-
debug: (_message, ...args) => {
|
|
13
|
-
console.debug('[default_debug]', ...args);
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
export const initOclifLog = (info, warn, debug) => {
|
|
17
|
-
log.info = info;
|
|
18
|
-
log.warn = warn;
|
|
19
|
-
log.debug = debug;
|
|
20
|
-
};
|
|
7
|
+
export const debugLogger = debug('oclif:herodevs-debug');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { M_SCAN } from "../../api/queries/nes/sbom.js";
|
|
2
|
-
import {
|
|
2
|
+
import { debugLogger } from "../log.svc.js";
|
|
3
3
|
export const buildScanResult = (scan) => {
|
|
4
4
|
const components = new Map();
|
|
5
5
|
for (const c of scan.components) {
|
|
@@ -16,8 +16,8 @@ export const SbomScanner = (client) => async (purls) => {
|
|
|
16
16
|
const res = await client.mutate(M_SCAN.gql, { input });
|
|
17
17
|
const scan = res.data?.insights?.scan?.eol;
|
|
18
18
|
if (!scan?.success) {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
debugLogger('failed scan %o', scan || {});
|
|
20
|
+
debugLogger('scan failed');
|
|
21
21
|
throw new Error('Failed to provide scan: ');
|
|
22
22
|
}
|
|
23
23
|
const result = buildScanResult(scan);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herodevs/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0-beta.1",
|
|
4
4
|
"author": "HeroDevs, Inc",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hd": "./bin/run.js"
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"clean": "shx rm -rf dist && npm run clean:files && shx rm -rf node_modules",
|
|
20
20
|
"clean:files": "shx rm -f nes.**.csv nes.**.json nes.**.text",
|
|
21
21
|
"dev": "npm run build && ./bin/dev.js",
|
|
22
|
+
"dev:debug": "npm run build && DEBUG=* ./bin/dev.js",
|
|
22
23
|
"format": "biome format --write",
|
|
23
24
|
"lint": "biome lint --write",
|
|
24
25
|
"postpack": "shx rm -f oclif.manifest.json",
|
|
@@ -36,9 +37,12 @@
|
|
|
36
37
|
],
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@apollo/client": "^3.13.1",
|
|
40
|
+
"@cyclonedx/cdxgen": "^11.2.2",
|
|
41
|
+
"@oclif/core": "^4",
|
|
42
|
+
"@oclif/plugin-help": "^6",
|
|
43
|
+
"@oclif/plugin-plugins": "^5",
|
|
39
44
|
"graphql": "^16.8.1",
|
|
40
|
-
"inquirer": "^12.5.0"
|
|
41
|
-
"@oclif/core": "^4"
|
|
45
|
+
"inquirer": "^12.5.0"
|
|
42
46
|
},
|
|
43
47
|
"devDependencies": {
|
|
44
48
|
"@biomejs/biome": "^1.8.3",
|
|
@@ -49,6 +53,7 @@
|
|
|
49
53
|
"oclif": "^4",
|
|
50
54
|
"shx": "^0.3.3",
|
|
51
55
|
"sinon": "^19.0.2",
|
|
56
|
+
"ts-node": "^10",
|
|
52
57
|
"typescript": "^5.8.0"
|
|
53
58
|
},
|
|
54
59
|
"engines": {
|
|
@@ -67,15 +72,12 @@
|
|
|
67
72
|
"commands": "./dist/commands",
|
|
68
73
|
"plugins": [
|
|
69
74
|
"@oclif/plugin-help",
|
|
70
|
-
"@oclif/plugin-plugins"
|
|
71
|
-
"@oclif/plugin-update"
|
|
75
|
+
"@oclif/plugin-plugins"
|
|
72
76
|
],
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
}
|
|
77
|
+
"hooks": {
|
|
78
|
+
"prerun": "./dist/hooks/prerun.js"
|
|
79
|
+
},
|
|
80
|
+
"topicSeparator": " "
|
|
79
81
|
},
|
|
80
82
|
"types": "dist/index.d.ts"
|
|
81
83
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { initOclifLog, log } from "../../service/log.svc.js";
|
|
2
|
-
const hook = async (opts) => {
|
|
3
|
-
initOclifLog(opts.context.log, opts.context.log, opts.context.debug);
|
|
4
|
-
log.info = opts.context.log || log.info;
|
|
5
|
-
log.warn = opts.context.log || log.warn;
|
|
6
|
-
log.debug = opts.context.debug || log.debug;
|
|
7
|
-
};
|
|
8
|
-
export default hook;
|
|
File without changes
|