@slicedearth/whoisleuth-cli 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/DISCLOSURE +1 -1
  2. package/NOTICE +2 -2
  3. package/cli/argument-values.mjs +55 -0
  4. package/cli/arguments.mjs +24 -470
  5. package/cli/artifact-validation/case-response.mjs +67 -9
  6. package/cli/artifact-validation/lookup-evidence.mjs +18 -4
  7. package/cli/artifact-validation/signed-review.mjs +61 -64
  8. package/cli/artifact-verify.mjs +11 -2
  9. package/cli/assurance-arguments.mjs +56 -0
  10. package/cli/assurance-command-definitions.mjs +179 -0
  11. package/cli/assurance-command-runner.mjs +1 -1
  12. package/cli/bounded-json.mjs +1 -1
  13. package/cli/bulk-checkpoint.mjs +1 -1
  14. package/cli/bulk-command-runner.mjs +6 -8
  15. package/cli/bulk.mjs +2 -3
  16. package/cli/case-command.mjs +99 -6
  17. package/cli/case-pack.mjs +19 -4
  18. package/cli/ci-report.mjs +6 -3
  19. package/cli/collection-arguments.mjs +123 -0
  20. package/cli/collection-command-definitions.mjs +330 -0
  21. package/cli/collection-preflight.mjs +8 -1
  22. package/cli/command-argument-grammar.mjs +8 -210
  23. package/cli/command-definition.mjs +330 -0
  24. package/cli/command-reference.mjs +55 -1388
  25. package/cli/command-seeds.mjs +18 -0
  26. package/cli/compare.mjs +1 -1
  27. package/cli/config-profile.mjs +1 -1
  28. package/cli/ct-event-intake.mjs +4 -4
  29. package/cli/discover.mjs +2 -2
  30. package/cli/discovery-observation-snapshot.mjs +2 -2
  31. package/cli/discovery-scan-command-runner.mjs +7 -9
  32. package/cli/discovery-scan.mjs +1 -1
  33. package/cli/discovery-snapshot.mjs +2 -2
  34. package/cli/discovery-workflow.mjs +4 -4
  35. package/cli/domain-control-monitor.mjs +6 -6
  36. package/cli/domain-control-observations.mjs +2 -1
  37. package/cli/domain-feed.mjs +61 -0
  38. package/cli/errors.mjs +5 -6
  39. package/cli/evidence-arguments.mjs +46 -0
  40. package/cli/evidence-command-definitions.mjs +69 -0
  41. package/cli/evidence-command-runner.mjs +2 -3
  42. package/cli/evidence-signing.mjs +3 -2
  43. package/cli/external-observation-mapping.mjs +2 -2
  44. package/cli/formatters/json.mjs +1 -13
  45. package/cli/formatters/terminal-command-formats.mjs +6 -0
  46. package/cli/formatters/terminal-lookup.mjs +6 -0
  47. package/cli/history-arguments.mjs +39 -0
  48. package/cli/history-command-definitions.mjs +88 -0
  49. package/cli/indicator-set-command.mjs +32 -0
  50. package/cli/inline-command-families.mjs +6 -47
  51. package/cli/inline-command-runner.mjs +4 -31
  52. package/cli/input.mjs +2 -2
  53. package/cli/intake-command.mjs +147 -0
  54. package/cli/interchange-report.mjs +4 -2
  55. package/cli/investigation-artifacts.mjs +3 -4
  56. package/cli/investigation-package-review.mjs +2 -0
  57. package/cli/investigation-plan.mjs +32 -154
  58. package/cli/investigation-recipes.mjs +177 -0
  59. package/cli/investigation-run.mjs +78 -12
  60. package/cli/local-mmdb-review.mjs +220 -17
  61. package/cli/lookalike-calibration.mjs +1 -1
  62. package/cli/lookup-brief.mjs +2 -2
  63. package/cli/lookup-command-runner.mjs +19 -35
  64. package/cli/lookup-diff.mjs +10 -9
  65. package/cli/lookup-plan.mjs +10 -14
  66. package/cli/lookup-reconcile.mjs +6 -5
  67. package/cli/lookup-timeline.mjs +7 -6
  68. package/cli/mail-header-review.mjs +24 -247
  69. package/cli/mail-review.mjs +9 -7
  70. package/cli/network-arguments.mjs +51 -0
  71. package/cli/network-command-definitions.mjs +130 -0
  72. package/cli/network-command-runner.mjs +43 -31
  73. package/cli/offline-evidence-review.mjs +85 -6
  74. package/cli/posture.mjs +1 -1
  75. package/cli/registry-fixture-scaffold.mjs +1 -1
  76. package/cli/retained-artifact-diff.mjs +8 -7
  77. package/cli/review-arguments.mjs +132 -0
  78. package/cli/review-command-definitions.mjs +279 -0
  79. package/cli/review-command-runner.mjs +72 -159
  80. package/cli/risk-calibration.mjs +14 -12
  81. package/cli/runner.mjs +20 -32
  82. package/cli/saved-lookup.mjs +2 -2
  83. package/cli/selected-input-worker.mjs +73 -0
  84. package/cli/sharing-review.mjs +5 -64
  85. package/cli/source-reliability.mjs +2 -2
  86. package/cli/support-arguments.mjs +49 -0
  87. package/cli/support-command-definitions.mjs +209 -0
  88. package/cli/support-command-runner.mjs +4 -4
  89. package/cli/watchlist-review.mjs +43 -0
  90. package/cli/workflow-arguments.mjs +100 -0
  91. package/cli/workflow-command-definitions.mjs +103 -0
  92. package/cli/workflow-command-runner.mjs +40 -1
  93. package/docs/cli-reference.md +27 -0
  94. package/docs/cli.md +322 -4
  95. package/docs/contextual-reviews.md +178 -0
  96. package/docs/infrastructure-observations.md +126 -0
  97. package/docs/offline-intake-context.md +213 -0
  98. package/lib/analyst-taxonomy.mjs +2 -40
  99. package/lib/application-version.mjs +1 -1
  100. package/lib/availability.mjs +13 -789
  101. package/lib/bounded-contract-normalizers.mjs +4 -0
  102. package/lib/bounded-json.mjs +2 -330
  103. package/lib/browser-library-profile.mjs +62 -84
  104. package/lib/candidate-provenance-bounds.mjs +2 -0
  105. package/lib/classify.mjs +1 -1
  106. package/lib/credential-form-attribution.mjs +136 -0
  107. package/lib/credential-surface-profile.mjs +6 -2
  108. package/lib/ct-query.mjs +2 -27
  109. package/lib/ct-response-bounds.mjs +2 -9
  110. package/lib/ct-search.mjs +83 -47
  111. package/lib/dmarc-discovery.mjs +62 -0
  112. package/lib/dns-convergence-review.mjs +21 -8
  113. package/lib/dns-delegation-health.mjs +35 -15
  114. package/lib/dns-inheritance-review.mjs +53 -63
  115. package/lib/dns-intelligence.mjs +27 -11
  116. package/lib/dns-query-coverage.mjs +40 -0
  117. package/lib/domain-assurance.mjs +1 -1
  118. package/lib/domain-change-packet.mjs +12 -13
  119. package/lib/domain-change-review.mjs +32 -14
  120. package/lib/domain-control-flight-recorder.mjs +103 -27
  121. package/lib/domain-control-manifest.mjs +25 -17
  122. package/lib/domain-posture-analysis.mjs +75 -7
  123. package/lib/domain-posture-parsers.mjs +9 -0
  124. package/lib/domain-posture.mjs +45 -25
  125. package/lib/domain-registration.mjs +392 -0
  126. package/lib/evidence-export-privacy.mjs +27 -17
  127. package/lib/evidence-export.mjs +3 -3
  128. package/lib/evidence-report.mjs +5 -1
  129. package/lib/external-intelligence-risk.mjs +2 -124
  130. package/lib/favicon.mjs +13 -4
  131. package/lib/generated/cisa-kev-catalog.mjs +41 -3
  132. package/lib/generated/registrar-standing-catalogue.mjs +18 -10
  133. package/lib/generated/retire-browser-catalog.mjs +332 -19
  134. package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
  135. package/lib/html-signals.mjs +13 -2
  136. package/lib/http-evidence-bounds.mjs +2 -8
  137. package/lib/http-intelligence.mjs +13 -1
  138. package/lib/idn-confusable-policy.mjs +6 -4
  139. package/lib/idn-confusables.mjs +1 -1
  140. package/lib/interchange-fidelity-registry.mjs +10 -0
  141. package/lib/lookup-child-profile-contract.mjs +28 -11
  142. package/lib/lookup-contract-primitives.mjs +1 -1
  143. package/lib/lookup-diagnostics.mjs +1 -23
  144. package/lib/lookup-network-evidence-bounds.mjs +1 -10
  145. package/lib/lookup-progress.mjs +50 -1
  146. package/lib/lookup-response-contract.mjs +70 -40
  147. package/lib/lookup-response.mjs +5 -15
  148. package/lib/lookup-source-progress.mjs +8 -22
  149. package/lib/lookup.mjs +1 -1
  150. package/lib/operation-budget.mjs +4 -0
  151. package/lib/opportunity-scoring.mjs +2 -107
  152. package/lib/outbound-identity.mjs +1 -1
  153. package/lib/page-fingerprints.mjs +18 -1
  154. package/lib/perceptual-hash-comparison.mjs +2 -34
  155. package/lib/perceptual-hash.mjs +36 -10
  156. package/lib/portable-generator.mjs +2 -29
  157. package/lib/project-metadata.mjs +2 -8
  158. package/lib/rdap-client.mjs +11 -7
  159. package/lib/registrable-domain.mjs +2 -56
  160. package/lib/registrar-standing-contract.mjs +1 -1
  161. package/lib/registrar-standing.mjs +9 -13
  162. package/lib/registry-capabilities.mjs +2 -2
  163. package/lib/registry-capability-catalogue.mjs +27 -19
  164. package/lib/risk-calibration-summary.mjs +2 -352
  165. package/lib/risk-scoring.mjs +2 -345
  166. package/lib/scoring-evidence-quality.mjs +2 -107
  167. package/lib/semantic-version.mjs +2 -54
  168. package/lib/service-binding-dns.mjs +46 -2
  169. package/lib/smtp-transport-review.mjs +48 -19
  170. package/lib/sslbl-certificates.generated.mjs +142 -139
  171. package/lib/static-html-analysis.mjs +32 -4
  172. package/lib/structured-data-identity.mjs +1 -1
  173. package/lib/technology-evidence-role.mjs +1 -51
  174. package/lib/threat-intelligence-manifests.mjs +1 -1
  175. package/lib/threat-intelligence-runtime.mjs +1 -1
  176. package/lib/threat-intelligence-targets.mjs +1 -1
  177. package/lib/threat-intelligence-types.mjs +2 -75
  178. package/lib/tls-intelligence.mjs +30 -5
  179. package/lib/typosquat-generator.mjs +11 -2
  180. package/lib/website-enrichment.mjs +404 -0
  181. package/lib/whois-authority.mjs +43 -5
  182. package/lib/whois-chain.mjs +12 -3
  183. package/lib/whois-common-formats.mjs +3 -1
  184. package/lib/whois-contacts.mjs +13 -2
  185. package/lib/whois-normalization.mjs +3 -1
  186. package/lib/whois-parser.mjs +5 -3
  187. package/package.json +15 -14
  188. package/packages/analysis/analyst-taxonomy.mjs +40 -0
  189. package/packages/analysis/bounded-json.mjs +330 -0
  190. package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
  191. package/packages/analysis/cli-argument-grammar.mjs +216 -0
  192. package/packages/analysis/ct-query.mjs +27 -0
  193. package/packages/analysis/ct-response-bounds.mjs +9 -0
  194. package/packages/analysis/external-intelligence-risk.mjs +124 -0
  195. package/packages/analysis/http-evidence-bounds.mjs +8 -0
  196. package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
  197. package/packages/analysis/opportunity-scoring.mjs +107 -0
  198. package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
  199. package/packages/analysis/portable-generator.mjs +29 -0
  200. package/packages/analysis/project-metadata.mjs +8 -0
  201. package/packages/analysis/registrable-domain.mjs +56 -0
  202. package/packages/analysis/risk-calibration-summary.mjs +352 -0
  203. package/packages/analysis/risk-scoring.mjs +348 -0
  204. package/packages/analysis/scoring-evidence-quality.mjs +107 -0
  205. package/packages/analysis/semantic-version.mjs +54 -0
  206. package/packages/analysis/threat-intelligence-types.mjs +75 -0
  207. package/packages/cases/case-containment-handoff.mjs +195 -0
  208. package/packages/cases/case-evidence-links.mjs +151 -0
  209. package/packages/cases/case-evidence-model.mjs +89 -12
  210. package/packages/cases/case-evidence-quality.mjs +5 -0
  211. package/packages/cases/case-export-input.mjs +7 -5
  212. package/packages/cases/case-incident-context.mjs +52 -0
  213. package/packages/cases/case-markdown.mjs +11 -0
  214. package/packages/cases/case-migration-model.mjs +40 -24
  215. package/packages/cases/case-object-coverage.mjs +33 -0
  216. package/packages/cases/case-packet-correction.mjs +106 -0
  217. package/packages/cases/case-recheck-model.mjs +124 -36
  218. package/packages/cases/case-record-contracts.mjs +1 -1
  219. package/packages/cases/case-record-core.mjs +1 -1
  220. package/packages/cases/case-record-operations.mjs +111 -118
  221. package/packages/cases/case-record-projection.mjs +23 -4
  222. package/packages/cases/case-record-version-input.mjs +41 -0
  223. package/packages/cases/case-report.mjs +93 -27
  224. package/packages/cases/case-requested-evidence.mjs +146 -0
  225. package/packages/cases/case-response-actions.mjs +159 -1
  226. package/packages/cases/case-response-model.mjs +16 -1
  227. package/packages/cases/case-response-object.mjs +119 -0
  228. package/packages/cases/case-response-outcomes.mjs +108 -20
  229. package/packages/cases/case-response-packet-render.mjs +35 -12
  230. package/packages/cases/case-response-packet.mjs +55 -13
  231. package/packages/cases/case-response-records.mjs +0 -1
  232. package/packages/cases/case-response-review-inputs.mjs +60 -8
  233. package/packages/cases/case-storage-model.mjs +1 -1
  234. package/packages/cases/case-workflow-metadata.mjs +217 -0
  235. package/packages/cases/case-workflow-migration.mjs +43 -0
  236. package/packages/cases/http-summary.mjs +6 -1
  237. package/packages/comparison/favicon-similarity.mjs +1 -1
  238. package/packages/comparison/page-similarity.mjs +1 -1
  239. package/packages/comparison/relationship-evidence.mjs +21 -9
  240. package/packages/comparison/relationship-provenance.mjs +7 -1
  241. package/packages/contracts/analyst-interchange.mjs +35 -15
  242. package/packages/contracts/candidate-watch-review.mjs +54 -0
  243. package/packages/contracts/case-portability.mjs +360 -49
  244. package/packages/contracts/cli-command-semantics.mjs +5 -0
  245. package/packages/contracts/cli-grammar.mjs +6 -0
  246. package/packages/contracts/cli-lookup-brief.mjs +2 -0
  247. package/packages/contracts/context-review.mjs +25 -0
  248. package/packages/contracts/document-intake.mjs +11 -0
  249. package/packages/contracts/domain-control-flight-recorder.mjs +30 -14
  250. package/packages/contracts/domain-control-manifest.mjs +1 -1
  251. package/packages/contracts/domain-control-monitor.mjs +13 -2
  252. package/packages/contracts/domain-control-review.mjs +36 -15
  253. package/packages/contracts/external-observation-interchange.mjs +96 -28
  254. package/packages/contracts/har-review.mjs +2 -0
  255. package/packages/contracts/identity-events.mjs +4 -0
  256. package/packages/contracts/investigation-run.mjs +2 -0
  257. package/packages/contracts/ip-address.mjs +10 -0
  258. package/packages/contracts/lookup-evidence.mjs +3 -2
  259. package/packages/contracts/mail-authentication.mjs +5 -0
  260. package/packages/contracts/message-intake.mjs +37 -0
  261. package/packages/contracts/offline-comparison.mjs +4 -1
  262. package/packages/contracts/passive-mail.mjs +10 -0
  263. package/packages/contracts/risk-calibration.mjs +44 -29
  264. package/packages/contracts/schema-lifecycle.mjs +3 -1
  265. package/packages/contracts/web-capture.mjs +2 -2
  266. package/packages/contracts/workspace-portability.mjs +144 -28
  267. package/packages/evidence/collection-quality.mjs +67 -0
  268. package/packages/evidence/credential-form-attribution.mjs +60 -0
  269. package/packages/evidence/dns-query-outcome.mjs +72 -0
  270. package/packages/evidence/email-recipient.mjs +18 -0
  271. package/packages/evidence/image-regions.mjs +15 -0
  272. package/packages/evidence/lookup-target.mjs +5 -1
  273. package/packages/evidence/observation.mjs +2 -0
  274. package/packages/evidence/png-pixels.mjs +117 -0
  275. package/packages/evidence/public-address-policy.mjs +83 -0
  276. package/packages/evidence/record-identity.mjs +60 -16
  277. package/packages/evidence/technology-evidence-role.mjs +51 -0
  278. package/packages/interchange/defensive-indicator-export.mjs +264 -0
  279. package/packages/interchange/external-findings-import.mjs +36 -29
  280. package/packages/interchange/json-output.mjs +14 -0
  281. package/packages/interchange/managed-indicator-export.mjs +36 -0
  282. package/packages/interchange/managed-indicator-set.mjs +223 -0
  283. package/packages/interchange/misp-indicator-export.mjs +93 -0
  284. package/packages/interchange/sharing-policy.mjs +56 -0
  285. package/packages/interchange/stix-indicator-export.mjs +129 -0
  286. package/packages/interchange/web-capture-import.mjs +22 -15
  287. package/packages/investigation/acquisition-decision-packet.mjs +79 -1
  288. package/packages/investigation/bulk-mail-exposure.mjs +7 -13
  289. package/packages/investigation/capture-coverage.mjs +57 -0
  290. package/packages/investigation/connector-provenance-review.mjs +100 -0
  291. package/packages/investigation/context-review.mjs +35 -0
  292. package/packages/investigation/document-intake.mjs +87 -0
  293. package/packages/investigation/docx-intake.mjs +133 -0
  294. package/packages/investigation/domain-history-review.mjs +132 -0
  295. package/packages/investigation/domain-transition-review.mjs +49 -0
  296. package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
  297. package/packages/investigation/har-intake.mjs +68 -0
  298. package/packages/investigation/identity-event-intake.mjs +131 -0
  299. package/packages/investigation/identity-incident-review.mjs +39 -0
  300. package/packages/investigation/incident-sequence-review.mjs +58 -0
  301. package/packages/investigation/infrastructure-observation.mjs +234 -0
  302. package/packages/investigation/intake-context.mjs +284 -0
  303. package/packages/investigation/intake-indicators.mjs +116 -0
  304. package/packages/investigation/intake-phones.mjs +155 -0
  305. package/packages/investigation/intake-report.mjs +20 -0
  306. package/packages/investigation/investigation-manifest.mjs +35 -9
  307. package/packages/investigation/link-intake.mjs +209 -0
  308. package/packages/investigation/lookup-asset-graph.mjs +1 -1
  309. package/packages/investigation/mail-authentication-review.mjs +222 -0
  310. package/packages/investigation/mail-header-identity.mjs +209 -0
  311. package/packages/investigation/message-intake.mjs +235 -0
  312. package/packages/investigation/page-behaviour.mjs +113 -0
  313. package/packages/investigation/pdf-intake.mjs +155 -0
  314. package/packages/investigation/platform-continuity-review.mjs +60 -0
  315. package/packages/investigation/qr-intake.mjs +56 -0
  316. package/packages/investigation/requested-action-hints.mjs +11 -0
  317. package/packages/investigation/selected-input-review.mjs +17 -0
  318. package/packages/investigation/storefront-review.mjs +70 -0
  319. package/packages/monitoring/domain-feed.mjs +321 -0
  320. package/packages/workspace/brand-candidate-workflow.mjs +351 -0
  321. package/packages/workspace/brand-keyword-campaign.mjs +231 -0
  322. package/packages/workspace/brand-profile-model.mjs +48 -4
  323. package/packages/workspace/bulk-review-model.mjs +28 -26
  324. package/packages/workspace/bulk-session-model.mjs +27 -13
  325. package/packages/workspace/campaign-model.mjs +1 -1
  326. package/packages/workspace/candidate-watch-handoff.mjs +195 -0
  327. package/packages/workspace/detection-rule-model.mjs +11 -3
  328. package/packages/workspace/hostile-input.mjs +1 -1
  329. package/packages/workspace/investigation-guide.mjs +2 -2
  330. package/packages/workspace/investigation-template-model.mjs +26 -2
  331. package/packages/workspace/page-baseline.mjs +2 -2
  332. package/packages/workspace/relationship-observation-model.mjs +17 -13
  333. package/packages/workspace/shortlist-model.mjs +24 -7
  334. package/packages/workspace/watchlist-history.mjs +59 -16
  335. package/packages/workspace/watchlist-store.mjs +152 -11
  336. package/packages/workspace/website-snapshot-model.mjs +28 -3
  337. package/packages/workspace/workspace-archive-crypto.mjs +1 -1
  338. package/packages/workspace/workspace-archive.mjs +5 -4
  339. package/third-party-notices.txt +549 -16
  340. package/lib/hostname.mjs +0 -2
package/DISCLOSURE CHANGED
@@ -1,4 +1,4 @@
1
- WHOISleuth is a defensive domain investigation and brand-protection tool. Its
1
+ WHOISleuth is a defensive domain investigation and brand protection tool. Its
2
2
  command-line package can collect bounded WHOIS, RDAP, DNS, HTTP, TLS,
3
3
  certificate-transparency, website, and related infrastructure evidence. It can
4
4
  also generate and review lookalike-domain candidates, compare saved evidence,
package/NOTICE CHANGED
@@ -44,8 +44,8 @@ Shalini Chandra (2024), licensed under CC BY 4.0 without warranties. The source
44
44
  and licence links, modifications and reproduction instructions are in that
45
45
  directory's README.md. This dataset is not included in the website or CLI.
46
46
 
47
- The generated Unicode confusable projection is derived from Unicode UTS #39
48
- data file confusables.txt, Unicode version 17.0.0.
47
+ The generated Unicode confusable projection is derived from the Unicode UTS #39
48
+ data file confusables.txt. Its source version and digest accompany the projection.
49
49
 
50
50
  UNICODE LICENSE V3 COPYRIGHT AND PERMISSION NOTICE
51
51
  Copyright © 1991-2026 Unicode, Inc.
@@ -0,0 +1,55 @@
1
+ import {} from "./command-argument-grammar.mjs";
2
+ import { CliUsageError } from "./errors.mjs";
3
+ import { parseCliFailPolicies, } from "./fail-policy.mjs";
4
+ import { PRESENTATION_OPTIONS } from "./command-definition.mjs";
5
+ function terminalOptions(parsed) {
6
+ return { quiet: parsed.hasOption('--quiet'), color: !parsed.hasOption('--no-color') };
7
+ }
8
+ function jsonOutput(parsed) {
9
+ return parsed.hasOption('--json') ? 'json' : 'terminal';
10
+ }
11
+ function normalizedLabel(parsed, option, maximum) {
12
+ const value = parsed.optionValue(option);
13
+ if (value === null)
14
+ return null;
15
+ const normalized = value.replace(/\s+/gu, ' ').trim();
16
+ if (!normalized || normalized.length > maximum) {
17
+ throw new CliUsageError(`${option} requires a label of at most ${maximum} characters.`);
18
+ }
19
+ return normalized;
20
+ }
21
+ function failPolicies(parsed, command) {
22
+ const value = parsed.optionValue('--fail-on');
23
+ return value === null ? null : parseCliFailPolicies(value, command);
24
+ }
25
+ function parseOutput(parsed, definition) {
26
+ // Membership proves the returned literal belongs to this command's declared
27
+ // options. Grammar validation independently rejects conflicting selections.
28
+ return (PRESENTATION_OPTIONS.find(([option]) => definition.options.includes(option) && parsed.hasOption(option))?.[2] ?? 'terminal');
29
+ }
30
+ function parseTwoFileComparisonArguments(parsed, command) {
31
+ const sources = parsed.positionalValues('sources');
32
+ if (sources[0] === sources[1])
33
+ throw new CliUsageError(`${command} requires two different input files.`);
34
+ return {
35
+ leftSource: sources[0],
36
+ rightSource: sources[1],
37
+ output: jsonOutput(parsed),
38
+ ...terminalOptions(parsed),
39
+ };
40
+ }
41
+ function uniqueSources(parsed, command) {
42
+ const sources = parsed.positionalValues('sources');
43
+ if (new Set(sources).size !== sources.length)
44
+ throw new CliUsageError(`${command} input files must be different.`);
45
+ return sources;
46
+ }
47
+ function singleInputAction(action, parsed, positionalName = 'source') {
48
+ return {
49
+ action,
50
+ [positionalName]: parsed.positionalValue(positionalName),
51
+ output: jsonOutput(parsed),
52
+ ...terminalOptions(parsed),
53
+ };
54
+ }
55
+ export { terminalOptions, jsonOutput, normalizedLabel, failPolicies, parseOutput, parseTwoFileComparisonArguments, uniqueSources, singleInputAction, };
package/cli/arguments.mjs CHANGED
@@ -1,482 +1,36 @@
1
1
  import { parseCommandArguments } from "./command-argument-grammar.mjs";
2
2
  import { CliUsageError, hasUnsafeCliText } from "./errors.mjs";
3
- import { parseArchiveContentDigest } from "./archive-content-digest.mjs";
4
- import { parseCliFailPolicies } from "./fail-policy.mjs";
5
3
  import { isDirectLookupTarget } from "../lib/classify.mjs";
6
- import { MAX_INVESTIGATION_MANIFEST_ARTIFACTS } from "../packages/investigation/investigation-manifest.mjs";
7
- import { CLI_COMMANDS, CLI_CASE_OPERATIONS, cliMetaActionForInvocation, isCliCommand, } from "./command-reference.mjs";
8
- const MAX_CLI_ARGUMENTS = MAX_INVESTIGATION_MANIFEST_ARTIFACTS + 32;
9
- const MAX_CLI_ARGUMENT_LENGTH = 1024;
4
+ import { MAX_CLI_ARGUMENTS, MAX_CLI_ARGUMENT_LENGTH } from "../packages/contracts/cli-grammar.mjs";
5
+ import { CLI_COMMANDS, cliMetaActionForInvocation, isCliCommand } from "./command-reference.mjs";
6
+ import { commandHasOwner } from "./command-reference.mjs";
7
+ import { SUPPORT_ARGUMENT_PARSERS } from "./support-arguments.mjs";
8
+ import { ASSURANCE_ARGUMENT_PARSERS } from "./assurance-arguments.mjs";
9
+ import { COLLECTION_ARGUMENT_PARSERS } from "./collection-arguments.mjs";
10
+ import { NETWORK_ARGUMENT_PARSERS } from "./network-arguments.mjs";
11
+ import { REVIEW_ARGUMENT_PARSERS } from "./review-arguments.mjs";
12
+ import { EVIDENCE_ARGUMENT_PARSERS } from "./evidence-arguments.mjs";
13
+ import { WORKFLOW_ARGUMENT_PARSERS } from "./workflow-arguments.mjs";
14
+ import { HISTORY_ARGUMENT_PARSERS } from "./history-arguments.mjs";
15
+ const CLI_PARSERS = Object.freeze({
16
+ ...SUPPORT_ARGUMENT_PARSERS,
17
+ ...ASSURANCE_ARGUMENT_PARSERS,
18
+ ...COLLECTION_ARGUMENT_PARSERS,
19
+ ...NETWORK_ARGUMENT_PARSERS,
20
+ ...REVIEW_ARGUMENT_PARSERS,
21
+ ...EVIDENCE_ARGUMENT_PARSERS,
22
+ ...WORKFLOW_ARGUMENT_PARSERS,
23
+ ...HISTORY_ARGUMENT_PARSERS,
24
+ });
25
+ export function isCliCommandForOwner(args, owner) {
26
+ return commandHasOwner(args.action, owner);
27
+ }
10
28
  function boundedArgument(value) {
11
29
  if (typeof value !== 'string' || value.length > MAX_CLI_ARGUMENT_LENGTH || hasUnsafeCliText(value)) {
12
30
  throw new CliUsageError('Arguments must be bounded text without control characters.');
13
31
  }
14
32
  return value;
15
33
  }
16
- function terminalOptions(parsed) {
17
- return { quiet: parsed.hasOption('--quiet'), color: !parsed.hasOption('--no-color') };
18
- }
19
- function parseInspectArchiveArguments(parsed) {
20
- const expectedContentDigest = parsed.optionValue('--expect-content-digest');
21
- try {
22
- parseArchiveContentDigest(expectedContentDigest);
23
- }
24
- catch {
25
- throw new CliUsageError('--expect-content-digest requires sha256:<64 lowercase hex> or sorted-json-v2:sha256:<64 lowercase hex>.');
26
- }
27
- return {
28
- action: 'inspect-archive',
29
- source: parsed.positionalValue('source'),
30
- passphraseSource: parsed.optionValue('--passphrase-file'),
31
- search: parsed.optionValue('--search'),
32
- reveal: parsed.hasOption('--reveal'),
33
- requireMatch: parsed.hasOption('--require-match'),
34
- expectedContentDigest,
35
- output: parsed.hasOption('--json') ? 'json' : 'terminal',
36
- ...terminalOptions(parsed),
37
- };
38
- }
39
- function parseSignArtifactArguments(parsed) {
40
- return {
41
- action: 'sign-artifact',
42
- source: parsed.positionalValue('source'),
43
- privateKeySource: parsed.optionValue('--private-key-file'),
44
- };
45
- }
46
- function parseVerifySignatureArguments(parsed) {
47
- return {
48
- action: 'verify-signature',
49
- source: parsed.positionalValue('source'),
50
- publicKeySource: parsed.optionValue('--public-key-file'),
51
- trustStoreSource: parsed.optionValue('--trust-store-file'),
52
- output: parsed.hasOption('--json') ? 'json' : 'terminal',
53
- ...terminalOptions(parsed),
54
- };
55
- }
56
- function jsonOutput(parsed) {
57
- return parsed.hasOption('--json') ? 'json' : 'terminal';
58
- }
59
- function normalizedLabel(parsed, option, maximum) {
60
- const value = parsed.optionValue(option);
61
- if (value === null)
62
- return null;
63
- const normalized = value.replace(/\s+/gu, ' ').trim();
64
- if (!normalized || normalized.length > maximum) {
65
- throw new CliUsageError(`${option} requires a label of at most ${maximum} characters.`);
66
- }
67
- return normalized;
68
- }
69
- function failPolicies(parsed, command) {
70
- const value = parsed.optionValue('--fail-on');
71
- return value === null ? null : parseCliFailPolicies(value, command);
72
- }
73
- function parseOutput(parsed, formats) {
74
- return formats.find(([option]) => parsed.hasOption(option))?.[1] ?? 'terminal';
75
- }
76
- function singleInputAction(action, parsed, positionalName = 'source') {
77
- return {
78
- action,
79
- [positionalName === 'source' ? 'source' : positionalName]: parsed.positionalValue(positionalName),
80
- output: jsonOutput(parsed),
81
- ...terminalOptions(parsed),
82
- };
83
- }
84
- function parseLookupArguments(parsed) {
85
- const output = parseOutput(parsed, [
86
- ['--json', 'json'], ['--junit', 'junit'], ['--markdown', 'markdown'], ['--html', 'html'],
87
- ]);
88
- const selectedFailPolicies = failPolicies(parsed, 'lookup');
89
- return {
90
- action: 'lookup',
91
- query: parsed.positionalValue('target'),
92
- output,
93
- deep: parsed.hasOption('--deep'),
94
- detail: parsed.hasOption('--summary') ? 'summary' : parsed.hasOption('--verbose') ? 'verbose' : 'standard',
95
- strictExit: parsed.hasOption('--strict-exit'),
96
- events: parsed.hasOption('--events'),
97
- plan: parsed.hasOption('--plan'),
98
- includeAttribution: !parsed.hasOption('--no-attribution'),
99
- observerLabel: normalizedLabel(parsed, '--observer', 80),
100
- vantageLabel: normalizedLabel(parsed, '--vantage', 80),
101
- ...terminalOptions(parsed),
102
- ...(parsed.hasOption('--browse') ? { browse: true } : {}),
103
- ...(parsed.hasOption('--exact-url') ? { exactUrl: true } : {}),
104
- ...(parsed.optionValue('--save-lookup') ? { saveLookup: parsed.optionValue('--save-lookup') } : {}),
105
- ...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
106
- };
107
- }
108
- function parseManifestArguments(parsed) {
109
- const sources = parsed.positionalValues('artefacts');
110
- if (sources.some((source) => !source))
111
- throw new CliUsageError(`manifest requires from 1 to ${MAX_INVESTIGATION_MANIFEST_ARTIFACTS} artefact files.`);
112
- if (new Set(sources).size !== sources.length)
113
- throw new CliUsageError('manifest artefact files must be different.');
114
- const configurationDigestSha256 = parsed.optionValue('--configuration-digest');
115
- if (configurationDigestSha256 !== null && !/^sha256:[a-f0-9]{64}$/u.test(configurationDigestSha256)) {
116
- throw new CliUsageError('--configuration-digest requires a sha256: hexadecimal digest.');
117
- }
118
- return {
119
- action: 'manifest',
120
- sources,
121
- workflow: parsed.optionValue('--workflow'),
122
- configurationDigestSha256,
123
- ...(parsed.optionValue('--passphrase-file') ? { passphraseSource: parsed.optionValue('--passphrase-file') } : {}),
124
- ...(parsed.hasOption('--package') ? { package: true } : {}),
125
- ...(parsed.hasOption('--bagit') ? { bagit: true } : {}),
126
- ...(parsed.optionValue('--folder') ? { folder: parsed.optionValue('--folder') } : {}),
127
- output: jsonOutput(parsed),
128
- ...terminalOptions(parsed),
129
- };
130
- }
131
- function parseBulkArguments(parsed) {
132
- const deep = parsed.hasOption('--deep');
133
- const selectedFailPolicies = failPolicies(parsed, 'bulk');
134
- const output = parseOutput(parsed, [
135
- ['--json', 'json'], ['--jsonl', 'jsonl'], ['--csv', 'csv'], ['--domains', 'domains'],
136
- ['--csv-with-metadata', 'csv_metadata'],
137
- ['--queries', 'queries'], ['--junit', 'junit'],
138
- ]);
139
- const filter = parsed.hasOption('--registered-only') ? 'registered'
140
- : parsed.hasOption('--inconclusive-only') ? 'inconclusive'
141
- : parsed.hasOption('--errors-only') ? 'errors' : 'all';
142
- return {
143
- action: 'bulk',
144
- source: parsed.positionalValue('source'),
145
- output,
146
- deep,
147
- concurrency: parsed.integerOption('--concurrency') ?? (deep ? 2 : 4),
148
- checkpoint: parsed.optionValue('--checkpoint'),
149
- resume: parsed.hasOption('--resume'),
150
- events: parsed.hasOption('--events'),
151
- plan: parsed.hasOption('--plan'),
152
- filter,
153
- ...terminalOptions(parsed),
154
- ...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
155
- };
156
- }
157
- function discoveryValues(parsed) {
158
- const familyText = parsed.optionValue('--families');
159
- return {
160
- preset: (familyText ? 'custom' : parsed.optionValue('--preset') ?? 'all'),
161
- keyboardLayout: (parsed.optionValue('--keyboard') ?? 'qwerty'),
162
- tldText: parsed.optionValue('--tlds'),
163
- dictionarySource: parsed.optionValue('--dictionary'),
164
- familyText,
165
- };
166
- }
167
- function parseDiscoverArguments(parsed) {
168
- return {
169
- action: 'discover',
170
- seed: parsed.positionalValue('subject'),
171
- output: parseOutput(parsed, [['--json', 'json'], ['--jsonl', 'jsonl'], ['--domains', 'domains']]),
172
- ...discoveryValues(parsed),
173
- snapshotSource: parsed.optionValue('--snapshot'),
174
- ...terminalOptions(parsed),
175
- };
176
- }
177
- function parseDiscoverScanArguments(parsed) {
178
- const deep = parsed.hasOption('--deep');
179
- const selectedFailPolicies = failPolicies(parsed, 'discover-scan');
180
- const filter = parsed.hasOption('--registered-only') ? 'registered'
181
- : parsed.hasOption('--inconclusive-only') ? 'inconclusive'
182
- : parsed.hasOption('--acquisition-only') ? 'acquisition'
183
- : parsed.hasOption('--suppressed-only') ? 'suppressed' : 'all';
184
- return {
185
- action: 'discover-scan',
186
- seed: parsed.positionalValue('subject'),
187
- output: parseOutput(parsed, [['--json', 'json'], ['--jsonl', 'jsonl'], ['--csv', 'csv'], ['--csv-with-metadata', 'csv_metadata'], ['--domains', 'domains']]),
188
- ...discoveryValues(parsed),
189
- deep,
190
- scanLimit: parsed.integerOption('--scan-limit') ?? Math.min(100, deep ? 50 : 500),
191
- chunkSize: parsed.integerOption('--chunk-size') ?? 25,
192
- concurrency: parsed.integerOption('--concurrency') ?? (deep ? 2 : 4),
193
- checkpoint: parsed.optionValue('--checkpoint'),
194
- resume: parsed.hasOption('--resume'),
195
- resolverText: parsed.optionValue('--resolver'),
196
- observationSnapshot: parsed.optionValue('--observation-snapshot'),
197
- allowlistSource: parsed.optionValue('--allowlist'),
198
- filter,
199
- events: parsed.hasOption('--events'),
200
- plan: parsed.hasOption('--plan'),
201
- ...terminalOptions(parsed),
202
- ...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
203
- };
204
- }
205
- function parsePostureArguments(parsed) {
206
- const mailProfile = parsed.optionValue('--mail-profile') ?? 'standard';
207
- return {
208
- action: 'posture',
209
- domain: parsed.positionalValue('domain'),
210
- output: parseOutput(parsed, [['--json', 'json'], ['--sarif', 'sarif']]),
211
- selectorText: parsed.optionValue('--selectors'),
212
- retiredSelectorText: parsed.optionValue('--retired-selectors'),
213
- mailProfile: mailProfile === 'defensive-no-mail' ? 'defensive_no_mail' : mailProfile,
214
- ownedDomain: parsed.hasOption('--owned-domain'),
215
- ...(parsed.hasOption('--include-inherited-dns') ? { includeInheritedDns: true } : {}),
216
- ...terminalOptions(parsed),
217
- };
218
- }
219
- function parseDnssecValidateArguments(parsed) {
220
- return {
221
- action: 'dnssec-validate',
222
- target: parsed.positionalValue('domain'),
223
- resolver: parsed.optionValue('--resolver'),
224
- trustAnchorSource: parsed.optionValue('--trust-anchor'),
225
- ownedOrAuthorized: true,
226
- output: jsonOutput(parsed),
227
- ...terminalOptions(parsed),
228
- };
229
- }
230
- function parseMailTransportArguments(parsed) {
231
- return {
232
- action: 'mail-transport',
233
- source: parsed.positionalValue('source'),
234
- resolver: parsed.optionValue('--resolver'),
235
- trustAnchorSource: parsed.optionValue('--trust-anchor'),
236
- ownedOrAuthorized: true,
237
- activeProbeAcknowledged: true,
238
- output: jsonOutput(parsed),
239
- ...terminalOptions(parsed),
240
- };
241
- }
242
- function parseTwoFileComparisonArguments(parsed, command) {
243
- const sources = parsed.positionalValues('sources');
244
- if (sources[0] === sources[1])
245
- throw new CliUsageError(`${command} requires two different input files.`);
246
- return { leftSource: sources[0], rightSource: sources[1], output: jsonOutput(parsed), ...terminalOptions(parsed) };
247
- }
248
- function parseVerifyArtifactArguments(parsed) {
249
- if (parsed.hasOption('--folder') && parsed.positionalValue('source'))
250
- throw new CliUsageError('--folder selects its own input; do not also supply a file or stdin marker.');
251
- return {
252
- action: 'verify-artifact',
253
- source: parsed.positionalValue('source'),
254
- passphraseSource: parsed.optionValue('--passphrase-file'),
255
- manifestSource: parsed.optionValue('--manifest'),
256
- manifestEntryId: parsed.optionValue('--manifest-entry'),
257
- ...(parsed.hasOption('--package') ? { package: true } : {}),
258
- ...(parsed.hasOption('--bagit') ? { bagit: true } : {}),
259
- ...(parsed.optionValue('--folder') ? { folder: parsed.optionValue('--folder') } : {}),
260
- output: jsonOutput(parsed),
261
- strictExit: parsed.hasOption('--strict-exit'),
262
- ...terminalOptions(parsed),
263
- };
264
- }
265
- function parseMonitorOnceArguments(parsed) {
266
- const selectedFailPolicies = failPolicies(parsed, 'monitor-once');
267
- return {
268
- action: 'monitor-once',
269
- source: parsed.positionalValue('source'),
270
- previousSource: parsed.optionValue('--previous'),
271
- output: parseOutput(parsed, [['--json', 'json'], ['--junit', 'junit']]),
272
- limit: parsed.integerOption('--limit') ?? 20,
273
- concurrency: parsed.integerOption('--concurrency') ?? 2,
274
- ...terminalOptions(parsed),
275
- ...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
276
- };
277
- }
278
- function parseSharingReviewArguments(parsed) {
279
- const purpose = parsed.optionValue('--purpose').replace(/\s+/gu, ' ').trim();
280
- if (!purpose || purpose.length > 200)
281
- throw new CliUsageError('--purpose is limited to 200 characters.');
282
- return {
283
- action: 'sharing-review',
284
- source: parsed.positionalValue('source'),
285
- output: jsonOutput(parsed),
286
- marking: parsed.optionValue('--marking'),
287
- recipientScope: parsed.optionValue('--recipient-scope'),
288
- purpose,
289
- humanReviewed: parsed.hasOption('--human-reviewed'),
290
- personalDataReviewed: parsed.hasOption('--personal-data-reviewed'),
291
- redactionsConfirmed: parsed.hasOption('--redactions-confirmed'),
292
- ...terminalOptions(parsed),
293
- };
294
- }
295
- function parseWorkflowPlanArguments(parsed) {
296
- const options = { output: jsonOutput(parsed), ...terminalOptions(parsed) };
297
- if (parsed.hasOption('--list')) {
298
- if (parsed.allPositionals.length > 0)
299
- throw new CliUsageError('workflow-plan discovery options do not accept a subject.');
300
- return { action: 'workflow-plan', discovery: 'list', ...options };
301
- }
302
- if (parsed.hasOption('--explain')) {
303
- if (parsed.allPositionals.length > 0)
304
- throw new CliUsageError('workflow-plan discovery options do not accept a subject.');
305
- return { action: 'workflow-plan', discovery: 'explain', recipe: parsed.optionValue('--explain'), ...options };
306
- }
307
- if (parsed.allPositionals.length !== 2) {
308
- throw new CliUsageError('workflow-plan requires one fixed recipe and one subject, --list, or --explain <recipe>.');
309
- }
310
- return {
311
- action: 'workflow-plan',
312
- recipe: parsed.positionalValue('recipe'),
313
- subject: parsed.positionalValue('subject'),
314
- ...options,
315
- };
316
- }
317
- function parseWorkflowRunArguments(parsed) {
318
- const artifactBindings = parsed.optionValues('--use-artifact').map((value) => {
319
- const match = /^([a-z0-9]+(?:-[a-z0-9]+)*):([1-9][0-9]?)=([a-z0-9]+(?:-[a-z0-9]+)*)$/u.exec(value);
320
- if (!match)
321
- throw new CliUsageError('--use-artifact requires <step-id>:<input-number>=<earlier-step-id>.');
322
- return Object.freeze({ stepId: match[1], input: Number(match[2]), sourceStepId: match[3] });
323
- });
324
- const selections = parsed.optionValues('--select').map((value) => {
325
- const separator = value.indexOf('=');
326
- const stepId = separator === -1 ? '' : value.slice(0, separator);
327
- const selection = separator === -1 ? '' : value.slice(separator + 1);
328
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(stepId) || !selection.trim()) {
329
- throw new CliUsageError('--select requires <step-id>=<path-or-value>.');
330
- }
331
- if (selection.startsWith('-')) {
332
- throw new CliUsageError('--select values cannot start with a hyphen; prefix a file path with ./ when needed.');
333
- }
334
- return Object.freeze({ stepId, value: selection });
335
- });
336
- return {
337
- action: 'workflow-run',
338
- recipe: parsed.positionalValue('recipe'),
339
- subject: parsed.positionalValue('subject'),
340
- resumeSource: parsed.optionValue('--resume'),
341
- selections: Object.freeze(selections),
342
- artifactBindings: Object.freeze(artifactBindings),
343
- confirmedReviews: Object.freeze(parsed.optionValues('--confirm-review')),
344
- approveNetwork: parsed.hasOption('--approve-network'),
345
- interactive: parsed.hasOption('--interactive'),
346
- output: jsonOutput(parsed),
347
- ...terminalOptions(parsed),
348
- };
349
- }
350
- function parseDiffArguments(parsed) {
351
- const compared = parseTwoFileComparisonArguments(parsed, 'diff');
352
- const leftSessionId = parsed.optionValue('--left-session');
353
- const rightSessionId = parsed.optionValue('--right-session');
354
- for (const [option, value] of [['--left-session', leftSessionId], ['--right-session', rightSessionId]]) {
355
- if (value !== null && !/^[A-Za-z0-9_-]{1,128}$/u.test(value)) {
356
- throw new CliUsageError(`${option} requires one bounded saved-session ID.`);
357
- }
358
- }
359
- return { action: 'diff', ...compared, leftSessionId, rightSessionId };
360
- }
361
- function uniqueSources(parsed, command) {
362
- const sources = parsed.positionalValues('sources');
363
- if (new Set(sources).size !== sources.length)
364
- throw new CliUsageError(`${command} input files must be different.`);
365
- return sources;
366
- }
367
- function parseCaseArguments(parsed) {
368
- const operation = parsed.positionalValue('operation');
369
- const source = parsed.positionalValue('source');
370
- const domain = parsed.optionValue('--domain');
371
- const caseId = parsed.optionValue('--case-id');
372
- const title = parsed.optionValue('--title');
373
- const newIncident = parsed.hasOption('--new-incident');
374
- const text = parsed.optionValue('--text');
375
- const noteSource = parsed.optionValue('--note-file');
376
- const inputSource = parsed.optionValue('--input');
377
- const expectedFileDigest = parsed.optionValue('--expect-file-digest');
378
- if (source === '-' || inputSource === '-' || noteSource === '-')
379
- throw new CliUsageError('Case operations require selected files, not stdin.');
380
- if (operation !== 'open' && !source)
381
- throw new CliUsageError(`${operation} requires a Case file.`);
382
- if (operation !== 'show' && !parsed.hasOption('--output'))
383
- throw new CliUsageError('Case mutations require --output; nothing was changed.');
384
- if (operation === 'open' && !domain)
385
- throw new CliUsageError('case open requires --domain.');
386
- if (operation !== 'open' && (title || newIncident))
387
- throw new CliUsageError('--title and --new-incident belong to case open.');
388
- if (newIncident && (!title || caseId))
389
- throw new CliUsageError('--new-incident requires a distinguishing --title and cannot select an existing --case-id.');
390
- if (operation === 'note' ? (text === null && !noteSource) : (text !== null || noteSource !== null)) {
391
- throw new CliUsageError('Only case note accepts and requires --text or --note-file.');
392
- }
393
- if (['pin', 'assess', 'recheck'].includes(operation) ? !inputSource : inputSource !== null) {
394
- throw new CliUsageError('Only case pin, assess and recheck accept and require --input.');
395
- }
396
- if (expectedFileDigest !== null && (!source || !/^sha256:[a-f0-9]{64}$/u.test(expectedFileDigest))) {
397
- throw new CliUsageError('--expect-file-digest requires a source file and sha256:<64 lowercase hexadecimal characters>.');
398
- }
399
- return { action: 'case', operation, source, caseId, domain, title, newIncident, text, noteSource, inputSource, expectedFileDigest, output: jsonOutput(parsed), ...terminalOptions(parsed) };
400
- }
401
- const CLI_PARSERS = Object.freeze({
402
- completion: (parsed) => ({ action: 'completion', shell: parsed.positionalValue('shell') }),
403
- doctor: (parsed) => ({ action: 'doctor', network: parsed.hasOption('--network'), output: jsonOutput(parsed), ...terminalOptions(parsed) }),
404
- commands: (parsed) => ({
405
- action: 'commands', output: jsonOutput(parsed), common: parsed.hasOption('--common'),
406
- group: parsed.optionValue('--group'),
407
- mode: parsed.optionValue('--mode'),
408
- ...terminalOptions(parsed),
409
- }),
410
- manual: () => ({ action: 'manual' }),
411
- manifest: parseManifestArguments,
412
- 'map-observations': (parsed) => singleInputAction('map-observations', parsed),
413
- 'oam-export': (parsed) => singleInputAction('oam-export', parsed),
414
- lookup: parseLookupArguments,
415
- bulk: parseBulkArguments,
416
- 'ct-search': (parsed) => singleInputAction('ct-search', parsed, 'keyword'),
417
- 'ct-intake': (parsed) => singleInputAction('ct-intake', parsed),
418
- discover: parseDiscoverArguments,
419
- 'discover-scan': parseDiscoverScanArguments,
420
- posture: parsePostureArguments,
421
- http: (parsed) => singleInputAction('http', parsed, 'domain'),
422
- tls: (parsed) => singleInputAction('tls', parsed, 'hostname'),
423
- 'dnssec-validate': parseDnssecValidateArguments,
424
- 'mail-transport': parseMailTransportArguments,
425
- 'registry-support': (parsed) => ({
426
- action: 'registry-support', target: parsed.positionalValue('domain-or-suffix'),
427
- output: jsonOutput(parsed), ...terminalOptions(parsed),
428
- }),
429
- 'registry-doctor': (parsed) => singleInputAction('registry-doctor', parsed),
430
- 'registry-cohort': (parsed) => singleInputAction('registry-cohort', parsed),
431
- 'registry-scaffold': (parsed) => ({
432
- action: 'registry-scaffold', profile: parsed.optionValue('--profile'), suffix: parsed.optionValue('--suffix'),
433
- scenario: parsed.optionValue('--scenario'),
434
- }),
435
- 'risk-calibrate': (parsed) => ({
436
- action: 'risk-calibrate', source: parsed.positionalValue('source'),
437
- output: parsed.hasOption('--json') ? 'json' : parsed.hasOption('--summary-json') ? 'summary_json' : 'terminal',
438
- ...terminalOptions(parsed),
439
- }),
440
- 'lookalike-calibrate': (parsed) => singleInputAction('lookalike-calibrate', parsed),
441
- 'verify-artifact': parseVerifyArtifactArguments,
442
- 'interchange-report': (parsed) => ({
443
- action: 'interchange-report', source: parsed.positionalValue('source'),
444
- passphraseSource: parsed.optionValue('--passphrase-file'), output: jsonOutput(parsed), ...terminalOptions(parsed),
445
- }),
446
- 'inspect-archive': parseInspectArchiveArguments,
447
- 'sign-artifact': parseSignArtifactArguments,
448
- 'verify-signature': parseVerifySignatureArguments,
449
- 'source-report': (parsed) => singleInputAction('source-report', parsed),
450
- compare: (parsed) => singleInputAction('compare', parsed),
451
- 'page-compare': (parsed) => ({ action: 'page-compare', ...parseTwoFileComparisonArguments(parsed, 'page-compare') }),
452
- 'mail-review': (parsed) => singleInputAction('mail-review', parsed),
453
- 'mail-headers': (parsed) => singleInputAction('mail-headers', parsed),
454
- 'review-evidence': (parsed) => ({
455
- action: 'review-evidence', source: parsed.positionalValue('source'), mmdbSource: parsed.optionValue('--mmdb'),
456
- output: jsonOutput(parsed), strictExit: parsed.hasOption('--strict-exit'), ...terminalOptions(parsed),
457
- }),
458
- brief: (parsed) => singleInputAction('brief', parsed),
459
- case: parseCaseArguments,
460
- 'case-pack': (parsed) => ({
461
- action: 'case-pack', source: parsed.positionalValue('source'), output: jsonOutput(parsed),
462
- audience: parsed.optionValue('--audience'), reviewed: true, ...terminalOptions(parsed),
463
- }),
464
- 'domain-control': (parsed) => singleInputAction('domain-control', parsed),
465
- 'monitor-once': parseMonitorOnceArguments,
466
- assurance: (parsed) => singleInputAction('assurance', parsed),
467
- 'change-packet': (parsed) => singleInputAction('change-packet', parsed),
468
- 'sharing-review': parseSharingReviewArguments,
469
- 'workflow-plan': parseWorkflowPlanArguments,
470
- 'workflow-run': parseWorkflowRunArguments,
471
- diff: parseDiffArguments,
472
- reconcile: (parsed) => ({ action: 'reconcile', sources: uniqueSources(parsed, 'reconcile'), output: jsonOutput(parsed), ...terminalOptions(parsed) }),
473
- timeline: (parsed) => ({ action: 'timeline', sources: uniqueSources(parsed, 'timeline'), output: jsonOutput(parsed), ...terminalOptions(parsed) }),
474
- export: (parsed) => ({
475
- action: 'export', source: parsed.positionalValue('source'),
476
- format: parsed.hasOption('--markdown') ? 'markdown' : parsed.hasOption('--html') ? 'html' : 'json',
477
- compact: parsed.hasOption('--compact'), includeAttribution: !parsed.hasOption('--no-attribution'),
478
- }),
479
- });
480
34
  function parseCliArguments(rawArgv) {
481
35
  if (!Array.isArray(rawArgv) || rawArgv.length > MAX_CLI_ARGUMENTS) {
482
36
  throw new CliUsageError(`At most ${MAX_CLI_ARGUMENTS} command arguments are supported.`);