@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
@@ -1,7 +1,8 @@
1
1
  // Pure, bounded workspace archive composition. Existing typed store models remain the
2
2
  // authority for normalization and merge semantics; this module only packages
3
3
  // their portable contracts, verifies integrity, and previews conflicts.
4
- import { buildCaseExport, enforceStoreBudget, mergeCases, } from "../cases/case-model.mjs";
4
+ import { buildCaseExport, enforceStoreBudget } from "../cases/case-storage-model.mjs";
5
+ import { mergeCases } from "../cases/case-migration-model.mjs";
5
6
  import { assertBrandProfileStoreBudget, buildBrandProfileExport, mergeBrandProfiles, normalizeBrandProfileId, } from "./brand-profile-model.mjs";
6
7
  import { assertCampaignStoreBudget, buildCampaignExport, mergeCampaigns, } from "./campaign-model.mjs";
7
8
  import { normalizeExplicitIsoTimestamp } from "../evidence/observation.mjs";
@@ -18,6 +19,8 @@ import { buildAnalystReviewStateExport, emptyAnalystReviewStateStore, mergeAnaly
18
19
  import { isSupportedWorkspaceArchiveVersion, MAX_WORKSPACE_ARCHIVE_BYTES, MAX_WORKSPACE_ARCHIVE_SECTION_BYTES, MAX_WORKSPACE_ARCHIVE_SECTIONS, serialiseWorkspaceArchiveSection, SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS, WORKSPACE_ARCHIVE_CASE_SECTION, WORKSPACE_ARCHIVE_SCHEMA, WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_VERSION, WORKSPACE_ARCHIVE_VERSION, WORKSPACE_SETTINGS_COMPATIBILITY, WORKSPACE_SETTINGS_SCHEMA, WORKSPACE_SETTINGS_VERSION, } from "../contracts/case-portability.mjs";
19
20
  import { ANALYST_REVIEW_STATE_COMPATIBILITY } from "../contracts/analyst-review-state.mjs";
20
21
  import { WORKSPACE_PORTABILITY_ARCHIVE_SECTION_REFERENCES } from "../contracts/workspace-portability.mjs";
22
+ import { CASE_VIEWS_COMPATIBILITY } from "../contracts/case-views.mjs";
23
+ import { emptyCaseViewsStore, mergeCaseViews, normalizeCaseViewsStore } from "./case-views.mjs";
21
24
  export { isSupportedWorkspaceArchiveVersion, MAX_WORKSPACE_ARCHIVE_BYTES, MAX_WORKSPACE_ARCHIVE_SECTION_BYTES, MAX_WORKSPACE_ARCHIVE_SECTIONS, SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS, WORKSPACE_ARCHIVE_CASE_SECTION, WORKSPACE_ARCHIVE_SCHEMA, WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS, PUBLIC_WORKSPACE_ARCHIVE_VERSION, WORKSPACE_ARCHIVE_VERSION, WORKSPACE_SETTINGS_SCHEMA, WORKSPACE_SETTINGS_VERSION, };
22
25
  const CONTROL_RE = /[\x00-\x1f\x7f]/;
23
26
  const CHECKSUM_RE = /^sha256:[a-f0-9]{64}$/;
@@ -111,6 +114,7 @@ function workspaceArchiveSections(input, now) {
111
114
  investigationTemplates: buildInvestigationTemplateExport(input.investigationTemplates, now),
112
115
  bulkReview: buildBulkReviewExport(input.bulkReview),
113
116
  analystReviewState: buildAnalystReviewStateExport(input.analystReviewState),
117
+ caseViews: normalizeCaseViewsStore(input.caseViews),
114
118
  settings: settingsDocument(input),
115
119
  };
116
120
  }
@@ -150,7 +154,7 @@ const SECTION_DEFINITIONS = [
150
154
  merge: (local, data) => {
151
155
  const result = mergeCases(local.cases, data);
152
156
  const bounded = enforceStoreBudget(result.cases);
153
- return { ...result, cases: bounded.cases, pruned: bounded.pruned };
157
+ return { ...result, document: bounded.cases, pruned: bounded.pruned };
154
158
  },
155
159
  },
156
160
  {
@@ -158,7 +162,7 @@ const SECTION_DEFINITIONS = [
158
162
  count: (data) => arrayCount(data, 'campaigns'),
159
163
  merge: (local, data) => {
160
164
  const result = mergeCampaigns(local.campaigns, data);
161
- return { ...result, campaigns: assertCampaignStoreBudget(result.campaigns).campaigns };
165
+ return { ...result, document: assertCampaignStoreBudget(result.campaigns).campaigns };
162
166
  },
163
167
  },
164
168
  {
@@ -166,7 +170,8 @@ const SECTION_DEFINITIONS = [
166
170
  count: (data) => arrayCount(data, 'profiles'),
167
171
  merge: (local, data, now) => {
168
172
  const result = mergeBrandProfiles(local.brandProfiles, data, { nowIso: now });
169
- return { ...result, profiles: assertBrandProfileStoreBudget(result.profiles).profiles };
173
+ const profiles = assertBrandProfileStoreBudget(result.profiles).profiles;
174
+ return { ...result, profiles, document: profiles };
170
175
  },
171
176
  },
172
177
  {
@@ -174,7 +179,7 @@ const SECTION_DEFINITIONS = [
174
179
  count: (data) => objectCount(data, 'watchlists'),
175
180
  merge: (local, data) => {
176
181
  const result = mergeWatchlistStores(local.watchlists, data);
177
- return { ...result, watchlists: assertWatchlistStoreBudget(result.watchlists).watchlists };
182
+ return { ...result, document: assertWatchlistStoreBudget(result.watchlists).watchlists };
178
183
  },
179
184
  },
180
185
  {
@@ -182,7 +187,7 @@ const SECTION_DEFINITIONS = [
182
187
  count: (data) => arrayCount(data, 'entries'),
183
188
  merge: (local, data) => {
184
189
  const result = mergeShortlistStores(local.shortlist, data);
185
- return { ...result, entries: assertShortlistStoreBudget(result.entries).entries };
190
+ return { ...result, document: assertShortlistStoreBudget(result.entries).entries };
186
191
  },
187
192
  },
188
193
  {
@@ -190,14 +195,17 @@ const SECTION_DEFINITIONS = [
190
195
  count: (data) => arrayCount(data, 'rules'),
191
196
  merge: (local, data) => {
192
197
  const result = mergeDetectionRules(local.detectionRules, data);
193
- return { ...result, rules: assertDetectionRuleStoreBudget(result.rules).rules };
198
+ return { ...result, document: assertDetectionRuleStoreBudget(result.rules).rules };
194
199
  },
195
200
  },
196
201
  {
197
202
  ...sectionContract('relationshipObservations'),
198
203
  label: 'Retained relationship observations',
199
204
  count: (data) => arrayCount(data, 'observations'),
200
- merge: (local, data) => mergeRelationshipObservations(local.relationshipObservations, data),
205
+ merge: (local, data) => {
206
+ const result = mergeRelationshipObservations(local.relationshipObservations, data);
207
+ return { ...result, document: result.observations };
208
+ },
201
209
  },
202
210
  {
203
211
  ...sectionContract('bulkSessions'),
@@ -205,27 +213,36 @@ const SECTION_DEFINITIONS = [
205
213
  count: (data) => arrayCount(data, 'sessions'),
206
214
  merge: (local, data) => {
207
215
  const result = mergeBulkSessions(local.bulkSessions, data);
208
- enforceBulkSessionStoreBudget(result.sessions);
209
- return result;
216
+ const bounded = enforceBulkSessionStoreBudget(result.sessions);
217
+ return { ...result, document: bounded.store.sessions };
210
218
  },
211
219
  },
212
220
  {
213
221
  ...sectionContract('websiteSnapshots'),
214
222
  label: 'Website profile snapshots',
215
223
  count: (data) => arrayCount(data, 'snapshots'),
216
- merge: (local, data) => mergeWebsiteSnapshots(local.websiteSnapshots, data),
224
+ merge: (local, data) => {
225
+ const result = mergeWebsiteSnapshots(local.websiteSnapshots, data);
226
+ return { ...result, document: result.snapshots };
227
+ },
217
228
  },
218
229
  {
219
230
  ...sectionContract('investigationTemplates'),
220
231
  label: 'Investigation templates',
221
232
  count: (data) => arrayCount(data, 'templates'),
222
- merge: (local, data) => mergeInvestigationTemplates(local.investigationTemplates, data),
233
+ merge: (local, data) => {
234
+ const result = mergeInvestigationTemplates(local.investigationTemplates, data);
235
+ return { ...result, document: result.templates };
236
+ },
223
237
  },
224
238
  {
225
239
  ...sectionContract('bulkReview'),
226
240
  label: 'Bulk saved views and review queue',
227
241
  count: (data) => arrayCount(data, 'presets') + arrayCount(data, 'rows'),
228
- merge: (local, data) => mergeBulkReviewStores(local.bulkReview, data),
242
+ merge: (local, data) => {
243
+ const result = mergeBulkReviewStores(local.bulkReview, data);
244
+ return { ...result, document: result.store };
245
+ },
229
246
  },
230
247
  {
231
248
  id: 'analystReviewState',
@@ -234,7 +251,19 @@ const SECTION_DEFINITIONS = [
234
251
  version: ANALYST_REVIEW_STATE_COMPATIBILITY.currentVersion,
235
252
  supportedVersions: ANALYST_REVIEW_STATE_COMPATIBILITY.supportedVersions,
236
253
  count: (data) => arrayCount(data, 'records'),
237
- merge: (local, data) => mergeAnalystReviewStateStores(local.analystReviewState, data),
254
+ merge: (local, data) => {
255
+ const result = mergeAnalystReviewStateStores(local.analystReviewState, data);
256
+ return { ...result, document: result.store };
257
+ },
258
+ },
259
+ {
260
+ id: 'caseViews', label: 'Saved Case views', schema: compatibilitySchema(CASE_VIEWS_COMPATIBILITY),
261
+ version: CASE_VIEWS_COMPATIBILITY.currentVersion, supportedVersions: CASE_VIEWS_COMPATIBILITY.supportedVersions,
262
+ count: (data) => arrayCount(data, 'views'),
263
+ merge: (local, data) => {
264
+ const result = mergeCaseViews(local.caseViews, data);
265
+ return { ...result, document: result.store };
266
+ },
238
267
  },
239
268
  {
240
269
  id: 'settings', label: 'Workspace settings', schema: compatibilitySchema(WORKSPACE_SETTINGS_COMPATIBILITY),
@@ -245,6 +274,19 @@ const SECTION_DEFINITIONS = [
245
274
  },
246
275
  ];
247
276
  const DEFINITION_BY_ID = new Map(SECTION_DEFINITIONS.map((definition) => [definition.id, definition]));
277
+ /** Apply previously verified data sections through the same owners as preview. */
278
+ export function mergeReadyWorkspaceArchiveData(localInput, sections, generatedAt) {
279
+ if (sections.length > MAX_WORKSPACE_ARCHIVE_SECTIONS || new Set(sections.map(section => section.id)).size !== sections.length) {
280
+ throw new Error('Workspace data sections exceed their count or contain duplicate identities.');
281
+ }
282
+ const local = normalizedInput(localInput);
283
+ return sections.map(section => {
284
+ const definition = DEFINITION_BY_ID.get(section.id);
285
+ if (section.status !== 'ready' || !definition?.merge)
286
+ throw new Error('Only verified, supported workspace data sections can be merged.');
287
+ return { ...definition.merge(local, section.data, generatedAt), id: section.id };
288
+ });
289
+ }
248
290
  const SECTION_ORDER = new Map(WORKSPACE_ARCHIVE_SECTION_IDS.map((id, index) => [id, index]));
249
291
  function canonicalSectionOrder(left, right) {
250
292
  const leftIndex = SECTION_ORDER.get(left.id) ?? Number.MAX_SAFE_INTEGER;
@@ -266,6 +308,7 @@ function normalizedInput(input) {
266
308
  investigationTemplates: Array.isArray(value.investigationTemplates) ? value.investigationTemplates : [],
267
309
  bulkReview: record(value.bulkReview) || {},
268
310
  analystReviewState: record(value.analystReviewState) || emptyAnalystReviewStateStore(),
311
+ caseViews: value.caseViews ?? emptyCaseViewsStore(),
269
312
  settings: record(value.settings) || {},
270
313
  };
271
314
  }
@@ -273,7 +316,7 @@ function ensureArchiveBudget(value) {
273
316
  const serialized = serialize(value);
274
317
  const bytes = byteLength(serialized);
275
318
  if (bytes > MAX_WORKSPACE_ARCHIVE_BYTES) {
276
- throw new Error('Workspace archives are limited to 10 MiB. Export smaller collections separately before trying again.');
319
+ throw new Error(`Workspace archives are limited to ${MAX_WORKSPACE_ARCHIVE_BYTES / 1024 / 1024} MiB. Export smaller collections separately before trying again.`);
277
320
  }
278
321
  return { serialized, bytes };
279
322
  }
@@ -290,7 +333,7 @@ export async function buildWorkspaceArchive(input, options = {}) {
290
333
  const data = sections[definition.id];
291
334
  const sectionBytes = byteLength(serialize(data));
292
335
  if (sectionBytes > MAX_WORKSPACE_ARCHIVE_SECTION_BYTES) {
293
- throw new Error(`${definition.label} exceeds the 5 MiB workspace archive section limit.`);
336
+ throw new Error(`${definition.label} exceeds the ${MAX_WORKSPACE_ARCHIVE_SECTION_BYTES / 1024 / 1024} MiB workspace archive section limit.`);
294
337
  }
295
338
  const recordCount = definition.count(data);
296
339
  manifestSections.push({
@@ -338,8 +381,11 @@ function manifestEntry(raw) {
338
381
  }
339
382
  /** Validate structure, section byte counts, and checksums without applying data. */
340
383
  export async function readWorkspaceArchive(raw, options = {}) {
341
- assertWorkspaceInputGraph(raw, 'Workspace archive');
342
- const value = record(raw);
384
+ assertWorkspaceInputGraph(raw, 'Workspace archive', { maximumBytes: MAX_WORKSPACE_ARCHIVE_BYTES });
385
+ // Snapshot before the first asynchronous digest: caller-owned data must not
386
+ // change between its checksum, contract checks and eventual import preview.
387
+ const { serialized, bytes } = ensureArchiveBudget(raw);
388
+ const value = record(JSON.parse(serialized));
343
389
  if (!value || value.schema !== WORKSPACE_ARCHIVE_SCHEMA) {
344
390
  throw new Error('This file is not a WHOISleuth workspace archive.');
345
391
  }
@@ -355,11 +401,10 @@ export async function readWorkspaceArchive(raw, options = {}) {
355
401
  const sourceVersion = value.version;
356
402
  const expectedSectionIds = sourceVersion === PUBLIC_WORKSPACE_ARCHIVE_VERSION
357
403
  ? PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS
358
- : WORKSPACE_ARCHIVE_SECTION_IDS;
404
+ : sourceVersion < 9 ? WORKSPACE_ARCHIVE_SECTION_IDS.filter(id => id !== 'caseViews') : WORKSPACE_ARCHIVE_SECTION_IDS;
359
405
  const generatedAt = timestamp(value.generatedAt);
360
406
  if (!generatedAt)
361
407
  throw new Error('The workspace archive generation time is invalid.');
362
- const { bytes } = ensureArchiveBudget(value);
363
408
  assertExactKeys(value, WORKSPACE_ARCHIVE_ROOT_KEYS, `version ${sourceVersion} envelope`);
364
409
  const manifest = record(value.manifest);
365
410
  const sectionValues = record(value.sections);
@@ -423,7 +468,7 @@ export async function readWorkspaceArchive(raw, options = {}) {
423
468
  else if (definition.count(data) !== entry.recordCount) {
424
469
  throw new Error(`${entry.id} does not match its manifest record count.`);
425
470
  }
426
- sections.push({ ...entry, label: definition?.label || entry.id, status, reason, data: clone(data) });
471
+ sections.push({ ...entry, label: definition?.label || entry.id, status, reason, data });
427
472
  totalRecords += entry.recordCount;
428
473
  }
429
474
  if (Object.keys(sectionValues).some((id) => !seen.has(id))) {
@@ -450,6 +495,15 @@ export async function readWorkspaceArchive(raw, options = {}) {
450
495
  data,
451
496
  });
452
497
  }
498
+ if (sourceVersion < 9) {
499
+ const data = emptyCaseViewsStore();
500
+ sections.push({
501
+ id: 'caseViews', label: 'Saved Case views', schema: compatibilitySchema(CASE_VIEWS_COMPATIBILITY),
502
+ version: CASE_VIEWS_COMPATIBILITY.currentVersion, recordCount: 0,
503
+ bytes: byteLength(serialize(data)), checksum: await checksum(data, options.cryptoProvider), status: 'ready',
504
+ reason: 'This archive predates saved Case views. Import supplies an empty section and does not remove existing views.', data,
505
+ });
506
+ }
453
507
  sections.sort(canonicalSectionOrder);
454
508
  return {
455
509
  schema: WORKSPACE_ARCHIVE_SCHEMA,
@@ -491,9 +545,20 @@ function settingsPreview(data, local, mergedProfiles) {
491
545
  || (skipped === 0 && activeProfileId !== retainedActiveProfileId));
492
546
  return { added: 0, updated, skipped, reason, settings: { activeProfileId, theme } };
493
547
  }
548
+ /** Retain one verified snapshot; each read or preview returns an independent copy. */
549
+ export async function prepareWorkspaceArchive(raw, options = {}) {
550
+ const archive = await readWorkspaceArchive(raw, options);
551
+ return Object.freeze({
552
+ read: () => clone(archive),
553
+ preview: (localInput, selection = {}) => previewVerifiedWorkspaceArchive(clone(archive), localInput, selection),
554
+ });
555
+ }
494
556
  /** Preview section-specific non-destructive merge outcomes without writing. */
495
557
  export async function previewWorkspaceArchive(raw, localInput, options = {}) {
496
- const archive = await readWorkspaceArchive(raw, options);
558
+ // A one-shot preview owns the reader result directly; retained readers copy it.
559
+ return previewVerifiedWorkspaceArchive(await readWorkspaceArchive(raw, options), localInput, options);
560
+ }
561
+ function previewVerifiedWorkspaceArchive(archive, localInput, options) {
497
562
  const local = normalizedInput(localInput);
498
563
  const results = [];
499
564
  let mergedProfiles = local.brandProfiles;
@@ -520,15 +585,14 @@ export async function previewWorkspaceArchive(raw, localInput, options = {}) {
520
585
  results.push({
521
586
  ...section,
522
587
  status: 'ready',
523
- reason: definition.id === 'settings' && selected && (result.skipped ?? 0) > 0
524
- ? result.reason ?? 'The imported workspace preference was skipped. Existing browser-local context is preserved.'
525
- : section.reason,
588
+ reason: selected && result.reason ? result.reason : section.reason,
526
589
  added: result.added ?? 0,
527
590
  updated: result.updated ?? 0,
528
591
  skipped: result.skipped ?? 0,
529
592
  pruned: result.pruned ?? 0,
530
593
  brandProfileReferencesOmitted: result.brandProfileReferencesOmitted ?? 0,
531
594
  authoredHistoryOmitted: result.authoredHistoryOmitted ?? 0,
595
+ evidenceHistoryOmitted: result.evidenceHistoryOmitted ?? 0,
532
596
  selected,
533
597
  normalizedSettings: result.settings || null,
534
598
  });
@@ -569,6 +633,7 @@ export async function previewWorkspaceArchive(raw, localInput, options = {}) {
569
633
  pruned: 0,
570
634
  brandProfileReferencesOmitted: 0,
571
635
  authoredHistoryOmitted: 0,
636
+ evidenceHistoryOmitted: 0,
572
637
  selected: false,
573
638
  normalizedSettings: dependentId === 'settings' ? null : dependentSection.normalizedSettings ?? null,
574
639
  };