@herodevs/cli 0.3.1 → 1.0.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.
Files changed (273) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +146 -6
  3. package/bin/dev.cmd +3 -0
  4. package/bin/dev.js +11 -0
  5. package/bin/run.cmd +3 -0
  6. package/bin/run.js +4 -0
  7. package/dist/api/client.d.ts +14 -0
  8. package/dist/api/client.js +42 -0
  9. package/dist/api/nes/nes.client.d.ts +16 -0
  10. package/dist/api/nes/nes.client.js +17 -0
  11. package/dist/api/queries/nes/sbom.d.ts +3 -0
  12. package/dist/api/queries/nes/sbom.js +29 -0
  13. package/{report-ingestion/src/lib/queries.d.ts → dist/api/queries/nes/telemetry.d.ts} +2 -2
  14. package/{report-ingestion/src/lib/queries.js → dist/api/queries/nes/telemetry.js} +3 -7
  15. package/dist/api/types/nes.types.d.ts +35 -0
  16. package/dist/api/types/nes.types.js +1 -0
  17. package/dist/commands/report/committers.d.ts +22 -0
  18. package/dist/commands/report/committers.js +104 -0
  19. package/dist/commands/report/purls.d.ts +13 -0
  20. package/dist/commands/report/purls.js +66 -0
  21. package/dist/commands/scan/eol.d.ts +16 -0
  22. package/dist/commands/scan/eol.js +71 -0
  23. package/dist/commands/scan/sbom.d.ts +18 -0
  24. package/dist/commands/scan/sbom.js +106 -0
  25. package/dist/config/update.d.ts +3 -0
  26. package/dist/config/update.js +7 -0
  27. package/dist/hooks/init/update.d.ts +2 -0
  28. package/dist/hooks/init/update.js +5 -0
  29. package/dist/hooks/prerun/CommandContextHook.d.ts +3 -0
  30. package/dist/hooks/prerun/CommandContextHook.js +8 -0
  31. package/dist/index.d.ts +1 -0
  32. package/dist/index.js +1 -0
  33. package/dist/service/committers.svc.d.ts +70 -0
  34. package/dist/service/committers.svc.js +175 -0
  35. package/dist/service/eol/cdx.svc.d.ts +22 -0
  36. package/dist/service/eol/cdx.svc.js +86 -0
  37. package/dist/service/eol/eol.svc.d.ts +34 -0
  38. package/dist/service/eol/eol.svc.js +86 -0
  39. package/dist/service/line.svc.d.ts +24 -0
  40. package/dist/service/line.svc.js +61 -0
  41. package/dist/service/log.svc.d.ts +10 -0
  42. package/dist/service/log.svc.js +20 -0
  43. package/dist/service/nes/nes.svc.d.ts +4 -0
  44. package/dist/service/nes/nes.svc.js +25 -0
  45. package/dist/service/purls.svc.d.ts +17 -0
  46. package/dist/service/purls.svc.js +29 -0
  47. package/dist/ui/eol.ui.d.ts +3 -0
  48. package/dist/ui/eol.ui.js +17 -0
  49. package/package.json +73 -16
  50. package/core-types/README.md +0 -11
  51. package/core-types/package.json +0 -12
  52. package/core-types/src/index.d.ts +0 -1
  53. package/core-types/src/index.js +0 -5
  54. package/core-types/src/index.js.map +0 -1
  55. package/core-types/src/lib/project-type.d.ts +0 -1
  56. package/core-types/src/lib/project-type.js +0 -3
  57. package/core-types/src/lib/project-type.js.map +0 -1
  58. package/init/README.md +0 -11
  59. package/init/package.json +0 -18
  60. package/init/src/index.d.ts +0 -1
  61. package/init/src/index.js +0 -5
  62. package/init/src/index.js.map +0 -1
  63. package/init/src/lib/configure-project.d.ts +0 -3
  64. package/init/src/lib/configure-project.js +0 -10
  65. package/init/src/lib/configure-project.js.map +0 -1
  66. package/init/src/lib/get-package-choices.d.ts +0 -2
  67. package/init/src/lib/get-package-choices.js +0 -16
  68. package/init/src/lib/get-package-choices.js.map +0 -1
  69. package/init/src/lib/get-product-choices.d.ts +0 -2
  70. package/init/src/lib/get-product-choices.js +0 -29
  71. package/init/src/lib/get-product-choices.js.map +0 -1
  72. package/init/src/lib/get-release-trains.d.ts +0 -2
  73. package/init/src/lib/get-release-trains.js +0 -57
  74. package/init/src/lib/get-release-trains.js.map +0 -1
  75. package/init/src/lib/init.d.ts +0 -5
  76. package/init/src/lib/init.js +0 -72
  77. package/init/src/lib/init.js.map +0 -1
  78. package/init/src/lib/mock-trains.d.ts +0 -2
  79. package/init/src/lib/mock-trains.js +0 -190
  80. package/init/src/lib/mock-trains.js.map +0 -1
  81. package/init/src/lib/models/choice.d.ts +0 -4
  82. package/init/src/lib/models/choice.js +0 -3
  83. package/init/src/lib/models/choice.js.map +0 -1
  84. package/init/src/lib/models/entry.d.ts +0 -6
  85. package/init/src/lib/models/entry.js +0 -3
  86. package/init/src/lib/models/entry.js.map +0 -1
  87. package/init/src/lib/models/index.d.ts +0 -5
  88. package/init/src/lib/models/index.js +0 -9
  89. package/init/src/lib/models/index.js.map +0 -1
  90. package/init/src/lib/models/package-version.d.ts +0 -14
  91. package/init/src/lib/models/package-version.js +0 -3
  92. package/init/src/lib/models/package-version.js.map +0 -1
  93. package/init/src/lib/models/product.d.ts +0 -5
  94. package/init/src/lib/models/product.js +0 -3
  95. package/init/src/lib/models/product.js.map +0 -1
  96. package/init/src/lib/models/release-train.d.ts +0 -9
  97. package/init/src/lib/models/release-train.js +0 -3
  98. package/init/src/lib/models/release-train.js.map +0 -1
  99. package/init/src/lib/npm/configure-npm-project.d.ts +0 -2
  100. package/init/src/lib/npm/configure-npm-project.js +0 -59
  101. package/init/src/lib/npm/configure-npm-project.js.map +0 -1
  102. package/init/src/lib/verify-project-type.d.ts +0 -6
  103. package/init/src/lib/verify-project-type.js +0 -19
  104. package/init/src/lib/verify-project-type.js.map +0 -1
  105. package/report-committers/README.md +0 -11
  106. package/report-committers/package.json +0 -15
  107. package/report-committers/src/index.d.ts +0 -1
  108. package/report-committers/src/index.js +0 -5
  109. package/report-committers/src/index.js.map +0 -1
  110. package/report-committers/src/lib/committers.d.ts +0 -11
  111. package/report-committers/src/lib/committers.js +0 -106
  112. package/report-committers/src/lib/committers.js.map +0 -1
  113. package/report-committers/src/lib/constants.d.ts +0 -4
  114. package/report-committers/src/lib/constants.js +0 -8
  115. package/report-committers/src/lib/constants.js.map +0 -1
  116. package/report-committers/src/lib/get-committer-counts.d.ts +0 -3
  117. package/report-committers/src/lib/get-committer-counts.js +0 -33
  118. package/report-committers/src/lib/get-committer-counts.js.map +0 -1
  119. package/report-committers/src/lib/parse-date-flags.d.ts +0 -4
  120. package/report-committers/src/lib/parse-date-flags.js +0 -11
  121. package/report-committers/src/lib/parse-date-flags.js.map +0 -1
  122. package/report-committers/src/lib/parse-git-log-entries.d.ts +0 -2
  123. package/report-committers/src/lib/parse-git-log-entries.js +0 -11
  124. package/report-committers/src/lib/parse-git-log-entries.js.map +0 -1
  125. package/report-committers/src/lib/parse-monthly.d.ts +0 -4
  126. package/report-committers/src/lib/parse-monthly.js +0 -56
  127. package/report-committers/src/lib/parse-monthly.js.map +0 -1
  128. package/report-committers/src/lib/types.d.ts +0 -27
  129. package/report-committers/src/lib/types.js +0 -3
  130. package/report-committers/src/lib/types.js.map +0 -1
  131. package/report-diagnostics/README.md +0 -11
  132. package/report-diagnostics/package.json +0 -15
  133. package/report-diagnostics/src/index.d.ts +0 -1
  134. package/report-diagnostics/src/index.js +0 -5
  135. package/report-diagnostics/src/index.js.map +0 -1
  136. package/report-diagnostics/src/lib/diagnostics.d.ts +0 -7
  137. package/report-diagnostics/src/lib/diagnostics.js +0 -94
  138. package/report-diagnostics/src/lib/diagnostics.js.map +0 -1
  139. package/report-diagnostics/src/lib/get-diagnostic-types.d.ts +0 -4
  140. package/report-diagnostics/src/lib/get-diagnostic-types.js +0 -15
  141. package/report-diagnostics/src/lib/get-diagnostic-types.js.map +0 -1
  142. package/report-diagnostics/src/lib/get-file-contents.d.ts +0 -1
  143. package/report-diagnostics/src/lib/get-file-contents.js +0 -12
  144. package/report-diagnostics/src/lib/get-file-contents.js.map +0 -1
  145. package/report-diagnostics/src/lib/get-package-json-section.d.ts +0 -1
  146. package/report-diagnostics/src/lib/get-package-json-section.js +0 -15
  147. package/report-diagnostics/src/lib/get-package-json-section.js.map +0 -1
  148. package/report-ingestion/README.md +0 -11
  149. package/report-ingestion/package.json +0 -13
  150. package/report-ingestion/src/index.d.ts +0 -1
  151. package/report-ingestion/src/index.js +0 -5
  152. package/report-ingestion/src/index.js.map +0 -1
  153. package/report-ingestion/src/lib/ingestion.d.ts +0 -3
  154. package/report-ingestion/src/lib/ingestion.js +0 -40
  155. package/report-ingestion/src/lib/ingestion.js.map +0 -1
  156. package/report-ingestion/src/lib/prompts.d.ts +0 -5
  157. package/report-ingestion/src/lib/prompts.js +0 -46
  158. package/report-ingestion/src/lib/prompts.js.map +0 -1
  159. package/report-ingestion/src/lib/queries.js.map +0 -1
  160. package/report-ingestion/src/lib/send-manifest.d.ts +0 -10
  161. package/report-ingestion/src/lib/send-manifest.js +0 -129
  162. package/report-ingestion/src/lib/send-manifest.js.map +0 -1
  163. package/report-ingestion/src/lib/types.d.ts +0 -6
  164. package/report-ingestion/src/lib/types.js +0 -3
  165. package/report-ingestion/src/lib/types.js.map +0 -1
  166. package/sea-config.json +0 -5
  167. package/src/lib/cli.d.ts +0 -1
  168. package/src/lib/cli.js +0 -22
  169. package/src/lib/cli.js.map +0 -1
  170. package/src/lib/create-group-command.d.ts +0 -2
  171. package/src/lib/create-group-command.js +0 -28
  172. package/src/lib/create-group-command.js.map +0 -1
  173. package/src/lib/ensure-version.d.ts +0 -2
  174. package/src/lib/ensure-version.js +0 -71
  175. package/src/lib/ensure-version.js.map +0 -1
  176. package/src/lib/get-commands.d.ts +0 -2
  177. package/src/lib/get-commands.js +0 -27
  178. package/src/lib/get-commands.js.map +0 -1
  179. package/src/lib/log-colors.d.ts +0 -28
  180. package/src/lib/log-colors.js +0 -9
  181. package/src/lib/log-colors.js.map +0 -1
  182. package/src/main.d.ts +0 -2
  183. package/src/main.js +0 -6
  184. package/src/main.js.map +0 -1
  185. package/tracker-init/README.md +0 -11
  186. package/tracker-init/package.json +0 -15
  187. package/tracker-init/src/index.d.ts +0 -1
  188. package/tracker-init/src/index.js +0 -5
  189. package/tracker-init/src/index.js.map +0 -1
  190. package/tracker-init/src/lib/default-config.d.ts +0 -2
  191. package/tracker-init/src/lib/default-config.js +0 -20
  192. package/tracker-init/src/lib/default-config.js.map +0 -1
  193. package/tracker-init/src/lib/init.d.ts +0 -5
  194. package/tracker-init/src/lib/init.js +0 -25
  195. package/tracker-init/src/lib/init.js.map +0 -1
  196. package/tracker-run/README.md +0 -11
  197. package/tracker-run/package.json +0 -18
  198. package/tracker-run/src/index.d.ts +0 -1
  199. package/tracker-run/src/index.js +0 -5
  200. package/tracker-run/src/index.js.map +0 -1
  201. package/tracker-run/src/lib/get-data-filepath.d.ts +0 -1
  202. package/tracker-run/src/lib/get-data-filepath.js +0 -8
  203. package/tracker-run/src/lib/get-data-filepath.js.map +0 -1
  204. package/tracker-run/src/lib/get-data.d.ts +0 -1
  205. package/tracker-run/src/lib/get-data.js +0 -14
  206. package/tracker-run/src/lib/get-data.js.map +0 -1
  207. package/tracker-run/src/lib/get-git-commit.d.ts +0 -4
  208. package/tracker-run/src/lib/get-git-commit.js +0 -32
  209. package/tracker-run/src/lib/get-git-commit.js.map +0 -1
  210. package/tracker-run/src/lib/process-category.d.ts +0 -2
  211. package/tracker-run/src/lib/process-category.js +0 -151
  212. package/tracker-run/src/lib/process-category.js.map +0 -1
  213. package/tracker-run/src/lib/process-config.d.ts +0 -2
  214. package/tracker-run/src/lib/process-config.js +0 -19
  215. package/tracker-run/src/lib/process-config.js.map +0 -1
  216. package/tracker-run/src/lib/run.d.ts +0 -7
  217. package/tracker-run/src/lib/run.js +0 -29
  218. package/tracker-run/src/lib/run.js.map +0 -1
  219. package/tracker-run/src/lib/save-results.d.ts +0 -2
  220. package/tracker-run/src/lib/save-results.js +0 -19
  221. package/tracker-run/src/lib/save-results.js.map +0 -1
  222. package/tracker-shared/README.md +0 -11
  223. package/tracker-shared/package.json +0 -12
  224. package/tracker-shared/src/index.d.ts +0 -2
  225. package/tracker-shared/src/index.js +0 -14
  226. package/tracker-shared/src/index.js.map +0 -1
  227. package/tracker-shared/src/lib/models/aggregate-result.d.ts +0 -4
  228. package/tracker-shared/src/lib/models/aggregate-result.js +0 -3
  229. package/tracker-shared/src/lib/models/aggregate-result.js.map +0 -1
  230. package/tracker-shared/src/lib/models/category-result.d.ts +0 -7
  231. package/tracker-shared/src/lib/models/category-result.js +0 -3
  232. package/tracker-shared/src/lib/models/category-result.js.map +0 -1
  233. package/tracker-shared/src/lib/models/category.d.ts +0 -9
  234. package/tracker-shared/src/lib/models/category.js +0 -3
  235. package/tracker-shared/src/lib/models/category.js.map +0 -1
  236. package/tracker-shared/src/lib/models/config.d.ts +0 -8
  237. package/tracker-shared/src/lib/models/config.js +0 -3
  238. package/tracker-shared/src/lib/models/config.js.map +0 -1
  239. package/tracker-shared/src/lib/models/file-result.d.ts +0 -5
  240. package/tracker-shared/src/lib/models/file-result.js +0 -3
  241. package/tracker-shared/src/lib/models/file-result.js.map +0 -1
  242. package/tracker-shared/src/lib/models/index.d.ts +0 -8
  243. package/tracker-shared/src/lib/models/index.js +0 -12
  244. package/tracker-shared/src/lib/models/index.js.map +0 -1
  245. package/tracker-shared/src/lib/models/process-result.d.ts +0 -6
  246. package/tracker-shared/src/lib/models/process-result.js +0 -3
  247. package/tracker-shared/src/lib/models/process-result.js.map +0 -1
  248. package/tracker-shared/src/lib/models/result.d.ts +0 -11
  249. package/tracker-shared/src/lib/models/result.js +0 -3
  250. package/tracker-shared/src/lib/models/result.js.map +0 -1
  251. package/tracker-shared/src/lib/models/total-result.d.ts +0 -4
  252. package/tracker-shared/src/lib/models/total-result.js +0 -3
  253. package/tracker-shared/src/lib/models/total-result.js.map +0 -1
  254. package/tracker-shared/src/lib/read-config.d.ts +0 -2
  255. package/tracker-shared/src/lib/read-config.js +0 -13
  256. package/tracker-shared/src/lib/read-config.js.map +0 -1
  257. package/utility/README.md +0 -11
  258. package/utility/package.json +0 -13
  259. package/utility/src/index.d.ts +0 -4
  260. package/utility/src/index.js +0 -8
  261. package/utility/src/index.js.map +0 -1
  262. package/utility/src/lib/get-project-types.d.ts +0 -2
  263. package/utility/src/lib/get-project-types.js +0 -13
  264. package/utility/src/lib/get-project-types.js.map +0 -1
  265. package/utility/src/lib/get-root-dir.d.ts +0 -1
  266. package/utility/src/lib/get-root-dir.js +0 -12
  267. package/utility/src/lib/get-root-dir.js.map +0 -1
  268. package/utility/src/lib/run-command.d.ts +0 -1
  269. package/utility/src/lib/run-command.js +0 -31
  270. package/utility/src/lib/run-command.js.map +0 -1
  271. package/utility/src/lib/sort-by-name.d.ts +0 -3
  272. package/utility/src/lib/sort-by-name.js +0 -7
  273. package/utility/src/lib/sort-by-name.js.map +0 -1
@@ -0,0 +1,71 @@
1
+ import { Command, Flags, ux } from '@oclif/core';
2
+ import { prepareRows, scanForEol } from "../../service/eol/eol.svc.js";
3
+ import { promptComponentDetails } from "../../ui/eol.ui.js";
4
+ import SbomScan from "./sbom.js";
5
+ export default class ScanEol extends Command {
6
+ static description = 'Scan a given sbom for EOL data';
7
+ static enableJsonFlag = true;
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> --dir=./my-project',
10
+ '<%= config.bin %> <%= command.id %> --file=path/to/sbom.json',
11
+ ];
12
+ static flags = {
13
+ file: Flags.string({
14
+ char: 'f',
15
+ description: 'The file path of an existing cyclonedx sbom to scan for EOL',
16
+ }),
17
+ dir: Flags.string({
18
+ char: 'd',
19
+ description: 'The directory to scan in order to create a cyclonedx sbom',
20
+ }),
21
+ save: Flags.boolean({
22
+ char: 's',
23
+ default: false,
24
+ description: 'Save the generated SBOM as nes.sbom.json in the scanned directory',
25
+ }),
26
+ };
27
+ async run() {
28
+ this.checkEolScanDisabled();
29
+ const { flags } = await this.parse(ScanEol);
30
+ const { dir: _dirFlag, file: _fileFlag } = flags;
31
+ // Load the SBOM: Only pass the file, dir, and save flags to SbomScan
32
+ const sbomArgs = SbomScan.getSbomArgs(flags);
33
+ const sbomCommand = new SbomScan(sbomArgs, this.config);
34
+ const sbom = await sbomCommand.run();
35
+ // Scan the SBOM for EOL information
36
+ const { purls, scan } = await scanForEol(sbom);
37
+ ux.action.stop('Scan completed');
38
+ if (!scan?.components) {
39
+ if (_fileFlag) {
40
+ throw new Error(`Scan failed to generate for file path: ${_fileFlag}`);
41
+ }
42
+ if (_dirFlag) {
43
+ throw new Error(`Scan failed to generate for dir: ${_dirFlag}`);
44
+ }
45
+ throw new Error('Scan failed to generate components.');
46
+ }
47
+ const lines = await prepareRows(purls, scan);
48
+ if (lines?.length === 0) {
49
+ this.log('No dependencies found');
50
+ return { components: [] };
51
+ }
52
+ const r = await promptComponentDetails(lines);
53
+ this.log('What now %o', r);
54
+ return scan;
55
+ }
56
+ checkEolScanDisabled(override = true) {
57
+ // Check if running in beta version or pre v1.0.0
58
+ const version = this.config.version;
59
+ const [major] = version.split('.').map(Number);
60
+ if (version.includes('beta') || major < 1) {
61
+ this.log(`VERSION=${version}`);
62
+ throw new Error('The EOL scan feature is not available in beta releases. Please wait for the stable release.');
63
+ }
64
+ // Just in case the beta check fails
65
+ if (override) {
66
+ this.log(`VERSION=${version}`);
67
+ this.log('EOL scan is disabled');
68
+ return { components: [] };
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,18 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { Sbom } from '../../service/eol/cdx.svc.ts';
3
+ export default class ScanSbom extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ dir: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ save: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ static getSbomArgs(flags: Record<string, string>): string[];
13
+ getScanOptions(): {};
14
+ run(): Promise<Sbom>;
15
+ private _getSbomFromScan;
16
+ private _getSbomFromFile;
17
+ private _saveSbom;
18
+ }
@@ -0,0 +1,106 @@
1
+ import path from 'node:path';
2
+ import { Command, Flags, ux } from '@oclif/core';
3
+ import fs from 'node:fs';
4
+ import { createSbom, validateIsCycloneDxSbom } from "../../service/eol/eol.svc.js";
5
+ export default class ScanSbom extends Command {
6
+ static description = 'Scan a SBOM for purls';
7
+ static enableJsonFlag = true;
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> --dir=./my-project',
10
+ '<%= config.bin %> <%= command.id %> --file=path/to/sbom.json',
11
+ ];
12
+ static flags = {
13
+ file: Flags.string({
14
+ char: 'f',
15
+ description: 'The file path of an existing cyclonedx sbom to scan for EOL',
16
+ }),
17
+ dir: Flags.string({
18
+ char: 'd',
19
+ description: 'The directory to scan in order to create a cyclonedx sbom',
20
+ }),
21
+ save: Flags.boolean({
22
+ char: 's',
23
+ default: false,
24
+ description: 'Save the generated SBOM as nes.sbom.json in the scanned directory',
25
+ }),
26
+ };
27
+ static getSbomArgs(flags) {
28
+ const { dir, file, save } = flags ?? {};
29
+ const sbomArgs = [];
30
+ if (file)
31
+ sbomArgs.push('--file', file);
32
+ if (dir)
33
+ sbomArgs.push('--dir', dir);
34
+ if (save)
35
+ sbomArgs.push('--save');
36
+ return sbomArgs;
37
+ }
38
+ getScanOptions() {
39
+ // intentionally provided for mocking
40
+ return {};
41
+ }
42
+ async run() {
43
+ const { flags } = await this.parse(ScanSbom);
44
+ const { dir: _dirFlag, save, file: _fileFlag } = flags;
45
+ // Validate that exactly one of --file or --dir is provided
46
+ if (_fileFlag && _dirFlag) {
47
+ throw new Error('Cannot specify both --file and --dir flags. Please use one or the other.');
48
+ }
49
+ let sbom;
50
+ if (_fileFlag) {
51
+ sbom = this._getSbomFromFile(_fileFlag);
52
+ }
53
+ else {
54
+ const _dir = _dirFlag || process.cwd();
55
+ sbom = await this._getSbomFromScan(_dir);
56
+ if (save) {
57
+ this._saveSbom(_dir, sbom);
58
+ }
59
+ }
60
+ return sbom;
61
+ }
62
+ async _getSbomFromScan(_dirFlag) {
63
+ const dir = path.resolve(_dirFlag);
64
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
65
+ throw new Error(`Directory not found or not a directory: ${dir}`);
66
+ }
67
+ ux.action.start(`Scanning ${dir}`);
68
+ const options = this.getScanOptions();
69
+ const sbom = await createSbom(dir, options);
70
+ if (!sbom) {
71
+ throw new Error(`SBOM failed to generate for dir: ${dir}`);
72
+ }
73
+ return sbom;
74
+ }
75
+ _getSbomFromFile(_fileFlag) {
76
+ const file = path.resolve(_fileFlag);
77
+ if (!fs.existsSync(file)) {
78
+ throw new Error(`SBOM file not found: ${file}`);
79
+ }
80
+ ux.action.start(`Loading sbom from ${file}`);
81
+ try {
82
+ const fileContent = fs.readFileSync(file, {
83
+ encoding: 'utf8',
84
+ flag: 'r',
85
+ });
86
+ const sbom = JSON.parse(fileContent);
87
+ validateIsCycloneDxSbom(sbom);
88
+ return sbom;
89
+ }
90
+ catch (error) {
91
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
92
+ throw new Error(`Failed to read or parse SBOM file: ${errorMessage}`);
93
+ }
94
+ }
95
+ _saveSbom(dir, sbom) {
96
+ try {
97
+ const outputPath = path.join(dir, 'nes.sbom.json');
98
+ fs.writeFileSync(outputPath, JSON.stringify(sbom, null, 2));
99
+ this.log(`SBOM saved to ${outputPath}`);
100
+ }
101
+ catch (error) {
102
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
103
+ this.warn(`Failed to save SBOM: ${errorMessage}`);
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,3 @@
1
+ import type { Config } from '@oclif/core';
2
+ export declare const updateConfig: NonNullable<Config['updateConfig']>;
3
+ export default updateConfig;
@@ -0,0 +1,7 @@
1
+ export const updateConfig = {
2
+ autoupdate: {
3
+ debounce: 24 * 60 * 60 * 1000, // 24 hours in milliseconds
4
+ rollout: 100, // 100% rollout
5
+ },
6
+ };
7
+ export default updateConfig;
@@ -0,0 +1,2 @@
1
+ import type { Hook } from '@oclif/core';
2
+ export declare const initUpdate: Hook<'init'>;
@@ -0,0 +1,5 @@
1
+ import updateConfig from '../../config/update.js';
2
+ export const initUpdate = async ({ config }) => {
3
+ // Apply update configuration
4
+ Object.assign(config, updateConfig);
5
+ };
@@ -0,0 +1,3 @@
1
+ import type { Hook } from '@oclif/core';
2
+ declare const hook: Hook<'prerun'>;
3
+ export default hook;
@@ -0,0 +1,8 @@
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;
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,70 @@
1
+ export interface CommitEntry {
2
+ month: string;
3
+ author: string;
4
+ }
5
+ export interface AuthorCommitCounts {
6
+ [author: string]: number;
7
+ }
8
+ export interface MonthlyData {
9
+ [month: string]: AuthorCommitCounts;
10
+ }
11
+ export interface ReportData {
12
+ monthly: {
13
+ [month: string]: {
14
+ [author: string]: number;
15
+ total: number;
16
+ };
17
+ };
18
+ overall: {
19
+ [author: string]: number;
20
+ total: number;
21
+ };
22
+ }
23
+ /**
24
+ * Parses git log output into structured data
25
+ * @param output - Git log command output
26
+ * @returns Parsed commit entries
27
+ */
28
+ export declare function parseGitLogOutput(output: string): CommitEntry[];
29
+ /**
30
+ * Groups commit data by month
31
+ * @param entries - Commit entries
32
+ * @returns Object with months as keys and author commit counts as values
33
+ */
34
+ export declare function groupCommitsByMonth(entries: CommitEntry[]): MonthlyData;
35
+ /**
36
+ * Calculates overall commit statistics by author
37
+ * @param entries - Commit entries
38
+ * @returns Object with authors as keys and total commit counts as values
39
+ */
40
+ export declare function calculateOverallStats(entries: CommitEntry[]): AuthorCommitCounts;
41
+ /**
42
+ * Formats monthly report sections
43
+ * @param monthlyData - Grouped commit data by month
44
+ * @returns Formatted monthly report sections
45
+ */
46
+ export declare function formatMonthlyReport(monthlyData: MonthlyData): string;
47
+ /**
48
+ * Formats overall statistics section
49
+ * @param overallStats - Overall commit counts by author
50
+ * @param grandTotal - Total number of commits
51
+ * @returns Formatted overall statistics section
52
+ */
53
+ export declare function formatOverallStats(overallStats: AuthorCommitCounts, grandTotal: number): string;
54
+ /**
55
+ * Formats the report data as CSV
56
+ * @param data - The structured report data
57
+ */
58
+ export declare function formatAsCsv(data: ReportData): string;
59
+ /**
60
+ * Formats the report data as text
61
+ * @param data - The structured report data
62
+ */
63
+ export declare function formatAsText(data: ReportData): string;
64
+ /**
65
+ * Format output based on user preference
66
+ * @param output
67
+ * @param reportData
68
+ * @returns
69
+ */
70
+ export declare function formatOutputBasedOnFlag(output: string, reportData: ReportData): string;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Parses git log output into structured data
3
+ * @param output - Git log command output
4
+ * @returns Parsed commit entries
5
+ */
6
+ export function parseGitLogOutput(output) {
7
+ return output
8
+ .split('\n')
9
+ .filter(Boolean)
10
+ .map((line) => {
11
+ // Remove surrounding double quotes if present (e.g. "March|John Doe" → March|John Doe)
12
+ const [month, author] = line.replace(/^"(.*)"$/, '$1').split('|');
13
+ return { month, author };
14
+ });
15
+ }
16
+ /**
17
+ * Groups commit data by month
18
+ * @param entries - Commit entries
19
+ * @returns Object with months as keys and author commit counts as values
20
+ */
21
+ export function groupCommitsByMonth(entries) {
22
+ const result = {};
23
+ // Group commits by month
24
+ const commitsByMonth = Object.groupBy(entries, (entry) => entry.month);
25
+ // Process each month
26
+ for (const [month, commits] of Object.entries(commitsByMonth)) {
27
+ if (!commits) {
28
+ result[month] = {};
29
+ continue;
30
+ }
31
+ // Count commits per author for this month
32
+ const commitsByAuthor = Object.groupBy(commits, (entry) => entry.author);
33
+ const authorCounts = {};
34
+ for (const [author, authorCommits] of Object.entries(commitsByAuthor)) {
35
+ authorCounts[author] = authorCommits?.length ?? 0;
36
+ }
37
+ result[month] = authorCounts;
38
+ }
39
+ return result;
40
+ }
41
+ /**
42
+ * Calculates overall commit statistics by author
43
+ * @param entries - Commit entries
44
+ * @returns Object with authors as keys and total commit counts as values
45
+ */
46
+ export function calculateOverallStats(entries) {
47
+ const commitsByAuthor = Object.groupBy(entries, (entry) => entry.author);
48
+ const result = {};
49
+ // Count commits for each author
50
+ for (const author in commitsByAuthor) {
51
+ result[author] = commitsByAuthor[author]?.length ?? 0;
52
+ }
53
+ return result;
54
+ }
55
+ /**
56
+ * Formats monthly report sections
57
+ * @param monthlyData - Grouped commit data by month
58
+ * @returns Formatted monthly report sections
59
+ */
60
+ export function formatMonthlyReport(monthlyData) {
61
+ const sortedMonths = Object.keys(monthlyData).sort();
62
+ let report = '';
63
+ for (const month of sortedMonths) {
64
+ report += `\n## ${month}\n`;
65
+ const authors = Object.entries(monthlyData[month]).sort((a, b) => b[1] - a[1]);
66
+ for (const [author, count] of authors) {
67
+ report += `${count.toString().padStart(6)} ${author}\n`;
68
+ }
69
+ const monthTotal = authors.reduce((sum, [_, count]) => sum + count, 0);
70
+ report += `${monthTotal.toString().padStart(6)} TOTAL\n`;
71
+ }
72
+ return report;
73
+ }
74
+ /**
75
+ * Formats overall statistics section
76
+ * @param overallStats - Overall commit counts by author
77
+ * @param grandTotal - Total number of commits
78
+ * @returns Formatted overall statistics section
79
+ */
80
+ export function formatOverallStats(overallStats, grandTotal) {
81
+ let report = '\n## Overall Statistics\n';
82
+ const sortedStats = Object.entries(overallStats).sort((a, b) => b[1] - a[1]);
83
+ for (const [author, count] of sortedStats) {
84
+ report += `${count.toString().padStart(6)} ${author}\n`;
85
+ }
86
+ report += `${grandTotal.toString().padStart(6)} GRAND TOTAL\n`;
87
+ return report;
88
+ }
89
+ /**
90
+ * Formats the report data as CSV
91
+ * @param data - The structured report data
92
+ */
93
+ export function formatAsCsv(data) {
94
+ // First prepare all author names (for columns)
95
+ const allAuthors = new Set();
96
+ // Collect all unique author names
97
+ for (const monthData of Object.values(data.monthly)) {
98
+ for (const author of Object.keys(monthData)) {
99
+ if (author !== 'total')
100
+ allAuthors.add(author);
101
+ }
102
+ }
103
+ const authors = Array.from(allAuthors).sort();
104
+ // Create CSV header
105
+ let csv = `Month,${authors.join(',')},Total\n`;
106
+ // Add monthly data rows
107
+ const sortedMonths = Object.keys(data.monthly).sort();
108
+ for (const month of sortedMonths) {
109
+ csv += month;
110
+ // Add data for each author
111
+ for (const author of authors) {
112
+ const count = data.monthly[month][author] || 0;
113
+ csv += `,${count}`;
114
+ }
115
+ // Add monthly total
116
+ csv += `,${`${data.monthly[month].total}\n`}`;
117
+ }
118
+ // Add overall totals row
119
+ csv += 'Overall';
120
+ for (const author of authors) {
121
+ const count = data.overall[author] || 0;
122
+ csv += `,${count}`;
123
+ }
124
+ csv += `,${data.overall.total}\n`;
125
+ return csv;
126
+ }
127
+ /**
128
+ * Formats the report data as text
129
+ * @param data - The structured report data
130
+ */
131
+ export function formatAsText(data) {
132
+ let report = 'Monthly Commit Report\n';
133
+ // Monthly sections
134
+ const sortedMonths = Object.keys(data.monthly).sort();
135
+ for (const month of sortedMonths) {
136
+ report += `\n## ${month}\n`;
137
+ const authors = Object.entries(data.monthly[month])
138
+ .filter(([author]) => author !== 'total')
139
+ .sort((a, b) => b[1] - a[1]);
140
+ for (const [author, count] of authors) {
141
+ report += `${count.toString().padStart(6)} ${author}\n`;
142
+ }
143
+ report += `${data.monthly[month].total.toString().padStart(6)} TOTAL\n`;
144
+ }
145
+ // Overall statistics
146
+ report += '\n## Overall Statistics\n';
147
+ const sortedEntries = Object.entries(data.overall)
148
+ .filter(([author]) => author !== 'total')
149
+ .sort((a, b) => b[1] - a[1]);
150
+ for (const [author, count] of sortedEntries) {
151
+ report += `${count.toString().padStart(6)} ${author}\n`;
152
+ }
153
+ report += `${data.overall.total.toString().padStart(6)} GRAND TOTAL\n`;
154
+ return report;
155
+ }
156
+ /**
157
+ * Format output based on user preference
158
+ * @param output
159
+ * @param reportData
160
+ * @returns
161
+ */
162
+ export function formatOutputBasedOnFlag(output, reportData) {
163
+ let formattedOutput;
164
+ switch (output) {
165
+ case 'json':
166
+ formattedOutput = JSON.stringify(reportData, null, 2);
167
+ break;
168
+ case 'csv':
169
+ formattedOutput = formatAsCsv(reportData);
170
+ break;
171
+ default:
172
+ formattedOutput = formatAsText(reportData);
173
+ }
174
+ return formattedOutput;
175
+ }
@@ -0,0 +1,22 @@
1
+ import type { CdxCreator, CdxGenOptions } from './eol.svc.ts';
2
+ export interface SbomEntry {
3
+ group: string;
4
+ name: string;
5
+ purl: string;
6
+ version: string;
7
+ }
8
+ export interface Sbom {
9
+ components: SbomEntry[];
10
+ dependencies: SbomEntry[];
11
+ }
12
+ /**
13
+ * Lazy loads cdxgen (for ESM purposes), scans
14
+ * `directory`, and returns the `bomJson` property.
15
+ */
16
+ export declare function createBomFromDir(directory: string, opts?: CdxGenOptions): Promise<Sbom | undefined>;
17
+ export declare const cdxgen: {
18
+ createBom: CdxCreator | undefined;
19
+ };
20
+ export declare function getCdxGen(): Promise<{
21
+ createBom: CdxCreator | undefined;
22
+ }>;
@@ -0,0 +1,86 @@
1
+ import { log } from "../../service/log.svc.js";
2
+ /**
3
+ * Lazy loads cdxgen (for ESM purposes), scans
4
+ * `directory`, and returns the `bomJson` property.
5
+ */
6
+ 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
+ const { createBom } = await getCdxGen();
67
+ const sbom = await createBom?.(directory, options);
68
+ log.info('Successfully generated SBOM');
69
+ return sbom?.bomJson;
70
+ }
71
+ // use a value holder, for easier mocking
72
+ export const cdxgen = { createBom: undefined };
73
+ export async function getCdxGen() {
74
+ if (cdxgen.createBom) {
75
+ return cdxgen;
76
+ }
77
+ const ogEnv = process.env.NODE_ENV;
78
+ process.env.NODE_ENV = undefined;
79
+ try {
80
+ cdxgen.createBom = (await import('@cyclonedx/cdxgen')).createBom;
81
+ }
82
+ finally {
83
+ process.env.NODE_ENV = ogEnv;
84
+ }
85
+ return cdxgen;
86
+ }
@@ -0,0 +1,34 @@
1
+ import type { ScanResult } from '../../api/types/nes.types.ts';
2
+ import type { Line } from '../line.svc.ts';
3
+ import { type Sbom } from './cdx.svc.ts';
4
+ export interface CdxGenOptions {
5
+ projectType?: string[];
6
+ }
7
+ export interface ScanOptions {
8
+ cdxgen?: CdxGenOptions;
9
+ }
10
+ export type CdxCreator = (dir: string, opts: CdxGenOptions) => Promise<{
11
+ bomJson: Sbom;
12
+ }>;
13
+ export declare function createSbom(directory: string, opts?: ScanOptions): Promise<Sbom>;
14
+ export declare function validateIsCycloneDxSbom(sbom: unknown): asserts sbom is Sbom;
15
+ /**
16
+ * Main function to scan directory and collect SBOM data
17
+ */
18
+ export declare function scanForEol(sbom: Sbom): Promise<{
19
+ purls: string[];
20
+ scan: ScanResult;
21
+ }>;
22
+ /**
23
+ * Uses the purls from the sbom to run the scan.
24
+ */
25
+ export declare function submitScan(purls: string[]): Promise<ScanResult>;
26
+ /**
27
+ * Work in progress; creates "rows" for each component
28
+ * based on the model + the scan result from NES.
29
+ *
30
+ * The idea being that each row can easily be used for
31
+ * processing and/or rendering.
32
+ */
33
+ export declare function prepareRows(purls: string[], scan: ScanResult): Promise<Line[]>;
34
+ export { cdxgen } from './cdx.svc.ts';