@herodevs/cli 2.0.0-beta.1 → 2.0.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 +14 -6
- package/bin/dev.js +0 -3
- package/dist/api/nes/nes.client.d.ts +1 -0
- package/dist/api/nes/nes.client.js +21 -1
- package/dist/api/queries/nes/sbom.js +1 -0
- package/dist/api/types/hd-cli.types.d.ts +1 -0
- package/dist/api/types/nes.types.d.ts +1 -0
- package/dist/commands/scan/eol.d.ts +1 -0
- package/dist/commands/scan/eol.js +12 -13
- package/dist/config/constants.js +1 -1
- package/dist/service/nes/nes.svc.js +1 -0
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -10,6 +10,14 @@ The HeroDevs CLI
|
|
|
10
10
|
* [@herodevs/cli](#herodevscli)
|
|
11
11
|
<!-- tocstop -->
|
|
12
12
|
|
|
13
|
+
## Installation Instructions
|
|
14
|
+
|
|
15
|
+
1. Install node v20 or higher: [Download Node](https://nodejs.org/en/download)
|
|
16
|
+
1. Install the CLI using one of the following methods:
|
|
17
|
+
- Globally: Refer to the [Usage](#usage) instructions on installing the CLI globally
|
|
18
|
+
- Npx:`npx @herodevs/cli@beta <commands>`
|
|
19
|
+
1. Refer to the [Commands](#commands) section for a list of commands
|
|
20
|
+
|
|
13
21
|
## TERMS
|
|
14
22
|
|
|
15
23
|
Use of this CLI is governed by the [HeroDevs End of Life Dataset Terms of Service and Data Policy](https://docs.herodevs.com/legal/end-of-life-dataset-terms).
|
|
@@ -30,7 +38,7 @@ $ npm install -g @herodevs/cli
|
|
|
30
38
|
$ hd COMMAND
|
|
31
39
|
running command...
|
|
32
40
|
$ hd (--version)
|
|
33
|
-
@herodevs/cli/2.0.0-beta.
|
|
41
|
+
@herodevs/cli/2.0.0-beta.3 linux-x64 node-v22.16.0
|
|
34
42
|
$ hd --help [COMMAND]
|
|
35
43
|
USAGE
|
|
36
44
|
$ hd COMMAND
|
|
@@ -95,7 +103,7 @@ EXAMPLES
|
|
|
95
103
|
$ hd report committers --csv
|
|
96
104
|
```
|
|
97
105
|
|
|
98
|
-
_See code: [src/commands/report/committers.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.
|
|
106
|
+
_See code: [src/commands/report/committers.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.3/src/commands/report/committers.ts)_
|
|
99
107
|
|
|
100
108
|
## `hd report purls`
|
|
101
109
|
|
|
@@ -129,7 +137,7 @@ EXAMPLES
|
|
|
129
137
|
$ hd report purls --save --csv
|
|
130
138
|
```
|
|
131
139
|
|
|
132
|
-
_See code: [src/commands/report/purls.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.
|
|
140
|
+
_See code: [src/commands/report/purls.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.3/src/commands/report/purls.ts)_
|
|
133
141
|
|
|
134
142
|
## `hd scan eol`
|
|
135
143
|
|
|
@@ -163,7 +171,7 @@ EXAMPLES
|
|
|
163
171
|
$ hd scan eol -a --dir=./my-project
|
|
164
172
|
```
|
|
165
173
|
|
|
166
|
-
_See code: [src/commands/scan/eol.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.
|
|
174
|
+
_See code: [src/commands/scan/eol.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.3/src/commands/scan/eol.ts)_
|
|
167
175
|
|
|
168
176
|
## `hd scan sbom`
|
|
169
177
|
|
|
@@ -191,7 +199,7 @@ EXAMPLES
|
|
|
191
199
|
$ hd scan sbom --file=path/to/sbom.json
|
|
192
200
|
```
|
|
193
201
|
|
|
194
|
-
_See code: [src/commands/scan/sbom.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.
|
|
202
|
+
_See code: [src/commands/scan/sbom.ts](https://github.com/herodevs/cli/blob/v2.0.0-beta.3/src/commands/scan/sbom.ts)_
|
|
195
203
|
|
|
196
204
|
## `hd update [CHANNEL]`
|
|
197
205
|
|
|
@@ -229,5 +237,5 @@ EXAMPLES
|
|
|
229
237
|
$ hd update --available
|
|
230
238
|
```
|
|
231
239
|
|
|
232
|
-
_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v4.6.
|
|
240
|
+
_See code: [@oclif/plugin-update](https://github.com/oclif/plugin-update/blob/v4.6.42/src/commands/update.ts)_
|
|
233
241
|
<!-- commandsstop -->
|
package/bin/dev.js
CHANGED
|
@@ -16,6 +16,7 @@ export declare class NesApolloClient implements NesClient {
|
|
|
16
16
|
query<T, V extends Record<string, unknown> | undefined>(query: apollo.DocumentNode, variables?: V): Promise<apollo.ApolloQueryResult<T>>;
|
|
17
17
|
}
|
|
18
18
|
export declare const batchSubmitPurls: (purls: string[], options?: ScanInputOptions, batchSize?: number) => Promise<ScanResult>;
|
|
19
|
+
export declare const dedupeAndEncodePurls: (purls: string[]) => string[];
|
|
19
20
|
export declare const createBatches: (items: string[], batchSize: number) => string[][];
|
|
20
21
|
export declare const processBatch: ({ batch, index, totalPages, scanOptions, previousScanId, }: ProcessBatchOptions) => Promise<InsightsEolScanResult>;
|
|
21
22
|
export declare const processBatches: (batches: string[][], scanOptions: ScanInputOptions) => Promise<InsightsEolScanResult[]>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PackageURL } from 'packageurl-js';
|
|
1
2
|
import { ApolloClient } from "../../api/client.js";
|
|
2
3
|
import { config } from "../../config/constants.js";
|
|
3
4
|
import { debugLogger } from "../../service/log.svc.js";
|
|
@@ -25,12 +26,14 @@ function submitScan(purls, options) {
|
|
|
25
26
|
const host = config.graphqlHost;
|
|
26
27
|
const path = config.graphqlPath;
|
|
27
28
|
const url = host + path;
|
|
29
|
+
debugLogger('Submitting scan to %s', url);
|
|
28
30
|
const client = new NesApolloClient(url);
|
|
29
31
|
return client.scan.purls(purls, options);
|
|
30
32
|
}
|
|
31
33
|
export const batchSubmitPurls = async (purls, options = DEFAULT_SCAN_INPUT_OPTIONS, batchSize = DEFAULT_SCAN_BATCH_SIZE) => {
|
|
32
34
|
try {
|
|
33
|
-
const
|
|
35
|
+
const dedupedAndEncodedPurls = dedupeAndEncodePurls(purls);
|
|
36
|
+
const batches = createBatches(dedupedAndEncodedPurls, batchSize);
|
|
34
37
|
debugLogger('Processing %d batches', batches.length);
|
|
35
38
|
if (batches.length === 0) {
|
|
36
39
|
return {
|
|
@@ -39,6 +42,7 @@ export const batchSubmitPurls = async (purls, options = DEFAULT_SCAN_INPUT_OPTIO
|
|
|
39
42
|
success: true,
|
|
40
43
|
warnings: [],
|
|
41
44
|
scanId: undefined,
|
|
45
|
+
createdOn: undefined,
|
|
42
46
|
};
|
|
43
47
|
}
|
|
44
48
|
const results = await processBatches(batches, options);
|
|
@@ -49,6 +53,22 @@ export const batchSubmitPurls = async (purls, options = DEFAULT_SCAN_INPUT_OPTIO
|
|
|
49
53
|
throw new Error(`Failed to process purls: ${error instanceof Error ? error.message : String(error)}`);
|
|
50
54
|
}
|
|
51
55
|
};
|
|
56
|
+
export const dedupeAndEncodePurls = (purls) => {
|
|
57
|
+
const dedupedAndEncodedPurls = new Set();
|
|
58
|
+
for (const purl of purls) {
|
|
59
|
+
try {
|
|
60
|
+
// The PackageURL.fromString method encodes each part of the purl
|
|
61
|
+
const encodedPurl = PackageURL.fromString(purl).toString();
|
|
62
|
+
if (!dedupedAndEncodedPurls.has(encodedPurl)) {
|
|
63
|
+
dedupedAndEncodedPurls.add(encodedPurl);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
debugLogger('Error encoding purl: %s', error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return Array.from(dedupedAndEncodedPurls);
|
|
71
|
+
};
|
|
52
72
|
export const createBatches = (items, batchSize) => {
|
|
53
73
|
const numberOfBatches = Math.ceil(items.length / batchSize);
|
|
54
74
|
return Array.from({ length: numberOfBatches }, (_, index) => {
|
|
@@ -52,7 +52,7 @@ export default class ScanEol extends Command {
|
|
|
52
52
|
ux.action.stop('\nScan completed');
|
|
53
53
|
const components = this.getFilteredComponents(scan, flags.all);
|
|
54
54
|
if (flags.save) {
|
|
55
|
-
await this.saveReport(components);
|
|
55
|
+
await this.saveReport(components, scan.createdOn);
|
|
56
56
|
}
|
|
57
57
|
if (!this.jsonEnabled()) {
|
|
58
58
|
if (flags.table) {
|
|
@@ -66,7 +66,7 @@ export default class ScanEol extends Command {
|
|
|
66
66
|
this.printWebReportUrl(scan.scanId);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
return { components };
|
|
69
|
+
return { components, createdOn: scan.createdOn ?? '' };
|
|
70
70
|
}
|
|
71
71
|
async getScan(flags, config) {
|
|
72
72
|
if (flags.purls) {
|
|
@@ -116,26 +116,25 @@ export default class ScanEol extends Command {
|
|
|
116
116
|
getFilteredComponents(scan, all) {
|
|
117
117
|
return Array.from(scan.components.values()).filter((component) => all || ['EOL', 'SUPPORTED'].includes(component.info.status));
|
|
118
118
|
}
|
|
119
|
-
async saveReport(components) {
|
|
119
|
+
async saveReport(components, createdOn) {
|
|
120
120
|
const { flags } = await this.parse(ScanEol);
|
|
121
121
|
const reportPath = path.join(flags.dir || process.cwd(), 'eol.report.json');
|
|
122
122
|
try {
|
|
123
|
-
fs.writeFileSync(reportPath, JSON.stringify({ components }, null, 2));
|
|
123
|
+
fs.writeFileSync(reportPath, JSON.stringify({ components, createdOn }, null, 2));
|
|
124
124
|
this.log('Report saved to eol.report.json');
|
|
125
125
|
}
|
|
126
126
|
catch (error) {
|
|
127
127
|
if (!isErrnoException(error)) {
|
|
128
128
|
this.error(`Failed to save report: ${getErrorMessage(error)}`);
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
this.error(`Failed to save report: ${getErrorMessage(error)}`);
|
|
130
|
+
if (error.code === 'EACCES') {
|
|
131
|
+
this.error('Permission denied. Unable to save report to eol.report.json');
|
|
132
|
+
}
|
|
133
|
+
else if (error.code === 'ENOSPC') {
|
|
134
|
+
this.error('No space left on device. Unable to save report to eol.report.json');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
this.error(`Failed to save report: ${getErrorMessage(error)}`);
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
140
|
}
|
package/dist/config/constants.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herodevs/cli",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.3",
|
|
4
4
|
"author": "HeroDevs, Inc",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hd": "./bin/run.js"
|
|
@@ -37,27 +37,27 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@apollo/client": "^3.13.8",
|
|
40
|
-
"@cyclonedx/cdxgen": "^11.2
|
|
41
|
-
"@oclif/core": "^4",
|
|
42
|
-
"@oclif/plugin-help": "^6",
|
|
43
|
-
"@oclif/plugin-update": "^4",
|
|
44
|
-
"@oclif/table": "^0.4.
|
|
40
|
+
"@cyclonedx/cdxgen": "^11.3.2",
|
|
41
|
+
"@oclif/core": "^4.3.1",
|
|
42
|
+
"@oclif/plugin-help": "^6.2.28",
|
|
43
|
+
"@oclif/plugin-update": "^4.6.42",
|
|
44
|
+
"@oclif/table": "^0.4.8",
|
|
45
45
|
"graphql": "^16.11.0",
|
|
46
46
|
"packageurl-js": "^2.0.1",
|
|
47
47
|
"update-notifier": "^7.3.1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@biomejs/biome": "^1.
|
|
51
|
-
"@oclif/test": "^4",
|
|
50
|
+
"@biomejs/biome": "^1.9.4",
|
|
51
|
+
"@oclif/test": "^4.1.13",
|
|
52
52
|
"@types/inquirer": "^9.0.8",
|
|
53
|
-
"@types/node": "^22",
|
|
53
|
+
"@types/node": "^22.15.30",
|
|
54
54
|
"@types/sinon": "^17.0.4",
|
|
55
55
|
"@types/update-notifier": "^6.0.8",
|
|
56
56
|
"globstar": "^1.0.0",
|
|
57
|
-
"oclif": "^4",
|
|
57
|
+
"oclif": "^4.18.0",
|
|
58
58
|
"shx": "^0.4.0",
|
|
59
59
|
"sinon": "^20.0.0",
|
|
60
|
-
"ts-node": "^10",
|
|
60
|
+
"ts-node": "^10.9.2",
|
|
61
61
|
"tsx": "^4.19.4",
|
|
62
62
|
"typescript": "^5.8.3"
|
|
63
63
|
},
|