@herodevs/cli 1.5.0-beta.2 → 1.5.0-beta.3
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 +20 -10
- package/bin/dev.js +2 -2
- package/dist/commands/report/committers.js +4 -4
- package/dist/commands/report/purls.js +2 -2
- package/dist/commands/scan/eol.js +5 -5
- package/dist/commands/scan/sbom.js +3 -3
- package/dist/service/eol/cdx.svc.d.ts +2 -2
- package/dist/service/eol/cdx.svc.js +2 -2
- package/dist/service/eol/sbom.worker.js +1 -1
- package/dist/service/purls.svc.d.ts +1 -1
- package/dist/service/purls.svc.js +12 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,6 +9,16 @@ The HeroDevs CLI
|
|
|
9
9
|
<!-- toc -->
|
|
10
10
|
* [@herodevs/cli](#herodevscli)
|
|
11
11
|
<!-- tocstop -->
|
|
12
|
+
|
|
13
|
+
## Scanning Behavior
|
|
14
|
+
|
|
15
|
+
The CLI's scanning commands (`hd scan eol` and `hd scan sbom`) are designed to be non-invasive:
|
|
16
|
+
|
|
17
|
+
* They do not install dependencies or modify package manager files (package-lock.json, yarn.lock, etc.)
|
|
18
|
+
* They analyze the project in its current state
|
|
19
|
+
* If you need dependencies installed for accurate scanning, please install them manually before running the scan
|
|
20
|
+
|
|
21
|
+
|
|
12
22
|
## Usage
|
|
13
23
|
<!-- usage -->
|
|
14
24
|
```sh-session
|
|
@@ -16,7 +26,7 @@ $ npm install -g @herodevs/cli
|
|
|
16
26
|
$ hd COMMAND
|
|
17
27
|
running command...
|
|
18
28
|
$ hd (--version)
|
|
19
|
-
@herodevs/cli/1.5.0-beta.
|
|
29
|
+
@herodevs/cli/1.5.0-beta.3 linux-x64 node-v22.14.0
|
|
20
30
|
$ hd --help [COMMAND]
|
|
21
31
|
USAGE
|
|
22
32
|
$ hd COMMAND
|
|
@@ -63,7 +73,7 @@ USAGE
|
|
|
63
73
|
FLAGS
|
|
64
74
|
-c, --csv Output in CSV format
|
|
65
75
|
-m, --months=<value> [default: 12] The number of months of git history to review
|
|
66
|
-
-s, --save Save the committers report as
|
|
76
|
+
-s, --save Save the committers report as eol.committers.<output>
|
|
67
77
|
|
|
68
78
|
GLOBAL FLAGS
|
|
69
79
|
--json Format output as json.
|
|
@@ -81,7 +91,7 @@ EXAMPLES
|
|
|
81
91
|
$ hd report committers --csv
|
|
82
92
|
```
|
|
83
93
|
|
|
84
|
-
_See code: [src/commands/report/committers.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.
|
|
94
|
+
_See code: [src/commands/report/committers.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.3/src/commands/report/committers.ts)_
|
|
85
95
|
|
|
86
96
|
## `hd report purls`
|
|
87
97
|
|
|
@@ -95,7 +105,7 @@ FLAGS
|
|
|
95
105
|
-c, --csv Save output in CSV format (only applies when using --save)
|
|
96
106
|
-d, --dir=<value> The directory to scan in order to create a cyclonedx sbom
|
|
97
107
|
-f, --file=<value> The file path of an existing cyclonedx sbom to scan for EOL
|
|
98
|
-
-s, --save Save the list of purls as
|
|
108
|
+
-s, --save Save the list of purls as eol.purls.<output>
|
|
99
109
|
|
|
100
110
|
GLOBAL FLAGS
|
|
101
111
|
--json Format output as json.
|
|
@@ -115,7 +125,7 @@ EXAMPLES
|
|
|
115
125
|
$ hd report purls --save --csv
|
|
116
126
|
```
|
|
117
127
|
|
|
118
|
-
_See code: [src/commands/report/purls.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.
|
|
128
|
+
_See code: [src/commands/report/purls.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.3/src/commands/report/purls.ts)_
|
|
119
129
|
|
|
120
130
|
## `hd scan eol`
|
|
121
131
|
|
|
@@ -130,7 +140,7 @@ FLAGS
|
|
|
130
140
|
-d, --dir=<value> The directory to scan in order to create a cyclonedx sbom
|
|
131
141
|
-f, --file=<value> The file path of an existing cyclonedx sbom to scan for EOL
|
|
132
142
|
-p, --purls=<value> The file path of a list of purls to scan for EOL
|
|
133
|
-
-s, --save Save the generated
|
|
143
|
+
-s, --save Save the generated report as eol.report.json in the scanned directory
|
|
134
144
|
-t, --table Display the results in a table
|
|
135
145
|
|
|
136
146
|
GLOBAL FLAGS
|
|
@@ -149,7 +159,7 @@ EXAMPLES
|
|
|
149
159
|
$ hd scan eol -a --dir=./my-project
|
|
150
160
|
```
|
|
151
161
|
|
|
152
|
-
_See code: [src/commands/scan/eol.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.
|
|
162
|
+
_See code: [src/commands/scan/eol.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.3/src/commands/scan/eol.ts)_
|
|
153
163
|
|
|
154
164
|
## `hd scan sbom`
|
|
155
165
|
|
|
@@ -163,7 +173,7 @@ FLAGS
|
|
|
163
173
|
-b, --background Run the scan in the background
|
|
164
174
|
-d, --dir=<value> The directory to scan in order to create a cyclonedx sbom
|
|
165
175
|
-f, --file=<value> The file path of an existing cyclonedx sbom to scan for EOL
|
|
166
|
-
-s, --save Save the generated SBOM as
|
|
176
|
+
-s, --save Save the generated SBOM as eol.sbom.json in the scanned directory
|
|
167
177
|
|
|
168
178
|
GLOBAL FLAGS
|
|
169
179
|
--json Format output as json.
|
|
@@ -177,7 +187,7 @@ EXAMPLES
|
|
|
177
187
|
$ hd scan sbom --file=path/to/sbom.json
|
|
178
188
|
```
|
|
179
189
|
|
|
180
|
-
_See code: [src/commands/scan/sbom.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.
|
|
190
|
+
_See code: [src/commands/scan/sbom.ts](https://github.com/herodevs/cli/blob/v1.5.0-beta.3/src/commands/scan/sbom.ts)_
|
|
181
191
|
|
|
182
192
|
## `hd update [CHANNEL]`
|
|
183
193
|
|
|
@@ -215,5 +225,5 @@ EXAMPLES
|
|
|
215
225
|
$ hd update --available
|
|
216
226
|
```
|
|
217
227
|
|
|
218
|
-
_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v4.6.
|
|
228
|
+
_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v4.6.38/src/commands/update.ts)_
|
|
219
229
|
<!-- commandsstop -->
|
package/bin/dev.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import { execute } from '@oclif/core';
|
|
4
4
|
|
|
5
5
|
// Localhost
|
|
6
|
-
process.env.GRAPHQL_HOST = 'http://localhost:3000';
|
|
6
|
+
// process.env.GRAPHQL_HOST = 'http://localhost:3000';
|
|
7
7
|
|
|
8
8
|
// Dev
|
|
9
|
-
|
|
9
|
+
process.env.GRAPHQL_HOST = 'https://api.dev.nes.herodevs.com';
|
|
10
10
|
|
|
11
11
|
// Prod
|
|
12
12
|
// process.env.GRAPHQL_HOST = 'https://api.nes.herodevs.com';
|
|
@@ -26,7 +26,7 @@ export default class Committers extends Command {
|
|
|
26
26
|
}),
|
|
27
27
|
save: Flags.boolean({
|
|
28
28
|
char: 's',
|
|
29
|
-
description: 'Save the committers report as
|
|
29
|
+
description: 'Save the committers report as eol.committers.<output>',
|
|
30
30
|
default: false,
|
|
31
31
|
}),
|
|
32
32
|
};
|
|
@@ -46,7 +46,7 @@ export default class Committers extends Command {
|
|
|
46
46
|
// JSON mode
|
|
47
47
|
if (save) {
|
|
48
48
|
try {
|
|
49
|
-
fs.writeFileSync(path.resolve('
|
|
49
|
+
fs.writeFileSync(path.resolve('eol.committers.json'), JSON.stringify(reportData, null, 2));
|
|
50
50
|
this.log('Report written to json');
|
|
51
51
|
}
|
|
52
52
|
catch (error) {
|
|
@@ -61,7 +61,7 @@ export default class Committers extends Command {
|
|
|
61
61
|
const csvOutput = formatAsCsv(reportData);
|
|
62
62
|
if (save) {
|
|
63
63
|
try {
|
|
64
|
-
fs.writeFileSync(path.resolve('
|
|
64
|
+
fs.writeFileSync(path.resolve('eol.committers.csv'), csvOutput);
|
|
65
65
|
this.log('Report written to csv');
|
|
66
66
|
}
|
|
67
67
|
catch (error) {
|
|
@@ -75,7 +75,7 @@ export default class Committers extends Command {
|
|
|
75
75
|
}
|
|
76
76
|
if (save) {
|
|
77
77
|
try {
|
|
78
|
-
fs.writeFileSync(path.resolve('
|
|
78
|
+
fs.writeFileSync(path.resolve('eol.committers.txt'), textOutput);
|
|
79
79
|
this.log('Report written to txt');
|
|
80
80
|
}
|
|
81
81
|
catch (error) {
|
|
@@ -26,7 +26,7 @@ export default class ReportPurls extends Command {
|
|
|
26
26
|
save: Flags.boolean({
|
|
27
27
|
char: 's',
|
|
28
28
|
default: false,
|
|
29
|
-
description: 'Save the list of purls as
|
|
29
|
+
description: 'Save the list of purls as eol.purls.<output>',
|
|
30
30
|
}),
|
|
31
31
|
csv: Flags.boolean({
|
|
32
32
|
char: 'c',
|
|
@@ -50,7 +50,7 @@ export default class ReportPurls extends Command {
|
|
|
50
50
|
if (save) {
|
|
51
51
|
try {
|
|
52
52
|
const outputFile = csv && !this.jsonEnabled() ? 'csv' : 'json';
|
|
53
|
-
const outputPath = path.join(_dirFlag || process.cwd(), `
|
|
53
|
+
const outputPath = path.join(_dirFlag || process.cwd(), `eol.purls.${outputFile}`);
|
|
54
54
|
const purlOutput = getPurlOutput(purls, outputFile);
|
|
55
55
|
fs.writeFileSync(outputPath, purlOutput);
|
|
56
56
|
this.log('Purls saved to %s', outputPath);
|
|
@@ -32,7 +32,7 @@ export default class ScanEol extends Command {
|
|
|
32
32
|
save: Flags.boolean({
|
|
33
33
|
char: 's',
|
|
34
34
|
default: false,
|
|
35
|
-
description: 'Save the generated
|
|
35
|
+
description: 'Save the generated report as eol.report.json in the scanned directory',
|
|
36
36
|
}),
|
|
37
37
|
all: Flags.boolean({
|
|
38
38
|
char: 'a',
|
|
@@ -107,10 +107,10 @@ export default class ScanEol extends Command {
|
|
|
107
107
|
}
|
|
108
108
|
async saveReport(components) {
|
|
109
109
|
const { flags } = await this.parse(ScanEol);
|
|
110
|
-
const reportPath = path.join(flags.dir || process.cwd(), '
|
|
110
|
+
const reportPath = path.join(flags.dir || process.cwd(), 'eol.report.json');
|
|
111
111
|
try {
|
|
112
112
|
fs.writeFileSync(reportPath, JSON.stringify({ components }, null, 2));
|
|
113
|
-
this.log('Report saved to
|
|
113
|
+
this.log('Report saved to eol.report.json');
|
|
114
114
|
}
|
|
115
115
|
catch (error) {
|
|
116
116
|
if (!isErrnoException(error)) {
|
|
@@ -118,10 +118,10 @@ export default class ScanEol extends Command {
|
|
|
118
118
|
}
|
|
119
119
|
switch (error.code) {
|
|
120
120
|
case 'EACCES':
|
|
121
|
-
this.error('Permission denied. Unable to save report to
|
|
121
|
+
this.error('Permission denied. Unable to save report to eol.report.json');
|
|
122
122
|
break;
|
|
123
123
|
case 'ENOSPC':
|
|
124
|
-
this.error('No space left on device. Unable to save report to
|
|
124
|
+
this.error('No space left on device. Unable to save report to eol.report.json');
|
|
125
125
|
break;
|
|
126
126
|
default:
|
|
127
127
|
this.error(`Failed to save report: ${getErrorMessage(error)}`);
|
|
@@ -23,7 +23,7 @@ export default class ScanSbom extends Command {
|
|
|
23
23
|
save: Flags.boolean({
|
|
24
24
|
char: 's',
|
|
25
25
|
default: false,
|
|
26
|
-
description: 'Save the generated SBOM as
|
|
26
|
+
description: 'Save the generated SBOM as eol.sbom.json in the scanned directory',
|
|
27
27
|
}),
|
|
28
28
|
background: Flags.boolean({
|
|
29
29
|
char: 'b',
|
|
@@ -72,7 +72,7 @@ export default class ScanSbom extends Command {
|
|
|
72
72
|
}
|
|
73
73
|
else if (background) {
|
|
74
74
|
this._getSbomInBackground(path);
|
|
75
|
-
this.log(`The scan is running in the background. The file will be saved at ${path}/
|
|
75
|
+
this.log(`The scan is running in the background. The file will be saved at ${path}/eol.sbom.json`);
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
else {
|
|
@@ -146,7 +146,7 @@ export default class ScanSbom extends Command {
|
|
|
146
146
|
}
|
|
147
147
|
_saveSbom(dir, sbom) {
|
|
148
148
|
try {
|
|
149
|
-
const outputPath = join(dir, '
|
|
149
|
+
const outputPath = join(dir, 'eol.sbom.json');
|
|
150
150
|
fs.writeFileSync(outputPath, JSON.stringify(sbom, null, 2));
|
|
151
151
|
if (!this.jsonEnabled()) {
|
|
152
152
|
this.log(`SBOM saved to ${outputPath}`);
|
|
@@ -29,8 +29,8 @@ export declare const SBOM_DEFAULT__OPTIONS: {
|
|
|
29
29
|
'include-formulation': boolean;
|
|
30
30
|
includeCrypto: boolean;
|
|
31
31
|
includeFormulation: boolean;
|
|
32
|
-
'install-deps': boolean;
|
|
33
|
-
|
|
32
|
+
'no-install-deps': boolean;
|
|
33
|
+
noInstallDeps: boolean;
|
|
34
34
|
'min-confidence': number;
|
|
35
35
|
minConfidence: number;
|
|
36
36
|
multiProject: boolean;
|
|
@@ -21,8 +21,8 @@ export const SBOM_DEFAULT__OPTIONS = {
|
|
|
21
21
|
'include-formulation': false,
|
|
22
22
|
includeCrypto: false,
|
|
23
23
|
includeFormulation: false,
|
|
24
|
-
'install-deps': true,
|
|
25
|
-
|
|
24
|
+
'no-install-deps': true,
|
|
25
|
+
noInstallDeps: true,
|
|
26
26
|
'min-confidence': 0,
|
|
27
27
|
minConfidence: 0,
|
|
28
28
|
multiProject: true,
|
|
@@ -15,7 +15,7 @@ try {
|
|
|
15
15
|
const options = JSON.parse(process.argv[2]);
|
|
16
16
|
const { path, opts } = options;
|
|
17
17
|
const { bomJson } = await createBom(path, { ...SBOM_DEFAULT__OPTIONS, ...opts });
|
|
18
|
-
const outputPath = join(path, '
|
|
18
|
+
const outputPath = join(path, 'eol.sbom.json');
|
|
19
19
|
writeFileSync(outputPath, JSON.stringify(bomJson, null, 2));
|
|
20
20
|
process.exit(0);
|
|
21
21
|
}
|
|
@@ -17,7 +17,7 @@ export declare function getPurlOutput(purls: string[], output: string): string;
|
|
|
17
17
|
export declare function extractPurls(sbom: Sbom): Promise<string[]>;
|
|
18
18
|
/**
|
|
19
19
|
* Parse a purls file in either JSON or text format, including the format of
|
|
20
|
-
*
|
|
20
|
+
* eol.purls.json - { purls: [ 'pkg:npm/express@4.18.2', 'pkg:npm/react@18.3.1' ] }
|
|
21
21
|
* or a text file with one purl per line.
|
|
22
22
|
*/
|
|
23
23
|
export declare function parsePurlsFile(purlsFileString: string): string[];
|
|
@@ -29,11 +29,16 @@ export async function extractPurls(sbom) {
|
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* Parse a purls file in either JSON or text format, including the format of
|
|
32
|
-
*
|
|
32
|
+
* eol.purls.json - { purls: [ 'pkg:npm/express@4.18.2', 'pkg:npm/react@18.3.1' ] }
|
|
33
33
|
* or a text file with one purl per line.
|
|
34
34
|
*/
|
|
35
35
|
export function parsePurlsFile(purlsFileString) {
|
|
36
|
+
// Handle empty string
|
|
37
|
+
if (!purlsFileString.trim()) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
36
40
|
try {
|
|
41
|
+
// Try parsing as JSON first
|
|
37
42
|
const parsed = JSON.parse(purlsFileString);
|
|
38
43
|
if (parsed && Array.isArray(parsed.purls)) {
|
|
39
44
|
return parsed.purls;
|
|
@@ -43,10 +48,16 @@ export function parsePurlsFile(purlsFileString) {
|
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
catch {
|
|
51
|
+
// If not JSON, try parsing as text file
|
|
46
52
|
const lines = purlsFileString
|
|
47
53
|
.split('\n')
|
|
48
54
|
.map((line) => line.trim())
|
|
49
55
|
.filter((line) => line.length > 0 && line.startsWith('pkg:'));
|
|
56
|
+
// Handle single purl case (no newlines)
|
|
57
|
+
if (lines.length === 0 && purlsFileString.trim().startsWith('pkg:')) {
|
|
58
|
+
return [purlsFileString.trim()];
|
|
59
|
+
}
|
|
60
|
+
// Return any valid purls found
|
|
50
61
|
if (lines.length > 0) {
|
|
51
62
|
return lines;
|
|
52
63
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herodevs/cli",
|
|
3
|
-
"version": "1.5.0-beta.
|
|
3
|
+
"version": "1.5.0-beta.3",
|
|
4
4
|
"author": "HeroDevs, Inc",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hd": "./bin/run.js"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"ci": "biome ci",
|
|
18
18
|
"ci:fix": "biome check --write",
|
|
19
19
|
"clean": "shx rm -rf dist && npm run clean:files && shx rm -rf node_modules",
|
|
20
|
-
"clean:files": "shx rm -f
|
|
20
|
+
"clean:files": "shx rm -f eol.**.csv eol.**.json eol.**.text",
|
|
21
21
|
"dev": "npm run build && ./bin/dev.js",
|
|
22
22
|
"dev:debug": "npm run build && DEBUG=oclif:* ./bin/dev.js",
|
|
23
23
|
"format": "biome format --write",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@apollo/client": "^3.13.8",
|
|
44
|
-
"@cyclonedx/cdxgen": "^11.2.
|
|
44
|
+
"@cyclonedx/cdxgen": "^11.2.5",
|
|
45
45
|
"@oclif/core": "^4",
|
|
46
46
|
"@oclif/plugin-help": "^6",
|
|
47
47
|
"@oclif/plugin-update": "^4",
|