@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
@@ -7,6 +7,10 @@ import { nonEmptyErrorMessage } from "./error-detail.mjs";
7
7
  import { safeFetch, readTextCapped } from "./safe-fetch.mjs";
8
8
  import { whoisleuthRequestHeaders } from "./outbound-identity.mjs";
9
9
  import { classifyMxRecords } from "./dns-mx.mjs";
10
+ import { collectDnsInheritanceReview } from "./dns-inheritance-review.mjs";
11
+ import { normalizeExplicitIsoTimestamp } from "../packages/evidence/observation.mjs";
12
+ import { DOMAIN_POSTURE_COMPARISON_VERSION, MAX_POSTURE_CHECK_RECORDS, POSTURE_CHECK_SOURCES } from "../packages/evidence/domain-posture-context.mjs";
13
+ import { canonicalPostureRecords } from "../packages/evidence/domain-control-runtime.mjs";
10
14
  import { parseSpfRecords, parseDmarcRecords, parseMtaStsDnsRecords, parseMtaStsPolicy, parseTlsRptRecords, parseBimiRecords, parseDkimRecords, } from "./domain-posture-parsers.mjs";
11
15
  import { buildExternalDependencies, expandSpfPolicy, validateDmarcExternalReporting, } from "./domain-posture-analysis.mjs";
12
16
  const DNS_TIMEOUT_MS = 6000;
@@ -88,14 +92,23 @@ function withTimeout(promise, label, timeoutMs = DNS_TIMEOUT_MS, timers = {
88
92
  });
89
93
  }
90
94
  async function resolveDns(label, factory, timeoutMs = DNS_TIMEOUT_MS, timers) {
95
+ const completed = (result) => {
96
+ let observedAt = null;
97
+ try {
98
+ const at = timers?.now?.();
99
+ observedAt = at instanceof Date && Number.isFinite(at.getTime()) ? at.toISOString() : null;
100
+ }
101
+ catch { /* Unknown source time remains explicit. */ }
102
+ return { ...result, observedAt };
103
+ };
91
104
  try {
92
- return { records: await withTimeout(factory(), label, timeoutMs, timers), error: null };
105
+ return completed({ records: await withTimeout(factory(), label, timeoutMs, timers), error: null });
93
106
  }
94
107
  catch (err) {
95
108
  const error = errorRecord(err);
96
109
  if (typeof error.code === 'string' && MISSING_DNS_CODES.has(error.code))
97
- return { records: [], error: null };
98
- return { records: [], error: nonEmptyErrorMessage(err, String(err)) };
110
+ return completed({ records: [], error: null });
111
+ return completed({ records: [], error: nonEmptyErrorMessage(err, String(err)) });
99
112
  }
100
113
  }
101
114
  function check(id, label, status, summary, { detail = '', records = [], remediation = '' } = {}) {
@@ -189,11 +202,18 @@ function dmarcCheck(query, authorizations = []) {
189
202
  parsed.failureReporting ? 'Failure reporting is configured.' : 'No failure reporting destination is configured.',
190
203
  ...parsed.issues,
191
204
  ];
192
- const external = authorizations.filter((authorization) => authorization.state !== 'self');
193
- const unresolvedExternal = external.filter((authorization) => authorization.state !== 'authorized');
194
- if (external.length > 0) {
195
- details.push(`${external.length} external reporting destination${external.length === 1 ? '' : 's'} checked; `
196
- + `${unresolvedExternal.length} could not be authorized.`);
205
+ const unresolvedExternal = authorizations.filter((authorization) => !['self', 'authorized'].includes(authorization.state));
206
+ const intendedDestinations = parsed.aggregateDestinations.length + parsed.failureDestinations.length;
207
+ const omittedDestinations = Math.max(0, intendedDestinations - authorizations.length);
208
+ if (omittedDestinations) {
209
+ details.push(`Reporting authorisation coverage: ${authorizations.length} of ${intendedDestinations} destinations reviewed; ${omittedDestinations} not checked within the bounded collection. Omitted destinations are not assumed authorised.`);
210
+ }
211
+ if (authorizations.length > 0) {
212
+ details.push(`${authorizations.length} reporting destination${authorizations.length === 1 ? '' : 's'} reviewed; `
213
+ + `${unresolvedExternal.length} need further evidence.`);
214
+ }
215
+ if (unresolvedExternal.some(item => item.state === 'unavailable')) {
216
+ details.push('An unknown organisational boundary or unavailable query does not establish a missing authorisation requirement.');
197
217
  }
198
218
  if (parsed.testMode) {
199
219
  return check('dmarc', 'DMARC', 'warning', 'Policy is in test mode (t=y)', {
@@ -213,11 +233,11 @@ function dmarcCheck(query, authorizations = []) {
213
233
  remediation: 'Set sp and np to quarantine or reject unless weaker subdomain treatment is intentional.',
214
234
  });
215
235
  }
216
- if (unresolvedExternal.length > 0) {
217
- return check('dmarc', 'DMARC', 'warning', `Enforced at p=${parsed.policy}; external reporting authorization is incomplete`, {
236
+ if (unresolvedExternal.length > 0 || omittedDestinations > 0) {
237
+ return check('dmarc', 'DMARC', 'warning', `Enforced at p=${parsed.policy}; reporting authorisation is incomplete`, {
218
238
  detail: `${details.join(' ')} ${unresolvedExternal.map((authorization) => `${authorization.destination}: ${authorization.state}.`).join(' ')}`,
219
239
  records: parsed.records,
220
- remediation: 'Publish the required external reporting authorisation record or remove the unavailable destination.',
240
+ remediation: 'Review unresolved destinations and their organisational boundaries, then confirm any required external reporting authorisation before relying on delivery.',
221
241
  });
222
242
  }
223
243
  if (!parsed.aggregateReporting) {
@@ -332,7 +352,11 @@ function matchesMtaPattern(host, pattern) {
332
352
  const normalizedPattern = String(pattern || '').toLowerCase().replace(/\.+$/, '');
333
353
  if (normalizedPattern.startsWith('*.')) {
334
354
  const suffix = normalizedPattern.slice(1);
335
- return normalizedHost.endsWith(suffix) && normalizedHost.length > suffix.length;
355
+ if (!normalizedHost.endsWith(suffix))
356
+ return false;
357
+ const label = normalizedHost.slice(0, -suffix.length);
358
+ // MTA-STS permits a wildcard for exactly one leftmost DNS label.
359
+ return /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/u.test(label);
336
360
  }
337
361
  return normalizedHost === normalizedPattern;
338
362
  }
@@ -565,10 +589,11 @@ function defensiveMailProfileCheck(profile, input) {
565
589
  function registrationLockCheck(registry) {
566
590
  if (registry.error)
567
591
  return queryFailureCheck('registration_lock', 'Registration controls', registry.error);
568
- const statuses = registry.statuses.map((status) => status.toLowerCase().replace(/[^a-z]/gu, ''));
592
+ const statuses = canonicalPostureRecords('registration_lock', registry.statuses) ?? [];
569
593
  if (statuses.length === 0) {
570
594
  return check('registration_lock', 'Registration controls', 'info', 'Registry lock state is unavailable', {
571
595
  detail: 'No normalised EPP status was returned. This does not describe registrar account security.',
596
+ records: registry.statuses,
572
597
  });
573
598
  }
574
599
  const transferLocks = statuses.filter((status) => ['clienttransferprohibited', 'servertransferprohibited'].includes(status));
@@ -635,11 +660,36 @@ function buildPostureReport(domain, input) {
635
660
  ...(input.registry && input.nameservers ? [nameserverCheck(input.nameservers, input.registry)] : []),
636
661
  ];
637
662
  const summary = { pass: 0, warning: 0, danger: 0, info: 0 };
638
- for (const item of checks)
663
+ for (const item of checks) {
639
664
  summary[item.status] += 1;
665
+ const source = POSTURE_CHECK_SOURCES[item.id];
666
+ const query = item.id === 'nameservers' ? input.nameservers : item.id === 'mx' ? input.mx : item.id === 'caa' ? input.caa : undefined;
667
+ const registry = item.id === 'registration_lock' ? input.registry : undefined;
668
+ if (!source || (!query && !registry))
669
+ continue;
670
+ const omittedRecords = Math.max(0, item.records.length - MAX_POSTURE_CHECK_RECORDS);
671
+ const records = item.records.slice(0, MAX_POSTURE_CHECK_RECORDS);
672
+ const error = query?.error ?? registry?.error;
673
+ const retained = canonicalPostureRecords(item.id, records);
674
+ const rawRecords = query?.records ?? registry?.statuses ?? [];
675
+ const raw = item.id === 'caa'
676
+ ? canonicalPostureRecords(item.id, rawRecords.map((record) => record && typeof record === 'object' ? caaDisplay(record) : record))
677
+ : canonicalPostureRecords(item.id, rawRecords);
678
+ const complete = !error && omittedRecords === 0 && retained !== null && raw !== null
679
+ && retained.length === raw.length && retained.every((value, index) => value === raw[index])
680
+ && (!registry || (registry.statusesComplete === true && records.length > 0));
681
+ item.sourceContext = {
682
+ version: DOMAIN_POSTURE_COMPARISON_VERSION, source,
683
+ observedAt: normalizeExplicitIsoTimestamp(query?.observedAt ?? registry?.observedAt),
684
+ state: error ? 'unavailable' : complete ? 'complete' : 'partial',
685
+ omittedRecords: complete ? 0 : omittedRecords || null,
686
+ };
687
+ item.records = records;
688
+ }
640
689
  return { domain, summary, checks };
641
690
  }
642
- async function fetchMtaStsPolicy(domain, fetcher = safeFetch) {
691
+ async function fetchMtaStsPolicy(domain, fetcher = safeFetch, signal) {
692
+ signal?.throwIfAborted();
643
693
  const controller = new AbortController();
644
694
  const timeout = setTimeout(() => controller.abort(), POLICY_TIMEOUT_MS);
645
695
  try {
@@ -648,7 +698,7 @@ async function fetchMtaStsPolicy(domain, fetcher = safeFetch) {
648
698
  // following HTTP redirects for policy discovery. A zero-hop safe fetch
649
699
  // also keeps the returned body bound to the required policy host/path.
650
700
  const res = await fetcher(url, {
651
- signal: controller.signal,
701
+ signal: signal ? AbortSignal.any([signal, controller.signal]) : controller.signal,
652
702
  headers: whoisleuthRequestHeaders({ Accept: 'text/plain' }),
653
703
  }, 0);
654
704
  if (res.status !== 200) {
@@ -659,11 +709,13 @@ async function fetchMtaStsPolicy(domain, fetcher = safeFetch) {
659
709
  return { text: '', contentType: res.headers.get('content-type'), error: `Policy endpoint returned HTTP ${res.status}.` };
660
710
  }
661
711
  const body = await readTextCapped(res, MAX_POLICY_BYTES);
712
+ signal?.throwIfAborted();
662
713
  if (body.truncated)
663
714
  return { text: '', contentType: res.headers.get('content-type'), error: `Policy exceeds ${MAX_POLICY_BYTES} bytes.` };
664
715
  return { text: body.text, contentType: res.headers.get('content-type'), error: null };
665
716
  }
666
717
  catch (err) {
718
+ signal?.throwIfAborted();
667
719
  return {
668
720
  text: '',
669
721
  contentType: null,
@@ -676,17 +728,8 @@ async function fetchMtaStsPolicy(domain, fetcher = safeFetch) {
676
728
  clearTimeout(timeout);
677
729
  }
678
730
  }
679
- async function checkDomainPosture(domain, { dkimSelectors = [], retiredDkimSelectors = [], mailProtectionProfile = 'standard', } = {}, dependencies = {
680
- resolveTxt: (name) => dns.resolveTxt(name),
681
- resolveMx: (name) => dns.resolveMx(name),
682
- resolveNs: (name) => dns.resolveNs(name),
683
- resolveCaa: (name) => dns.resolveCaa(name),
684
- fetchRdapRecord,
685
- fetchMtaStsPolicy: (name) => fetchMtaStsPolicy(name),
686
- now: () => new Date(),
687
- setTimer: (callback, milliseconds) => setTimeout(callback, milliseconds),
688
- clearTimer: (handle) => clearTimeout(handle),
689
- }) {
731
+ async function collectDomainPosture(domain, { dkimSelectors = [], retiredDkimSelectors = [], mailProtectionProfile = 'standard', includeInheritedDns, signal, }, dependencies) {
732
+ signal?.throwIfAborted();
690
733
  const normalizedDomain = normalizeAuditDomain(domain);
691
734
  if (!normalizedDomain)
692
735
  throw new Error('Invalid domain name for posture audit.');
@@ -717,10 +760,11 @@ async function checkDomainPosture(domain, { dkimSelectors = [], retiredDkimSelec
717
760
  ...await resolveDns(`TXT ${selector}._domainkey.${domain}`, () => dependencies.resolveTxt(`${selector}._domainkey.${domain}`), DNS_TIMEOUT_MS, dependencies),
718
761
  })),
719
762
  ]),
720
- dependencies.fetchRdapRecord('domain', domain).catch((err) => ({
763
+ dependencies.fetchRdapRecord('domain', domain, signal ? { signal } : {}).catch((err) => ({
721
764
  error: nonEmptyErrorMessage(err, String(err)),
722
765
  })),
723
766
  ]);
767
+ signal?.throwIfAborted();
724
768
  const parsedMtaDns = mtaStsDns.error ? null : parseMtaStsDnsRecords(mtaStsDns.records);
725
769
  const enrichmentStartedAt = dependencies.now();
726
770
  if (!(enrichmentStartedAt instanceof Date) || !Number.isFinite(enrichmentStartedAt.getTime())) {
@@ -728,6 +772,7 @@ async function checkDomainPosture(domain, { dkimSelectors = [], retiredDkimSelec
728
772
  }
729
773
  const enrichmentDeadline = enrichmentStartedAt.getTime() + POSTURE_ENRICHMENT_DEADLINE_MS;
730
774
  const resolveEnrichmentTxt = (name) => {
775
+ signal?.throwIfAborted();
731
776
  const observedAt = dependencies.now();
732
777
  if (!(observedAt instanceof Date) || !Number.isFinite(observedAt.getTime())) {
733
778
  return Promise.resolve({ records: [], error: 'The bounded posture-enrichment clock was unavailable.' });
@@ -737,11 +782,22 @@ async function checkDomainPosture(domain, { dkimSelectors = [], retiredDkimSelec
737
782
  return Promise.resolve({ records: [], error: 'The bounded posture-enrichment deadline was reached.' });
738
783
  return resolveDns(`TXT ${name}`, () => dependencies.resolveTxt(name), Math.min(DNS_TIMEOUT_MS, remaining), dependencies);
739
784
  };
740
- const [mtaStsPolicy, spfExpansion, dmarcAuthorizations] = await Promise.all([
741
- parsedMtaDns?.valid ? dependencies.fetchMtaStsPolicy(domain) : Promise.resolve(null),
785
+ const enrichment = [
786
+ parsedMtaDns?.valid ? dependencies.fetchMtaStsPolicy(domain, signal) : Promise.resolve(null),
742
787
  expandSpfPolicy(domain, spf, resolveEnrichmentTxt),
743
- validateDmarcExternalReporting(domain, dmarc, resolveEnrichmentTxt),
744
- ]);
788
+ includeInheritedDns === true
789
+ ? (dependencies.collectDnsInheritanceReview
790
+ ? dependencies.collectDnsInheritanceReview(domain, dmarc, nameservers, signal ? { signal } : {})
791
+ : Promise.reject(new TypeError('The explicit DNS review collector is unavailable.')))
792
+ : validateDmarcExternalReporting(domain, dmarc, resolveEnrichmentTxt)
793
+ .then(dmarcAuthorizations => ({ checks: [], dmarcAuthorizations })),
794
+ ];
795
+ // A rejected policy request must not release the operation's capacity while
796
+ // other started enrichment collectors are still settling.
797
+ const [mtaStsPolicy, spfExpansion, dnsReview] = await Promise.all(enrichment)
798
+ .finally(() => Promise.allSettled(enrichment));
799
+ const { dmarcAuthorizations, checks: inheritanceChecks } = dnsReview;
800
+ signal?.throwIfAborted();
745
801
  const dnssec = !rdap
746
802
  ? { value: null, error: 'RDAP did not return a domain record.' }
747
803
  : 'error' in rdap
@@ -758,6 +814,8 @@ async function checkDomainPosture(domain, { dkimSelectors = [], retiredDkimSelec
758
814
  dsRecordCount: parsedDomain.dsData.length,
759
815
  dsDataTruncated: parsedDomain.dsDataTruncated,
760
816
  error: null,
817
+ observedAt: normalizeExplicitIsoTimestamp(rdap && !('error' in rdap) ? rdap.fetchedAt : null),
818
+ statusesComplete: parsedDomain.statusesTruncated === false && parsedDomain.serverTruncated === false,
761
819
  }
762
820
  : {
763
821
  statuses: [],
@@ -794,6 +852,10 @@ async function checkDomainPosture(domain, { dkimSelectors = [], retiredDkimSelec
794
852
  if (!(checkedAt instanceof Date) || !Number.isFinite(checkedAt.getTime())) {
795
853
  throw new TypeError('Domain-posture completion time must be valid.');
796
854
  }
855
+ for (const item of inheritanceChecks) {
856
+ report.checks.push(item);
857
+ report.summary[item.status] += 1;
858
+ }
797
859
  return {
798
860
  ...report,
799
861
  checkedAt: checkedAt.toISOString(),
@@ -805,4 +867,30 @@ async function checkDomainPosture(domain, { dkimSelectors = [], retiredDkimSelec
805
867
  externalDependencies,
806
868
  };
807
869
  }
870
+ async function checkDomainPosture(domain, options = {}, dependencies) {
871
+ options.signal?.throwIfAborted();
872
+ // Cancelling one audit must never cancel another audit's resolver.
873
+ const resolver = dependencies ? null : new dns.Resolver();
874
+ const cancel = () => resolver?.cancel();
875
+ options.signal?.addEventListener('abort', cancel, { once: true });
876
+ try {
877
+ options.signal?.throwIfAborted();
878
+ return await collectDomainPosture(domain, options, dependencies ?? {
879
+ resolveTxt: name => resolver.resolveTxt(name),
880
+ resolveMx: name => resolver.resolveMx(name),
881
+ resolveNs: name => resolver.resolveNs(name),
882
+ resolveCaa: name => resolver.resolveCaa(name),
883
+ fetchRdapRecord,
884
+ fetchMtaStsPolicy: (name, signal) => fetchMtaStsPolicy(name, safeFetch, signal),
885
+ now: () => new Date(),
886
+ setTimer: (callback, milliseconds) => setTimeout(callback, milliseconds),
887
+ clearTimer: handle => clearTimeout(handle),
888
+ collectDnsInheritanceReview,
889
+ });
890
+ }
891
+ finally {
892
+ options.signal?.removeEventListener('abort', cancel);
893
+ cancel();
894
+ }
895
+ }
808
896
  export { normalizeAuditDomain, normalizeDkimSelectors, normalizeMailProtectionProfile, matchesMtaPattern, fetchMtaStsPolicy, buildPostureReport, checkDomainPosture, };