@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
@@ -0,0 +1,330 @@
1
+ import { INVESTIGATION_PLAN_RECIPES } from "./investigation-recipes.mjs";
2
+ import { WHOISLEUTH_SOURCE_REPOSITORY_URL } from "../packages/analysis/project-metadata.mjs";
3
+ import { LOOKUP_EVIDENCE_SCHEMA_VERSION, SUPPORTED_LOOKUP_EVIDENCE_SCHEMA_VERSIONS, V1_PUBLIC_LOOKUP_EVIDENCE_SCHEMA_VERSION } from "../packages/contracts/lookup-evidence.mjs";
4
+ import { SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS, WORKSPACE_ARCHIVE_VERSION } from "../packages/contracts/case-portability.mjs";
5
+ import {} from "../packages/contracts/cli-command-semantics.mjs";
6
+ import { CLI_FAIL_POLICIES_BY_COMMAND } from "./fail-policy.mjs";
7
+ import { MAX_INVESTIGATION_MANIFEST_ARTIFACTS } from "../packages/investigation/investigation-manifest.mjs";
8
+ import { IDENTITY_ACTIONS } from "../packages/contracts/message-intake.mjs";
9
+ import { bulkQueryLimit, bulkConcurrencyLimit } from "../lib/bulk-limits.mjs";
10
+ const LEGACY_WORKSPACE_ARCHIVE_VERSIONS = SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS
11
+ .filter((version) => version !== WORKSPACE_ARCHIVE_VERSION);
12
+ const LEGACY_WORKSPACE_ARCHIVE_DESCRIPTION = LEGACY_WORKSPACE_ARCHIVE_VERSIONS
13
+ .map((version) => `version-${version}`)
14
+ .join(' and ');
15
+ const PUBLISHED_V2_LOOKUP_EVIDENCE_VERSIONS = SUPPORTED_LOOKUP_EVIDENCE_SCHEMA_VERSIONS
16
+ .filter((version) => version > V1_PUBLIC_LOOKUP_EVIDENCE_SCHEMA_VERSION && version !== LOOKUP_EVIDENCE_SCHEMA_VERSION)
17
+ .join(', ');
18
+ const LEGACY_WORKSPACE_ARCHIVE_SCOPE = LEGACY_WORKSPACE_ARCHIVE_VERSIONS
19
+ .map((version) => `v${version}`)
20
+ .join(' and ');
21
+ const INLINE_COMMAND_FAMILIES = ['support', 'review', 'assurance', 'workflow', 'history'];
22
+ const CLI_CASE_OPERATIONS = ['show', 'open', 'note', 'pin', 'link', 'withdraw-link', 'assess', 'recheck', 'incident-link', 'action', 'action-event', 'recheck-question', 'close-object'];
23
+ const CLI_INDICATOR_OPERATIONS = ['revise', 'inspect', 'stix', 'misp'];
24
+ const CLI_META_ACTIONS = Object.freeze([
25
+ Object.freeze({
26
+ id: 'help',
27
+ aliases: Object.freeze(['--help', '-h']),
28
+ scope: 'root_or_command',
29
+ precedence: 'before_command_grammar',
30
+ bypassesOrdinaryRequirements: true,
31
+ acceptsAdditionalArguments: false,
32
+ }),
33
+ Object.freeze({
34
+ id: 'version',
35
+ aliases: Object.freeze(['--version', '-V']),
36
+ scope: 'root_only',
37
+ precedence: 'before_command_grammar',
38
+ bypassesOrdinaryRequirements: true,
39
+ acceptsAdditionalArguments: false,
40
+ }),
41
+ ]);
42
+ const CLI_META_ACTION_BY_ID = Object.freeze(Object.fromEntries(CLI_META_ACTIONS.map((action) => [action.id, action])));
43
+ // Human-facing command reference kept separate from command execution so
44
+ // additions do not expand the already broad runtime controller.
45
+ const HELP_INTRO = `WHOISleuth CLI
46
+ Domain investigation from your terminal.
47
+
48
+ Quick start:
49
+ whoisleuth
50
+ whoisleuth example.test
51
+ whoisleuth lookup example.test --deep
52
+ cat domains.txt | whoisleuth bulk --jsonl
53
+ `;
54
+ const HELP_FOOTER = `
55
+ Run "whoisleuth <command> --help" for focused usage and an example.
56
+ With no arguments, an eligible interactive terminal opens a bounded launcher;
57
+ redirected or unsupported terminals continue to print this help. No request
58
+ starts until a Lookup plan is shown and the analyst confirms collection.
59
+ Use --json or --jsonl where supported for machine-readable stdout.
60
+ Use --output <file> for atomic private file output and --force to replace it.
61
+ Use -- before positional filenames that begin with a hyphen; ./ also makes a
62
+ filename unambiguous, for example: whoisleuth verify-artifact -- ./-evidence.json.
63
+ Use --palette auto, light, or dark after the command to select a fixed terminal
64
+ colour palette; --no-color, NO_COLOR, and redirected output still suppress ANSI.
65
+ Use --config <file> and --profile <name> for explicit versioned safe defaults.
66
+ Registry scaffold is the exception: its --profile selects a fixture capability
67
+ profile and --config is rejected, so bootstrap defaults cannot alter fixtures.
68
+ An ICANN-recognised public domain, reserved documentation domain, IP, or ASN
69
+ may replace "lookup"; URL-like or ambiguous input requires the explicit
70
+ command. Both forms use the same Lookup options.
71
+ Diagnostics are written to stderr. Fast lookup is the default; deep collection
72
+ must be requested explicitly and can disclose a target to additional sources.
73
+
74
+ Copyright 2026 slicedearth. Licensed under AGPL-3.0-only.
75
+ Source and licence: ${WHOISLEUTH_SOURCE_REPOSITORY_URL}
76
+ `;
77
+ const COMMON_OPTIONS = Object.freeze([
78
+ '--help', '--output', '--force', '--config', '--profile', '--palette',
79
+ ]);
80
+ const REGISTRY_SCAFFOLD_COMMON_OPTIONS = Object.freeze(COMMON_OPTIONS.filter((option) => option !== '--config' && option !== '--profile'));
81
+ function commonOptionsForSeed(seed) {
82
+ return seed.bootstrapProfile === 'command_owned' ? REGISTRY_SCAFFOLD_COMMON_OPTIONS : COMMON_OPTIONS;
83
+ }
84
+ function positional(name, valueKind, minimum, maximum, values = [], inputSource = 'argv', requiredWhenOptions = []) {
85
+ return Object.freeze({
86
+ name,
87
+ valueKind,
88
+ minimum,
89
+ maximum,
90
+ values: Object.freeze([...values]),
91
+ inputSource,
92
+ requiredWhenOptions: Object.freeze([...requiredWhenOptions]),
93
+ });
94
+ }
95
+ const NO_POSITIONALS = Object.freeze([]);
96
+ const OPTIONAL_FILE_POSITIONAL = Object.freeze([positional('source', 'file', 0, 1, [], 'argv_or_stdin')]);
97
+ const OPTIONAL_TEXT_POSITIONAL = Object.freeze([positional('subject', 'text', 0, 1, [], 'argv_or_stdin')]);
98
+ const NO_INTEGER_RANGES = () => Object.freeze([]);
99
+ const BASE_INTEGER_RANGE = (minimum, maximum) => (Object.freeze({ minimum, maximum, whenOptionPresent: null }));
100
+ const DEEP_INTEGER_RANGE = (minimum, maximum) => (Object.freeze({ minimum, maximum, whenOptionPresent: '--deep' }));
101
+ function optionDefinition(valueKind, description, options = {}) {
102
+ const configuredValues = options.values;
103
+ const configuredDefault = options.defaultValue;
104
+ return Object.freeze({
105
+ description: typeof description === 'function' ? description : () => description,
106
+ defaultValue: typeof configuredDefault === 'function' ? configuredDefault : () => configuredDefault ?? null,
107
+ valueKind: typeof valueKind === 'function' ? valueKind : () => valueKind,
108
+ values: typeof configuredValues === 'function'
109
+ ? configuredValues
110
+ : () => Object.freeze([...(configuredValues ?? [])]),
111
+ occurrence: options.occurrence ?? 'once',
112
+ acceptsOptionLikeValue: options.acceptsOptionLikeValue ?? false,
113
+ metaAction: options.metaAction ?? null,
114
+ integerRanges: options.integerRanges ?? NO_INTEGER_RANGES,
115
+ });
116
+ }
117
+ const flag = (description, occurrence = 'once') => optionDefinition('flag', description, { occurrence });
118
+ const file = (description) => optionDefinition('file', description);
119
+ const text = (description, acceptsOptionLikeValue = false) => optionDefinition('text', description, { acceptsOptionLikeValue });
120
+ const enumeration = (description, values, defaultValue) => optionDefinition('enum', description, { values, ...(defaultValue === undefined ? {} : { defaultValue }) });
121
+ const integer = (description, ranges, defaultValue) => (optionDefinition('integer', description, { integerRanges: ranges, defaultValue }));
122
+ const CLI_OPTION_DEFINITIONS = Object.freeze({
123
+ '--help': optionDefinition('flag', 'Show command usage, options and an example without executing it.', { metaAction: 'help' }),
124
+ '--output': file('Write output atomically to this local file.'),
125
+ '--force': flag('Allow replacement of the selected output file.'),
126
+ '--config': file('Load explicit versioned CLI configuration from this file.'),
127
+ '--profile': text('Select a named profile from the supplied configuration.', true),
128
+ '--palette': enumeration('Choose the terminal colour palette; redirected output and no-colour settings still take precedence.', ['auto', 'light', 'dark']),
129
+ '--network': flag('Include the optional public DNS and port 43 runtime checks.'),
130
+ '--json': flag('Write structured JSON to stdout or the selected output file.'),
131
+ '--reported-action': optionDefinition('enum', 'Record an analyst-reported identity action; repeat for separate actions.', { values: IDENTITY_ACTIONS.map(action => action.id), occurrence: 'repeatable' }),
132
+ '--trusted-auth-header': optionDefinition('text', 'Select a recognised receiver header by part:header-index. This records analyst trust, not independent authentication; repeat for separate headers.', { occurrence: 'repeatable' }),
133
+ '--intake-context': optionDefinition('file', 'Read one selected version-1 distribution declaration or version-2 digest-bound phone/destination selection file. Declarations remain attributed claims.'),
134
+ '--package': flag('Verify a portable evidence ZIP or encrypted package rather than a single report.'),
135
+ '--folder': file('Verify the evidence package within this selected folder.'),
136
+ '--quiet': flag('Suppress ordinary terminal presentation.', 'idempotent'),
137
+ '--no-color': flag('Suppress ANSI colour in terminal output.', 'idempotent'),
138
+ '--common': flag('Show only commands marked as common.'),
139
+ '--group': enumeration('Filter commands by task group.', ['investigate', 'respond', 'assure', 'utilities']),
140
+ '--mode': enumeration('Filter commands by offline or network collection mode.', ['offline', 'network']),
141
+ '--workflow': text('Label the workflow recorded in the evidence manifest.', true),
142
+ '--configuration-digest': text('Record a supplied configuration digest in the manifest provenance.', true),
143
+ '--junit': flag('Write JUnit XML for automated result reporting.'),
144
+ '--markdown': flag('Write a Markdown report.'),
145
+ '--html': flag('Write an HTML report.'),
146
+ '--no-attribution': flag('Omit the optional product attribution from presentation output.'),
147
+ '--fast': flag('Select registration-first Fast collection.'),
148
+ '--deep': flag('Select broader Deep collection and its additional source requests.'),
149
+ '--exact-url': flag('With Deep Lookup, send the selected URL path and query to the website; omit its fragment.'),
150
+ '--observer': text('Attach the supplied observer label to the retained observation.'),
151
+ '--vantage': text('Attach the supplied collection-vantage label.'),
152
+ '--plan': flag('Describe intended collection and limits without making requests.'),
153
+ '--summary': flag('Show a concise terminal result.'),
154
+ '--verbose': flag('Show the detailed terminal result.'),
155
+ '--browse': flag('Open the interactive terminal evidence browser.'),
156
+ '--interactive': flag('Prompt for missing supported inputs on an interactive terminal.'),
157
+ '--save-lookup': file('After a normal evidence-browser close, save the completed private Lookup JSON to a new file.'),
158
+ '--strict-exit': flag('Use the command’s strict outcome policy when deciding the exit status.'),
159
+ '--fail-on': optionDefinition('policy_list', 'Return a failure-policy exit status for the selected comma-separated outcomes.', {
160
+ values: (command) => CLI_FAIL_POLICIES_BY_COMMAND[command] ?? [],
161
+ }),
162
+ '--events': flag('Emit collection progress events on stderr.'),
163
+ '--jsonl': flag('Write one JSON record per line.'),
164
+ '--csv': flag('Write compact CSV rows.'),
165
+ '--csv-with-metadata': flag('Write CSV with source, observation-time and collection-state metadata.'),
166
+ '--domains': flag('Write the selected domain names only.'),
167
+ '--queries': flag('Write the selected original queries only.'),
168
+ '--registered-only': flag('Keep registered results in the presented output.'),
169
+ '--inconclusive-only': flag('Keep inconclusive results in the presented output.'),
170
+ '--errors-only': flag('Keep error results in the presented output.'),
171
+ // Higher ceilings do not automatically increase the default collection load.
172
+ '--concurrency': integer('Set the maximum number of concurrent collection tasks.', () => Object.freeze([
173
+ BASE_INTEGER_RANGE(1, bulkConcurrencyLimit('fast')),
174
+ DEEP_INTEGER_RANGE(1, bulkConcurrencyLimit('deep')),
175
+ ]), (_command, deep) => Math.min(deep ? 2 : 4, bulkConcurrencyLimit(deep ? 'deep' : 'fast'))),
176
+ '--checkpoint': file('Save resumable collection state to this local file.'),
177
+ '--resume': flag('Resume collection from the selected checkpoint.'),
178
+ '--tlds': text('Use this comma-separated set of domain endings.', true),
179
+ '--preset': enumeration('Choose candidate-generation families; explicit families select a custom set instead.', ['common', 'impersonation', 'all'], 'all'),
180
+ '--families': text('Select the candidate-generation families explicitly.'),
181
+ '--keyboard': enumeration('Choose keyboard layouts for adjacent-key candidates.', ['qwerty', 'azerty', 'qwertz', 'all'], 'qwerty'),
182
+ '--dictionary': file('Read candidate words from this local dictionary.'),
183
+ '--snapshot': file('Use this retained observation snapshot.'),
184
+ '--scan-limit': integer('Limit the number of generated candidates to collect.', () => Object.freeze([
185
+ BASE_INTEGER_RANGE(1, bulkQueryLimit('fast')),
186
+ DEEP_INTEGER_RANGE(1, bulkQueryLimit('deep')),
187
+ ]), (_command, deep) => Math.min(deep ? 50 : 100, bulkQueryLimit(deep ? 'deep' : 'fast'))),
188
+ '--chunk-size': integer('Set the number of candidates processed per checkpoint chunk.', () => Object.freeze([BASE_INTEGER_RANGE(1, 100)]), () => 25),
189
+ '--resolver': text('Choose the supported DNS resolver for collection.'),
190
+ '--allowlist': file('Read reviewed domains whose priority should be suppressed, without changing their evidence.'),
191
+ '--observation-snapshot': file('Compare with this retained observation snapshot.'),
192
+ '--acquisition-only': flag('Present only acquisition candidates.'),
193
+ '--suppressed-only': flag('Present only candidates suppressed by the allowlist.'),
194
+ '--selectors': text('Supply explicit DKIM selectors; no selector enumeration is performed.', true),
195
+ '--retired-selectors': text('Supply previously retired DKIM selectors for review.', true),
196
+ '--mail-profile': enumeration('Choose the expected mail posture for the review.', ['standard', 'defensive-no-mail', 'parked'], 'standard'),
197
+ '--sarif': flag('Write the posture review as SARIF.'),
198
+ '--owned-domain': flag('Declare that the reviewed domain is owned by the analyst.'),
199
+ '--include-inherited-dns': flag('Explicitly review inherited DMARC, reporting boundaries and parent-delegation evidence.'),
200
+ '--trust-anchor': file('Read the analyst-selected DNSSEC trust anchor.'),
201
+ '--owned-or-authorized': flag('Acknowledge ownership or permission for this active collection.'),
202
+ '--active-probe': flag('Explicitly enable the bounded active protocol exchange.'),
203
+ '--suffix': text('Select the registry suffix.', true),
204
+ '--scenario': enumeration('Choose the expected registry fixture outcome.', ['registered', 'not_found', 'inconclusive']),
205
+ '--summary-json': flag('Write the concise structured summary.'),
206
+ '--passphrase-file': file('Read the archive passphrase from a local file, not a command-line value.'),
207
+ '--manifest': file('Use the selected investigation manifest.'),
208
+ '--bagit': flag('Verify the selected package as BagIt 1.0.'),
209
+ '--manifest-entry': enumeration('Select an artefact entry from the supplied manifest.', Array.from({ length: MAX_INVESTIGATION_MANIFEST_ARTIFACTS }, (_, index) => `artifact-${index + 1}`)),
210
+ '--search': text('Search the selected local archive.'),
211
+ '--require-match': flag('Require the local archive search to find a match.'),
212
+ '--reveal': flag('Include retained values otherwise redacted by archive inspection.'),
213
+ '--expect-content-digest': text('Compare archive content with the supplied version-qualified or historical digest.', true),
214
+ '--private-key-file': file('Read the private signing key from this local file.'),
215
+ '--public-key-file': file('Read the public verification key from this local file.'),
216
+ '--trust-store-file': file('Read the analyst-selected signer trust store.'),
217
+ '--mmdb': file('Read one selected local MMDB (up to 512 MiB); version-2 queries require a justified freshness policy.'),
218
+ '--audience': enumeration('Choose the export audience and its field-disclosure policy.', ['internal', 'trusted', 'public']),
219
+ '--reviewed': flag('Confirm the required human review of the exported material.'),
220
+ '--previous': file('Compare against this earlier retained report.'),
221
+ '--limit': integer('Limit the number of watchlist targets checked in this run.', () => Object.freeze([BASE_INTEGER_RANGE(1, 20)]), () => 20),
222
+ '--marking': enumeration('Declare the information-sharing marking.', ['clear', 'green', 'amber', 'amber-strict', 'red']),
223
+ '--recipient-scope': enumeration('Declare the intended recipient scope.', ['public', 'community', 'organization', 'named-recipients']),
224
+ '--purpose': text('Record the purpose of the intended sharing.'),
225
+ '--human-reviewed': flag('Confirm that a person reviewed the material.', 'idempotent'),
226
+ '--personal-data-reviewed': flag('Confirm that personal-data disclosure was reviewed.', 'idempotent'),
227
+ '--redactions-confirmed': flag('Confirm that the intended redactions were checked.', 'idempotent'),
228
+ '--list': flag('List available workflow recipes.'),
229
+ '--explain': enumeration('Explain a selected workflow without executing it.', INVESTIGATION_PLAN_RECIPES),
230
+ '--select': optionDefinition('text', 'Bind a literal input to a workflow step; repeat for further inputs.', { occurrence: 'repeatable', acceptsOptionLikeValue: true }),
231
+ '--use-artifact': optionDefinition('text', 'Connect a step input to an earlier compatible output.', { occurrence: 'repeatable' }),
232
+ '--confirm-review': optionDefinition('text', 'Confirm human review for the named step in this invocation.', { occurrence: 'repeatable' }),
233
+ '--approve-network': flag('Approve the workflow’s declared network steps for this invocation.'),
234
+ '--preview': flag('Inspect validated retained outputs, unresolved inputs and remaining approvals without executing or writing a checkpoint.'),
235
+ '--left-session': text('Select the left-hand retained capture session.', true),
236
+ '--right-session': text('Select the right-hand retained capture session.', true),
237
+ '--compact': flag('Write a compact report presentation.'),
238
+ '--case-id': text('Select the retained Case identifier.'),
239
+ '--domain': text('Supply the Case domain.'),
240
+ '--title': text('Supply the Case title.'),
241
+ '--new-incident': flag('Create a separate incident instead of updating a matching Case.'),
242
+ '--text': text('Supply the note text directly.'),
243
+ '--note-file': file('Read note text from a selected local file.'),
244
+ '--input': file('Read the selected local input file.'),
245
+ '--expect-file-digest': text('Require the input file to match this SHA-256 digest.'),
246
+ });
247
+ function constraint(value) {
248
+ if (value.kind === 'mutually_exclusive') {
249
+ return Object.freeze({ ...value, options: Object.freeze([...value.options]) });
250
+ }
251
+ if (value.kind === 'required') {
252
+ return Object.freeze({ ...value, options: Object.freeze([...value.options]) });
253
+ }
254
+ if (value.kind === 'excludes_all' || value.kind === 'value_excludes') {
255
+ return Object.freeze({ ...value, excludedOptions: Object.freeze([...value.excludedOptions]) });
256
+ }
257
+ return Object.freeze({ ...value, requiredOptions: Object.freeze([...value.requiredOptions]) });
258
+ }
259
+ const EMPTY_CONSTRAINTS = Object.freeze([]);
260
+ const FILE_OUTPUT_CONSTRAINTS = Object.freeze([
261
+ constraint({ kind: 'requires_all', option: '--force', requiredOptions: ['--output'] }),
262
+ ]);
263
+ const QUIET_OUTPUT_CONSTRAINT = constraint({
264
+ kind: 'mutually_exclusive',
265
+ options: ['--quiet', '--output'],
266
+ });
267
+ const PRESENTATION_OPTIONS = Object.freeze([
268
+ ['--json', 'JSON', 'json'], ['--jsonl', 'JSON Lines', 'jsonl'], ['--junit', 'JUnit XML', 'junit'],
269
+ ['--csv', 'CSV', 'csv'],
270
+ ['--csv-with-metadata', 'CSV with evidence metadata', 'csv_metadata'],
271
+ ['--domains', 'domain list', 'domains'], ['--queries', 'query list', 'queries'],
272
+ ['--markdown', 'Markdown', 'markdown'], ['--html', 'HTML', 'html'], ['--sarif', 'SARIF', 'sarif'],
273
+ ['--summary-json', 'summary JSON', 'summary_json'],
274
+ ]);
275
+ const MACHINE_OUTPUT_OPTIONS = Object.freeze(PRESENTATION_OPTIONS.map(([option]) => option));
276
+ function optionSpec(command, option, scope, definition = CLI_OPTION_DEFINITIONS[option]) {
277
+ const valueKind = definition.valueKind(command);
278
+ const values = definition.values(command);
279
+ return Object.freeze({
280
+ option,
281
+ scope,
282
+ arity: valueKind === 'flag' ? 0 : 1,
283
+ valueKind,
284
+ values: Object.freeze([...values]),
285
+ integerRanges: Object.freeze([...definition.integerRanges(command)]),
286
+ occurrence: definition.occurrence,
287
+ acceptsOptionLikeValue: definition.acceptsOptionLikeValue,
288
+ metaAction: definition.metaAction,
289
+ });
290
+ }
291
+ function grammarConstraints(commandOptions, commandConstraints) {
292
+ const machineOutputOptions = commandOptions.filter((option) => MACHINE_OUTPUT_OPTIONS.includes(option));
293
+ return Object.freeze([
294
+ ...FILE_OUTPUT_CONSTRAINTS,
295
+ ...(commandOptions.includes('--quiet') ? [QUIET_OUTPUT_CONSTRAINT] : []),
296
+ ...(commandOptions.includes('--quiet') && machineOutputOptions.length > 0
297
+ ? [constraint({ kind: 'excludes_all', option: '--quiet', excludedOptions: machineOutputOptions })]
298
+ : []),
299
+ ...(commandOptions.includes('--events')
300
+ ? [constraint({ kind: 'mutually_exclusive', options: ['--events', '--output'] })]
301
+ : []),
302
+ ...(commandOptions.includes('--browse')
303
+ ? [constraint({ kind: 'mutually_exclusive', options: ['--browse', '--output'] })]
304
+ : []),
305
+ ...commandConstraints,
306
+ ]);
307
+ }
308
+ function commandSeed(seed) {
309
+ const admitted = new Set([...seed.options, ...commonOptionsForSeed(seed)]);
310
+ for (const option of Object.keys(seed.optionOverrides ?? {})) {
311
+ if (!admitted.has(option))
312
+ throw new TypeError(`Cannot override an undeclared command option: ${option}.`);
313
+ }
314
+ return Object.freeze({
315
+ ...seed,
316
+ reference: Object.freeze({ ...seed.reference }),
317
+ collection: Object.freeze({
318
+ mode: seed.networkEffect === 'offline' ? 'offline' : 'network',
319
+ scope: seed.collection.scope,
320
+ }),
321
+ options: Object.freeze([...seed.options]),
322
+ ...(seed.optionOverrides ? { optionOverrides: Object.freeze({ ...seed.optionOverrides }) } : {}),
323
+ positionals: Object.freeze([...seed.positionals]),
324
+ constraints: Object.freeze([...seed.constraints]),
325
+ schemaIdentifiers: Object.freeze([...seed.schemaIdentifiers]),
326
+ primaryArtefacts: Object.freeze([...seed.primaryArtefacts]),
327
+ additionalOutputFormats: Object.freeze([...seed.additionalOutputFormats]),
328
+ });
329
+ }
330
+ export { LEGACY_WORKSPACE_ARCHIVE_VERSIONS, LEGACY_WORKSPACE_ARCHIVE_DESCRIPTION, PUBLISHED_V2_LOOKUP_EVIDENCE_VERSIONS, LEGACY_WORKSPACE_ARCHIVE_SCOPE, INLINE_COMMAND_FAMILIES, CLI_CASE_OPERATIONS, CLI_INDICATOR_OPERATIONS, CLI_META_ACTIONS, CLI_META_ACTION_BY_ID, HELP_INTRO, HELP_FOOTER, COMMON_OPTIONS, REGISTRY_SCAFFOLD_COMMON_OPTIONS, commonOptionsForSeed, positional, NO_POSITIONALS, OPTIONAL_FILE_POSITIONAL, OPTIONAL_TEXT_POSITIONAL, NO_INTEGER_RANGES, BASE_INTEGER_RANGE, DEEP_INTEGER_RANGE, optionDefinition, flag, file, text, enumeration, integer, CLI_OPTION_DEFINITIONS, constraint, EMPTY_CONSTRAINTS, FILE_OUTPUT_CONSTRAINTS, QUIET_OUTPUT_CONSTRAINT, PRESENTATION_OPTIONS, MACHINE_OUTPUT_OPTIONS, optionSpec, grammarConstraints, commandSeed };