@slicedearth/whoisleuth-cli 2.3.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (418) hide show
  1. package/NOTICE +8 -2
  2. package/README.md +19 -15
  3. package/bin/whoisleuth.mjs +8 -1
  4. package/cli/archive-content-digest.mjs +14 -0
  5. package/cli/archive-inspect.mjs +20 -53
  6. package/cli/argument-values.mjs +55 -0
  7. package/cli/arguments.mjs +24 -408
  8. package/cli/artifact-validation/case-response.mjs +65 -44
  9. package/cli/artifact-validation/investigation-capsule.mjs +52 -31
  10. package/cli/artifact-validation/lookup-evidence.mjs +15 -9
  11. package/cli/artifact-validation/signed-review.mjs +136 -68
  12. package/cli/artifact-validation/structure-primitives.mjs +1 -105
  13. package/cli/artifact-verify.mjs +92 -24
  14. package/cli/assurance-arguments.mjs +56 -0
  15. package/cli/assurance-command-definitions.mjs +179 -0
  16. package/cli/assurance-command-runner.mjs +40 -5
  17. package/cli/bagit-folder.mjs +57 -0
  18. package/cli/bagit-review.mjs +23 -0
  19. package/cli/bounded-json.mjs +1 -1
  20. package/cli/bulk-checkpoint.mjs +1 -1
  21. package/cli/bulk-command-runner.mjs +4 -2
  22. package/cli/bulk-output.mjs +29 -3
  23. package/cli/bulk.mjs +2 -3
  24. package/cli/case-command.mjs +212 -0
  25. package/cli/case-pack.mjs +50 -32
  26. package/cli/ci-report.mjs +6 -3
  27. package/cli/collection-arguments.mjs +123 -0
  28. package/cli/collection-command-definitions.mjs +330 -0
  29. package/cli/collection-preflight.mjs +8 -1
  30. package/cli/command-argument-grammar.mjs +8 -206
  31. package/cli/command-definition.mjs +329 -0
  32. package/cli/command-reference.mjs +94 -1357
  33. package/cli/command-seeds.mjs +18 -0
  34. package/cli/compare.mjs +1 -1
  35. package/cli/completion.mjs +298 -79
  36. package/cli/config-profile.mjs +8 -6
  37. package/cli/ct-event-intake.mjs +16 -16
  38. package/cli/discover.mjs +5 -4
  39. package/cli/discovery-observation-snapshot.mjs +46 -18
  40. package/cli/discovery-scan-command-runner.mjs +5 -3
  41. package/cli/discovery-scan.mjs +7 -4
  42. package/cli/discovery-snapshot.mjs +2 -2
  43. package/cli/discovery-workflow.mjs +5 -5
  44. package/cli/domain-control-monitor.mjs +50 -20
  45. package/cli/domain-control-observations.mjs +151 -103
  46. package/cli/errors.mjs +37 -6
  47. package/cli/evidence-arguments.mjs +46 -0
  48. package/cli/evidence-command-definitions.mjs +69 -0
  49. package/cli/evidence-command-runner.mjs +18 -8
  50. package/cli/evidence-signing.mjs +5 -4
  51. package/cli/external-observation-mapping.mjs +9 -10
  52. package/cli/formatters/terminal-command-formats.mjs +6 -0
  53. package/cli/formatters/terminal-lookup.mjs +24 -46
  54. package/cli/formatters/terminal-shared.mjs +5 -3
  55. package/cli/formatters/terminal-technology.mjs +114 -0
  56. package/cli/history-arguments.mjs +39 -0
  57. package/cli/history-command-definitions.mjs +88 -0
  58. package/cli/indicator-set-command.mjs +33 -0
  59. package/cli/inline-command-families.mjs +6 -46
  60. package/cli/inline-command-runner.mjs +4 -31
  61. package/cli/input.mjs +2 -2
  62. package/cli/intake-command.mjs +107 -0
  63. package/cli/interactive-launcher.mjs +2 -160
  64. package/cli/interchange-report.mjs +8 -10
  65. package/cli/investigation-artifacts.mjs +176 -0
  66. package/cli/investigation-checkpoint.mjs +10 -0
  67. package/cli/investigation-folder.mjs +114 -0
  68. package/cli/investigation-manifest.mjs +1 -112
  69. package/cli/investigation-package-review.mjs +103 -0
  70. package/cli/investigation-plan.mjs +38 -147
  71. package/cli/investigation-recipes.mjs +177 -0
  72. package/cli/investigation-run.mjs +256 -35
  73. package/cli/local-document-checkpoint.mjs +170 -0
  74. package/cli/local-mmdb-review.mjs +197 -17
  75. package/cli/lookalike-calibration.mjs +1 -1
  76. package/cli/lookup-brief.mjs +2 -2
  77. package/cli/lookup-browser.mjs +5 -12
  78. package/cli/lookup-command-runner.mjs +33 -39
  79. package/cli/lookup-diff.mjs +47 -12
  80. package/cli/lookup-plan.mjs +16 -14
  81. package/cli/lookup-reconcile.mjs +15 -12
  82. package/cli/lookup-timeline.mjs +7 -6
  83. package/cli/mail-header-review.mjs +24 -247
  84. package/cli/mail-review.mjs +75 -21
  85. package/cli/manual.mjs +1 -0
  86. package/cli/network-arguments.mjs +51 -0
  87. package/cli/network-command-definitions.mjs +130 -0
  88. package/cli/network-command-runner.mjs +45 -31
  89. package/cli/offline-evidence-review.mjs +39 -6
  90. package/cli/output-file.mjs +31 -5
  91. package/cli/posture.mjs +1 -1
  92. package/cli/registry-doctor.mjs +3 -1
  93. package/cli/registry-fixture-scaffold.mjs +1 -1
  94. package/cli/registry-support.mjs +1 -1
  95. package/cli/retained-artifact-diff.mjs +10 -9
  96. package/cli/review-arguments.mjs +131 -0
  97. package/cli/review-command-definitions.mjs +273 -0
  98. package/cli/review-command-runner.mjs +104 -149
  99. package/cli/risk-calibration.mjs +8 -8
  100. package/cli/runner.mjs +74 -37
  101. package/cli/saved-lookup.mjs +2 -2
  102. package/cli/selected-input-worker.mjs +63 -0
  103. package/cli/sharing-review.mjs +8 -80
  104. package/cli/signer-trust.mjs +104 -0
  105. package/cli/source-reliability.mjs +75 -84
  106. package/cli/support-arguments.mjs +49 -0
  107. package/cli/support-command-definitions.mjs +209 -0
  108. package/cli/support-command-runner.mjs +2 -2
  109. package/cli/terminal-input.mjs +168 -0
  110. package/cli/workflow-arguments.mjs +86 -0
  111. package/cli/workflow-command-definitions.mjs +78 -0
  112. package/cli/workflow-command-runner.mjs +75 -13
  113. package/docs/cli-reference.md +103 -30
  114. package/docs/cli.md +510 -25
  115. package/docs/contextual-reviews.md +155 -0
  116. package/lib/abort.mjs +4 -1
  117. package/lib/analyst-taxonomy.mjs +2 -40
  118. package/lib/application-version.mjs +1 -1
  119. package/lib/availability.mjs +27 -754
  120. package/lib/bounded-contract-normalizers.mjs +4 -0
  121. package/lib/bounded-file.mjs +3 -1
  122. package/lib/bounded-html-document.mjs +125 -0
  123. package/lib/bounded-json.mjs +2 -290
  124. package/lib/browser-library-profile.mjs +99 -130
  125. package/lib/browser-library-worker.mjs +112 -0
  126. package/lib/candidate-provenance-bounds.mjs +2 -0
  127. package/lib/classify.mjs +1 -1
  128. package/lib/client-behavior-profile.mjs +3 -3
  129. package/lib/ct-query.mjs +2 -27
  130. package/lib/ct-response-bounds.mjs +2 -9
  131. package/lib/ct-search.mjs +89 -46
  132. package/lib/dmarc-discovery.mjs +62 -0
  133. package/lib/dns-convergence-review.mjs +21 -8
  134. package/lib/dns-delegation-health.mjs +36 -22
  135. package/lib/dns-inheritance-review.mjs +207 -0
  136. package/lib/dns-intelligence.mjs +44 -29
  137. package/lib/dns-mx.mjs +1 -1
  138. package/lib/dns-query-coverage.mjs +40 -0
  139. package/lib/dnssec-evidence-validation.mjs +11 -3
  140. package/lib/domain-assurance.mjs +1 -1
  141. package/lib/domain-change-packet.mjs +18 -14
  142. package/lib/domain-change-review.mjs +40 -16
  143. package/lib/domain-control-flight-recorder.mjs +301 -124
  144. package/lib/domain-control-manifest.mjs +126 -58
  145. package/lib/domain-posture-analysis.mjs +41 -11
  146. package/lib/domain-posture-parsers.mjs +88 -6
  147. package/lib/domain-posture.mjs +120 -32
  148. package/lib/domain-registration.mjs +392 -0
  149. package/lib/evidence-export-privacy.mjs +568 -0
  150. package/lib/evidence-export.mjs +12 -593
  151. package/lib/evidence-report.mjs +7 -2
  152. package/lib/external-intelligence-risk.mjs +2 -124
  153. package/lib/favicon.mjs +117 -41
  154. package/lib/generated/cisa-kev-catalog.mjs +50 -4
  155. package/lib/generated/registrar-standing-catalogue.mjs +18 -10
  156. package/lib/generated/retire-browser-catalog.mjs +2742 -1034
  157. package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
  158. package/lib/html-signals.mjs +87 -85
  159. package/lib/http-evidence-bounds.mjs +2 -7
  160. package/lib/http-intelligence.mjs +52 -27
  161. package/lib/idn-confusable-policy.mjs +6 -4
  162. package/lib/idn-confusables.mjs +1 -1
  163. package/lib/interchange-fidelity-registry.mjs +10 -0
  164. package/lib/lookup-cache.mjs +34 -11
  165. package/lib/lookup-child-profile-contract.mjs +24 -15
  166. package/lib/lookup-contract-primitives.mjs +1 -1
  167. package/lib/lookup-diagnostics.mjs +2 -1
  168. package/lib/lookup-network-evidence-bounds.mjs +1 -10
  169. package/lib/lookup-progress.mjs +79 -13
  170. package/lib/lookup-response-contract.mjs +1077 -0
  171. package/lib/lookup-response.mjs +11 -24
  172. package/lib/lookup-source-progress.mjs +24 -35
  173. package/lib/lookup.mjs +49 -28
  174. package/lib/observed-network-context.mjs +43 -27
  175. package/lib/opportunity-scoring.mjs +2 -107
  176. package/lib/outbound-identity.mjs +1 -1
  177. package/lib/outbound-request-bounds.mjs +4 -1
  178. package/lib/page-fingerprints.mjs +87 -134
  179. package/lib/page-role-profile.mjs +12 -8
  180. package/lib/perceptual-hash-comparison.mjs +2 -34
  181. package/lib/perceptual-hash.mjs +79 -24
  182. package/lib/portable-generator.mjs +2 -29
  183. package/lib/project-metadata.mjs +2 -8
  184. package/lib/rdap-bootstrap.mjs +72 -22
  185. package/lib/rdap-client.mjs +26 -14
  186. package/lib/rdap-normalization.mjs +33 -18
  187. package/lib/rdap-search-workbench.mjs +1 -1
  188. package/lib/rdap-transport.mjs +47 -20
  189. package/lib/rdap-types.mjs +2 -1
  190. package/lib/rdap.mjs +18 -3
  191. package/lib/registrable-domain.mjs +2 -36
  192. package/lib/registrar-standing-contract.mjs +2 -1
  193. package/lib/registrar-standing.mjs +9 -13
  194. package/lib/registry-capabilities.mjs +7 -3
  195. package/lib/registry-capability-catalogue.mjs +29 -20
  196. package/lib/registry-dates.mjs +2 -190
  197. package/lib/registry-insights.mjs +56 -18
  198. package/lib/response-policy.mjs +86 -48
  199. package/lib/risk-calibration-summary.mjs +2 -338
  200. package/lib/risk-scoring.mjs +2 -345
  201. package/lib/rpki-evidence.mjs +8 -5
  202. package/lib/safe-fetch.mjs +54 -47
  203. package/lib/scoring-evidence-quality.mjs +2 -107
  204. package/lib/security-txt.mjs +10 -7
  205. package/lib/semantic-version.mjs +2 -54
  206. package/lib/service-binding-dns.mjs +35 -0
  207. package/lib/smtp-transport-review.mjs +75 -34
  208. package/lib/sslbl-certificates.generated.mjs +141 -138
  209. package/lib/static-html-analysis.mjs +117 -146
  210. package/lib/structured-data-identity.mjs +2 -1
  211. package/lib/technology-markup.mjs +100 -0
  212. package/lib/threat-intelligence-manifests.mjs +11 -139
  213. package/lib/threat-intelligence-runtime.mjs +24 -52
  214. package/lib/threat-intelligence-targets.mjs +6 -34
  215. package/lib/threat-intelligence-types.mjs +2 -36
  216. package/lib/typosquat-generator.mjs +29 -16
  217. package/lib/urlhaus-intelligence.mjs +8 -3
  218. package/lib/urlscan-intelligence.mjs +1 -1
  219. package/lib/website-enrichment.mjs +388 -0
  220. package/lib/website-security-posture.mjs +30 -6
  221. package/lib/website-technology.mjs +111 -71
  222. package/lib/whois-authority.mjs +55 -5
  223. package/lib/whois-chain.mjs +9 -2
  224. package/lib/whois-common-formats.mjs +5 -5
  225. package/lib/whois-contracts.mjs +2 -1
  226. package/lib/whois-dialects.mjs +2 -2
  227. package/lib/whois-normalization.mjs +3 -1
  228. package/lib/whois-parser-support.mjs +8 -1
  229. package/lib/whois-parser.mjs +5 -3
  230. package/lib/whois-transport.mjs +23 -7
  231. package/package.json +14 -14
  232. package/packages/analysis/analyst-taxonomy.mjs +40 -0
  233. package/packages/analysis/bounded-json.mjs +330 -0
  234. package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
  235. package/packages/analysis/cli-argument-grammar.mjs +216 -0
  236. package/packages/analysis/ct-query.mjs +27 -0
  237. package/packages/analysis/ct-response-bounds.mjs +9 -0
  238. package/packages/analysis/external-intelligence-risk.mjs +124 -0
  239. package/packages/analysis/http-evidence-bounds.mjs +8 -0
  240. package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
  241. package/packages/analysis/opportunity-scoring.mjs +107 -0
  242. package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
  243. package/packages/analysis/portable-generator.mjs +29 -0
  244. package/packages/analysis/project-metadata.mjs +8 -0
  245. package/packages/analysis/registrable-domain.mjs +56 -0
  246. package/packages/analysis/risk-calibration-summary.mjs +352 -0
  247. package/packages/analysis/risk-scoring.mjs +345 -0
  248. package/packages/analysis/scoring-evidence-quality.mjs +107 -0
  249. package/packages/analysis/semantic-version.mjs +54 -0
  250. package/packages/analysis/threat-intelligence-types.mjs +75 -0
  251. package/packages/cases/case-attachment-model.mjs +83 -0
  252. package/packages/cases/case-evidence-links.mjs +135 -0
  253. package/packages/cases/case-evidence-model.mjs +229 -69
  254. package/packages/cases/case-evidence-quality.mjs +5 -0
  255. package/packages/cases/case-export-input.mjs +55 -0
  256. package/packages/cases/case-incident-context.mjs +52 -0
  257. package/packages/cases/case-markdown.mjs +11 -0
  258. package/packages/cases/case-migration-model.mjs +98 -48
  259. package/packages/cases/case-model.mjs +1 -0
  260. package/packages/cases/case-recheck-model.mjs +149 -0
  261. package/packages/cases/case-record-contracts.mjs +1 -1
  262. package/packages/cases/case-record-core.mjs +4 -3
  263. package/packages/cases/case-record-operations.mjs +123 -133
  264. package/packages/cases/case-record-projection.mjs +30 -20
  265. package/packages/cases/case-record-version-input.mjs +19 -0
  266. package/packages/cases/case-report.mjs +114 -67
  267. package/packages/cases/case-requested-evidence.mjs +146 -0
  268. package/packages/cases/case-response-actions.mjs +756 -0
  269. package/packages/cases/case-response-model.mjs +49 -1129
  270. package/packages/cases/case-response-outcomes.mjs +338 -0
  271. package/packages/cases/case-response-packet-render.mjs +157 -0
  272. package/packages/cases/case-response-packet-types.mjs +1 -0
  273. package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
  274. package/packages/cases/case-response-packet.mjs +58 -593
  275. package/packages/cases/case-response-records.mjs +93 -0
  276. package/packages/cases/case-response-review-inputs.mjs +382 -0
  277. package/packages/cases/case-response-values.mjs +85 -0
  278. package/packages/cases/case-review-package.mjs +45 -0
  279. package/packages/cases/case-selection.mjs +46 -0
  280. package/packages/cases/case-storage-model.mjs +26 -1
  281. package/packages/cases/case-workflow-metadata.mjs +225 -0
  282. package/packages/cases/case-workflow-migration.mjs +43 -0
  283. package/packages/cases/http-summary.mjs +6 -1
  284. package/packages/cases/response-route-freshness.mjs +16 -0
  285. package/packages/comparison/capture-context.mjs +61 -0
  286. package/packages/comparison/comparison-ledger-bulk.mjs +4 -1
  287. package/packages/comparison/favicon-similarity.mjs +1 -1
  288. package/packages/comparison/page-similarity.mjs +3 -2
  289. package/packages/comparison/relationship-evidence.mjs +103 -14
  290. package/packages/comparison/relationship-provenance.mjs +99 -0
  291. package/packages/contracts/analyst-interchange.mjs +234 -0
  292. package/packages/contracts/analyst-review-state-contract.mjs +11 -0
  293. package/packages/contracts/analyst-review-state.mjs +5 -4
  294. package/packages/contracts/case-portability.mjs +370 -114
  295. package/packages/contracts/case-views-contract.mjs +12 -0
  296. package/packages/contracts/case-views.mjs +26 -0
  297. package/packages/contracts/cli-command-semantics.mjs +3 -0
  298. package/packages/contracts/cli-grammar.mjs +6 -0
  299. package/packages/contracts/cli-lookup-brief.mjs +2 -0
  300. package/packages/contracts/cli-lookup.mjs +0 -1
  301. package/packages/contracts/context-review.mjs +24 -0
  302. package/packages/contracts/document-intake.mjs +11 -0
  303. package/packages/contracts/domain-control-flight-recorder.mjs +98 -44
  304. package/packages/contracts/domain-control-manifest.mjs +128 -74
  305. package/packages/contracts/domain-control-monitor.mjs +59 -45
  306. package/packages/contracts/domain-control-review.mjs +200 -121
  307. package/packages/contracts/domain-name.mjs +21 -0
  308. package/packages/contracts/external-observation-interchange.mjs +2 -1
  309. package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
  310. package/packages/contracts/har-review.mjs +2 -0
  311. package/packages/contracts/http-url.mjs +2 -0
  312. package/packages/contracts/identity-events.mjs +4 -0
  313. package/packages/contracts/investigation-package-limits.mjs +24 -0
  314. package/packages/contracts/investigation-portability.mjs +56 -27
  315. package/packages/contracts/investigation-run.mjs +18 -0
  316. package/packages/contracts/ip-address.mjs +44 -0
  317. package/packages/contracts/lookup-evidence.mjs +35 -0
  318. package/packages/contracts/mail-authentication.mjs +5 -0
  319. package/packages/contracts/message-intake.mjs +31 -0
  320. package/packages/contracts/offline-comparison.mjs +4 -2
  321. package/packages/contracts/page-fingerprints.mjs +15 -0
  322. package/packages/contracts/passive-mail.mjs +10 -0
  323. package/packages/contracts/risk-calibration.mjs +0 -1
  324. package/packages/contracts/schema-compatibility.mjs +1 -1
  325. package/packages/contracts/schema-lifecycle.mjs +83 -153
  326. package/packages/contracts/selected-file-limits.mjs +6 -0
  327. package/packages/contracts/vulnerability-identifiers.mjs +5 -0
  328. package/packages/contracts/web-capture.mjs +10 -0
  329. package/packages/contracts/workspace-portability.mjs +219 -238
  330. package/packages/evidence/artifact-integrity.mjs +8 -2
  331. package/packages/evidence/artifact-structure.mjs +105 -0
  332. package/packages/evidence/collection-quality.mjs +67 -0
  333. package/packages/evidence/decision-fact.mjs +4 -1
  334. package/packages/evidence/dns-query-outcome.mjs +72 -0
  335. package/packages/evidence/domain-control-runtime.mjs +155 -27
  336. package/packages/evidence/domain-posture-context.mjs +79 -0
  337. package/packages/evidence/email-recipient.mjs +18 -0
  338. package/packages/evidence/image-regions.mjs +56 -0
  339. package/packages/evidence/latest-observations.mjs +28 -0
  340. package/packages/evidence/lookup-target.mjs +108 -0
  341. package/packages/evidence/observation.mjs +18 -1
  342. package/packages/evidence/passphrase-encryption.mjs +44 -0
  343. package/packages/evidence/png-pixels.mjs +117 -0
  344. package/packages/evidence/public-address-policy.mjs +83 -0
  345. package/packages/evidence/record-identity.mjs +72 -0
  346. package/packages/evidence/registry-dates.mjs +190 -0
  347. package/packages/evidence/retained-file.mjs +69 -0
  348. package/packages/evidence/review-age.mjs +14 -0
  349. package/packages/interchange/bagit.mjs +342 -0
  350. package/packages/interchange/bounded-zip-extraction.mjs +278 -0
  351. package/packages/interchange/crc32.mjs +17 -0
  352. package/packages/interchange/defensive-indicator-export.mjs +264 -0
  353. package/packages/interchange/external-findings-import.mjs +93 -61
  354. package/packages/interchange/managed-indicator-export.mjs +35 -0
  355. package/packages/interchange/managed-indicator-set.mjs +210 -0
  356. package/packages/interchange/misp-indicator-export.mjs +93 -0
  357. package/packages/interchange/sharing-policy.mjs +56 -0
  358. package/packages/interchange/stix-indicator-export.mjs +129 -0
  359. package/packages/interchange/web-capture-import.mjs +389 -0
  360. package/packages/investigation/acquisition-decision-packet.mjs +1 -1
  361. package/packages/investigation/bulk-mail-exposure.mjs +9 -14
  362. package/packages/investigation/capture-coverage.mjs +57 -0
  363. package/packages/investigation/connector-provenance-review.mjs +100 -0
  364. package/packages/investigation/context-review.mjs +35 -0
  365. package/packages/investigation/document-intake.mjs +81 -0
  366. package/packages/investigation/docx-intake.mjs +133 -0
  367. package/packages/investigation/domain-history-review.mjs +100 -0
  368. package/packages/investigation/domain-transition-review.mjs +47 -0
  369. package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
  370. package/packages/investigation/har-intake.mjs +68 -0
  371. package/packages/investigation/identity-event-intake.mjs +131 -0
  372. package/packages/investigation/identity-incident-review.mjs +39 -0
  373. package/packages/investigation/incident-sequence-review.mjs +58 -0
  374. package/packages/investigation/intake-report.mjs +19 -0
  375. package/packages/investigation/investigation-bagit.mjs +14 -0
  376. package/packages/investigation/investigation-capsule.mjs +33 -5
  377. package/packages/investigation/investigation-manifest.mjs +200 -0
  378. package/packages/investigation/investigation-package-crypto.mjs +98 -0
  379. package/packages/investigation/investigation-package.mjs +200 -0
  380. package/packages/investigation/link-intake.mjs +179 -0
  381. package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
  382. package/packages/investigation/lookup-asset-graph.mjs +161 -141
  383. package/packages/investigation/lookup-claim-readiness.mjs +18 -0
  384. package/packages/investigation/lookup-investigation-brief.mjs +3 -3
  385. package/packages/investigation/lookup-task-guidance.mjs +58 -0
  386. package/packages/investigation/mail-authentication-review.mjs +222 -0
  387. package/packages/investigation/mail-header-identity.mjs +209 -0
  388. package/packages/investigation/message-intake.mjs +195 -0
  389. package/packages/investigation/page-behaviour.mjs +113 -0
  390. package/packages/investigation/pdf-intake.mjs +155 -0
  391. package/packages/investigation/platform-continuity-review.mjs +60 -0
  392. package/packages/investigation/qr-intake.mjs +56 -0
  393. package/packages/investigation/requested-action-hints.mjs +11 -0
  394. package/packages/investigation/selected-input-review.mjs +17 -0
  395. package/packages/investigation/storefront-review.mjs +70 -0
  396. package/packages/monitoring/analyst-review-state.mjs +38 -90
  397. package/packages/workspace/brand-profile-model.mjs +160 -22
  398. package/packages/workspace/bulk-columns.mjs +17 -0
  399. package/packages/workspace/bulk-review-model.mjs +39 -31
  400. package/packages/workspace/bulk-session-model.mjs +112 -27
  401. package/packages/workspace/bulk-sort.mjs +1 -0
  402. package/packages/workspace/campaign-model.mjs +1 -1
  403. package/packages/workspace/case-views.mjs +102 -0
  404. package/packages/workspace/detection-rule-model.mjs +1 -1
  405. package/packages/workspace/hostile-input.mjs +29 -8
  406. package/packages/workspace/investigation-guide.mjs +2 -2
  407. package/packages/workspace/investigation-template-model.mjs +45 -6
  408. package/packages/workspace/page-baseline.mjs +20 -17
  409. package/packages/workspace/relationship-observation-model.mjs +54 -35
  410. package/packages/workspace/shortlist-model.mjs +14 -5
  411. package/packages/workspace/watchlist-history.mjs +30 -14
  412. package/packages/workspace/watchlist-store.mjs +24 -6
  413. package/packages/workspace/website-snapshot-model.mjs +98 -33
  414. package/packages/workspace/workspace-archive-crypto.mjs +41 -61
  415. package/packages/workspace/workspace-archive.mjs +90 -25
  416. package/third-party-notices.txt +577 -17
  417. package/lib/hostname.mjs +0 -38
  418. package/packages/investigation/bulk-review-export.mjs +0 -63
package/NOTICE CHANGED
@@ -38,8 +38,14 @@ distributed as third-party-notices.txt. The website inventory is served at
38
38
  only the CLI runtime dependency closure. Both are generated from the reviewed
39
39
  lockfile, and the website inventory is checked for lockfile drift in CI.
40
40
 
41
- The generated Unicode confusable projection is derived from Unicode UTS #39
42
- data file confusables.txt, Unicode version 17.0.0.
41
+ The development-only fixtures/risk-evaluation/rows.json is a minimised,
42
+ stratified adaptation of PhiUSIIL Phishing URL (Website), Arvind Prasad and
43
+ Shalini Chandra (2024), licensed under CC BY 4.0 without warranties. The source
44
+ and licence links, modifications and reproduction instructions are in that
45
+ directory's README.md. This dataset is not included in the website or CLI.
46
+
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.
43
49
 
44
50
  UNICODE LICENSE V3 COPYRIGHT AND PERMISSION NOTICE
45
51
  Copyright © 1991-2026 Unicode, Inc.
package/README.md CHANGED
@@ -51,14 +51,7 @@ only `bulk` accepts multiple targets.
51
51
 
52
52
  ## Command groups
53
53
 
54
- | Group | Common commands |
55
- | --- | --- |
56
- | Investigate | `lookup`, `bulk`, `discover`, `ct-search`, `posture`, `http`, `tls`, `compare`, `mail-headers`, `brief` |
57
- | Respond | `case-pack`, `change-packet`, `sharing-review`, `export` |
58
- | Assure | `dnssec-validate`, `mail-transport`, `domain-control`, `assurance`, `workflow-plan`, `diff`, `inspect-archive`, `verify-artifact` |
59
- | Utilities | `doctor`, `commands`, `completion`, `manual`, `registry-scaffold` |
60
-
61
- Use the installed registry for the complete inventory:
54
+ Use the installed registry to browse Investigate, Respond, Assure and Utilities:
62
55
 
63
56
  ```bash
64
57
  whoisleuth commands --group investigate
@@ -67,10 +60,6 @@ whoisleuth commands --json
67
60
  whoisleuth manual | man -l -
68
61
  ```
69
62
 
70
- `registry-scaffold` has a separate fixture contract: its `--profile` selects a
71
- fixed fixture profile and it rejects shared `--config` profiles. It creates
72
- sanitised local fixture material.
73
-
74
63
  ## Collection and output
75
64
 
76
65
  Fast Lookup is registration-first. Deep Lookup explicitly adds the applicable
@@ -86,9 +75,24 @@ or domain-only output only where declared. `--output` writes a private local
86
75
  file atomically and refuses an existing path unless `--force` is selected.
87
76
  `--strict-exit` and `--fail-on` expose selected evidence states to automation.
88
77
 
89
- Exit codes are 0 for completion, 2 for invalid input, 3 for collection or
90
- comparison failure, 4 for a partial result or unmet selected evidence policy,
91
- 70 for internal bootstrap failure, 130 for cancellation and 143 for SIGTERM.
78
+ `case open --domain example.test --output cases.json` creates an ordinary local
79
+ Case file. `case show`, `note`, `pin`, `assess` and `recheck` review or update it
80
+ offline. Mutations require explicit output and preserve earlier retained evidence;
81
+ see `docs/cli.md#local-case-files` for JSON inputs and conflict handling. Working
82
+ files contain private analyst content, not attached file bytes.
83
+
84
+ `manifest --package --output evidence.wlep --passphrase-file ./passphrase.txt`
85
+ encrypts selected evidence files and their manifest. Use the same explicit
86
+ passphrase file with `verify-artifact --package` to authenticate and verify it
87
+ offline. Ordinary ZIPs and folder exports remain unencrypted; see `docs/cli.md`
88
+ for complete examples and file-handling boundaries.
89
+
90
+ `workflow-run` pauses on a partial collection. Resume retains it without
91
+ recollection and keeps exit code 4 even if later steps finish. Failed validation
92
+ or export steps remain retryable. See `docs/cli.md` for checkpoint compatibility.
93
+
94
+ The included `docs/cli-reference.md` lists common exit codes and compatibility
95
+ contracts. A completed command can still contain partial source evidence.
92
96
 
93
97
  ## Repository checkout
94
98
 
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import EXIT_CODES from "../cli/exit-codes.mjs";
4
- import { metaActionDefinition } from "../cli/command-reference.mjs";
4
+ import { HELP, commandHelp, isCliCommand, metaActionDefinition } from "../cli/command-reference.mjs";
5
+ import { presentTerminalOutput, terminalPresentation } from "../cli/terminal-presentation.mjs";
5
6
  for (const stream of [process.stdout, process.stderr]) {
6
7
  stream.on('error', (error) => {
7
8
  if (error.code === 'EPIPE')
@@ -10,6 +11,9 @@ for (const stream of [process.stdout, process.stderr]) {
10
11
  });
11
12
  }
12
13
  const argv = process.argv.slice(2);
14
+ const rootHelp = argv.length === 1 && metaActionDefinition('help').aliases.includes(argv[0] ?? '');
15
+ const command = argv.length === 2 && isCliCommand(argv[0])
16
+ && metaActionDefinition('help').aliases.includes(argv[1] ?? '') ? argv[0] : null;
13
17
  if (argv.length === 1 && metaActionDefinition('version').aliases.includes(argv[0] ?? '')) {
14
18
  const require = createRequire(import.meta.url);
15
19
  const metadata = require('../package.json');
@@ -21,6 +25,9 @@ if (argv.length === 1 && metaActionDefinition('version').aliases.includes(argv[0
21
25
  process.stdout.write(`${metadata.version}\n`);
22
26
  }
23
27
  }
28
+ else if (rootHelp || command) {
29
+ process.stdout.write(presentTerminalOutput(command ? commandHelp(command) : HELP, terminalPresentation(process.stdout)));
30
+ }
24
31
  else {
25
32
  const [configModule, errorsModule, outputModule, runnerModule] = await Promise.all([
26
33
  import("../cli/config-profile.mjs"),
@@ -0,0 +1,14 @@
1
+ /** Bare hashes retain their original v1 meaning; new identities declare v2. */
2
+ export function parseArchiveContentDigest(value) {
3
+ if (value === null || value === undefined || value === '')
4
+ return null;
5
+ if (typeof value !== 'string' || value.length > 'sorted-json-v2:sha256:'.length + 64) {
6
+ throw new TypeError('Expected archive content digest must be sha256:<64 lowercase hex> or sorted-json-v2:sha256:<64 lowercase hex>.');
7
+ }
8
+ const explicit = value.startsWith('sorted-json-v2:');
9
+ const digestSha256 = explicit ? value.slice('sorted-json-v2:'.length) : value;
10
+ if (!/^sha256:[a-f0-9]{64}$/u.test(digestSha256)) {
11
+ throw new TypeError('Expected archive content digest must be sha256:<64 lowercase hex> or sorted-json-v2:sha256:<64 lowercase hex>.');
12
+ }
13
+ return Object.freeze({ canonicalization: explicit ? 'sorted-json-v2' : 'sorted-json-v1', digestSha256 });
14
+ }
@@ -1,14 +1,13 @@
1
1
  import { Buffer } from 'node:buffer';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { domainToASCII } from 'node:url';
4
- import { scanBoundedJson } from "../lib/bounded-json.mjs";
5
- import { decryptWorkspaceArchive, isEncryptedWorkspaceArchive, } from "../packages/workspace/workspace-archive-crypto.mjs";
6
4
  import { WORKSPACE_ARCHIVE_SCHEMA, readWorkspaceArchive, } from "../packages/workspace/workspace-archive.mjs";
7
- import { canonicalArtifactJson } from "../packages/evidence/artifact-integrity.mjs";
8
- import { MAX_OFFLINE_ARTIFACT_BYTES, verifyOfflineArtifact, } from "./artifact-verify.mjs";
5
+ import { canonicalArtifactJson, canonicalArtifactJsonV2 } from "../packages/evidence/artifact-integrity.mjs";
6
+ import { parseArchiveContentDigest } from "./archive-content-digest.mjs";
7
+ import { verifyOfflineWorkspaceArchive } from "./artifact-verify.mjs";
9
8
  import { safeTerminalValue } from "./formatters/terminal.mjs";
10
9
  export const ARCHIVE_INSPECTION_SCHEMA = 'whoisleuth.workspace-archive-inspection';
11
- export const ARCHIVE_INSPECTION_VERSION = 2;
10
+ export const ARCHIVE_INSPECTION_VERSION = 3;
12
11
  export const MAX_ARCHIVE_SEARCH_LENGTH = 253;
13
12
  export const MAX_ARCHIVE_SEARCH_MATCHES = 100;
14
13
  export const MAX_ARCHIVE_SEARCH_NODES = 100_000;
@@ -22,32 +21,11 @@ const SEARCHABLE_FIELDS = new Set([
22
21
  'target',
23
22
  ]);
24
23
  const CONTROL_RE = /[\u0000-\u001f\u007f]/u;
25
- const SHA256_DIGEST_RE = /^sha256:[a-f0-9]{64}$/u;
26
24
  function record(value) {
27
25
  return value && typeof value === 'object' && !Array.isArray(value)
28
26
  ? value
29
27
  : null;
30
28
  }
31
- function parseJson(raw) {
32
- if (typeof raw !== 'string')
33
- throw new TypeError('Archive input must be UTF-8 JSON text.');
34
- const bytes = Buffer.byteLength(raw, 'utf8');
35
- if (bytes === 0 || bytes > MAX_OFFLINE_ARTIFACT_BYTES) {
36
- throw new TypeError(`Archive input must be between 1 byte and ${MAX_OFFLINE_ARTIFACT_BYTES} bytes.`);
37
- }
38
- let parsed;
39
- try {
40
- scanBoundedJson(raw);
41
- parsed = JSON.parse(raw);
42
- }
43
- catch {
44
- throw new TypeError('Archive input must be valid bounded JSON without duplicate keys.');
45
- }
46
- const value = record(parsed);
47
- if (!value)
48
- throw new TypeError('Archive input must contain one JSON object.');
49
- return value;
50
- }
51
29
  function normalizeSearch(value) {
52
30
  if (value === null || value === undefined || value === '')
53
31
  return null;
@@ -71,7 +49,7 @@ function canonicalSearchValue(value) {
71
49
  function valueDigest(value) {
72
50
  return createHash('sha256').update(value, 'utf8').digest('hex');
73
51
  }
74
- function archiveContentDigest(sections) {
52
+ function archiveContentDigests(sections) {
75
53
  const identity = sections.map((section) => ({
76
54
  id: section.id,
77
55
  schema: section.schema,
@@ -85,15 +63,10 @@ function archiveContentDigest(sections) {
85
63
  return content;
86
64
  })(),
87
65
  }));
88
- return `sha256:${createHash('sha256').update(canonicalArtifactJson(identity), 'utf8').digest('hex')}`;
89
- }
90
- function expectedContentDigest(value) {
91
- if (value === null || value === undefined || value === '')
92
- return null;
93
- if (typeof value !== 'string' || !SHA256_DIGEST_RE.test(value)) {
94
- throw new TypeError('Expected archive content digest must use the form sha256 followed by 64 lowercase hexadecimal characters.');
95
- }
96
- return value;
66
+ return Object.freeze({
67
+ current: `sha256:${createHash('sha256').update(canonicalArtifactJsonV2(identity), 'utf8').digest('hex')}`,
68
+ legacy: `sha256:${createHash('sha256').update(canonicalArtifactJson(identity), 'utf8').digest('hex')}`,
69
+ });
97
70
  }
98
71
  function searchSection(section, value, search, reveal, results, state) {
99
72
  const stack = [{ value, depth: 0 }];
@@ -146,21 +119,10 @@ function searchSection(section, value, search, reveal, results, state) {
146
119
  }
147
120
  }
148
121
  export async function inspectWorkspaceArchive(raw, options = {}) {
149
- const parsed = parseJson(raw);
150
- const encrypted = isEncryptedWorkspaceArchive(parsed);
151
- if (encrypted && !options.passphrase) {
152
- throw new TypeError('Encrypted archive inspection requires a separate passphrase file.');
153
- }
154
- await verifyOfflineArtifact(raw, options.passphrase === undefined
155
- ? {}
156
- : { passphrase: options.passphrase });
157
- const archiveValue = encrypted
158
- ? await decryptWorkspaceArchive(parsed, options.passphrase)
159
- : parsed;
160
- const archive = await readWorkspaceArchive(archiveValue);
161
- const contentDigestSha256 = archiveContentDigest(archive.sections);
162
- const expectedDigest = expectedContentDigest(options.expectedContentDigest);
163
- if (expectedDigest && expectedDigest !== contentDigestSha256) {
122
+ const expectedDigest = parseArchiveContentDigest(options.expectedContentDigest);
123
+ const { archive, encrypted } = await verifyOfflineWorkspaceArchive(raw, options);
124
+ const digests = archiveContentDigests(archive.sections);
125
+ if (expectedDigest && expectedDigest.digestSha256 !== (expectedDigest.canonicalization === 'sorted-json-v2' ? digests.current : digests.legacy)) {
164
126
  throw new TypeError('Workspace archive content digest did not match the expected value.');
165
127
  }
166
128
  const search = normalizeSearch(options.search);
@@ -196,7 +158,9 @@ export async function inspectWorkspaceArchive(raw, options = {}) {
196
158
  inputBytes: Buffer.byteLength(raw, 'utf8'),
197
159
  sectionCount: archive.sections.length,
198
160
  recordCount: archive.sections.reduce((sum, section) => sum + section.recordCount, 0),
199
- contentDigestSha256,
161
+ contentDigest: `sorted-json-v2:${digests.current}`,
162
+ contentDigestSha256: digests.legacy,
163
+ expectedContentDigestCanonicalization: expectedDigest?.canonicalization ?? null,
200
164
  }),
201
165
  sections: Object.freeze(archive.sections.map((section) => Object.freeze({
202
166
  id: section.id,
@@ -218,6 +182,7 @@ export async function inspectWorkspaceArchive(raw, options = {}) {
218
182
  limitations: Object.freeze([
219
183
  'Inspection validates the archive and reports bounded section metadata without printing retained evidence by default.',
220
184
  'The content digest covers ordered section identities and normalised content after excluding each section export timestamp. Equality detects matching retained archive content but does not authenticate its source or accuracy.',
185
+ 'Use the versioned contentDigest identity for new comparisons. The compatibility contentDigestSha256 field and bare expected hashes retain the original locale-sensitive digest; they are not a cross-locale identity guarantee.',
221
186
  'Search checks exact values in a small allowlist of target fields and never searches analyst notes, contacts, or raw evidence.',
222
187
  ...(reveal
223
188
  ? ['Search values were revealed only because the operator supplied --reveal; handle the output as sensitive evidence.']
@@ -234,7 +199,9 @@ export function formatArchiveInspection(report) {
234
199
  `Archive: ${report.archive.encrypted ? 'encrypted' : 'plain'} · v${report.archive.version}`,
235
200
  `Sections: ${report.summary.sectionCount}`,
236
201
  `Records: ${report.summary.recordCount}`,
237
- `Content digest: ${report.summary.contentDigestSha256}`,
202
+ `Content identity: ${report.summary.contentDigest}`,
203
+ ...(report.summary.expectedContentDigestCanonicalization === 'sorted-json-v1'
204
+ ? ['Expected digest matched using the legacy locale-sensitive form.'] : []),
238
205
  ];
239
206
  for (const section of report.sections) {
240
207
  lines.push(`${safeTerminalValue(section.label)}: ${section.recordCount} records · ${safeTerminalValue(section.status)} · ${section.bytes} bytes`);
@@ -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, };