@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
@@ -62,6 +62,10 @@ export function requireRecord(value, label) {
62
62
  throw new TypeError(`${label} must be an object.`);
63
63
  return value;
64
64
  }
65
+ /** Stable ordering for bounded record selection, independent of host collation. */
66
+ export function compareCodeUnits(left, right) {
67
+ return left < right ? -1 : left > right ? 1 : 0;
68
+ }
65
69
  export function requireBoundedString(value, label, maxLength) {
66
70
  const normalised = strictBoundedString(value, maxLength);
67
71
  if (!normalised) {
@@ -1,7 +1,9 @@
1
1
  import { constants } from 'node:fs';
2
2
  import { lstat, open, realpath } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
- const MAX_BOUNDED_FILE_BYTES = 64 * 1024 * 1024;
4
+ // Caller-specific payload limits remain authoritative. The emergency ceiling
5
+ // also admits a 64-MiB evidence package plus its bounded manifest/ZIP headers.
6
+ const MAX_BOUNDED_FILE_BYTES = 128 * 1024 * 1024;
5
7
  function boundedSize(value, fallback) {
6
8
  return Number.isSafeInteger(value) && Number(value) >= 0 && Number(value) <= MAX_BOUNDED_FILE_BYTES
7
9
  ? Number(value)
@@ -0,0 +1,125 @@
1
+ // Native HTML tree construction over an already-captured body. Resource limits
2
+ // are enforced while nodes are created, including implied/reconstructed nodes;
3
+ // a post-parse traversal limit alone would not bound hostile parser work.
4
+ import { parse, defaultTreeAdapter as adapter } from 'parse5';
5
+ import { MAX_HOMEPAGE_BYTES } from "./outbound-request-bounds.mjs";
6
+ import { MAX_STATIC_HTML_TAGS, MAX_STATIC_HTML_NODES, MAX_STATIC_HTML_DEPTH, } from "../packages/contracts/page-fingerprints.mjs";
7
+ export { MAX_STATIC_HTML_TAGS, MAX_STATIC_HTML_NODES, MAX_STATIC_HTML_DEPTH, MAX_STATIC_HTML_TREE_EVENTS, } from "../packages/contracts/page-fingerprints.mjs";
8
+ // UTF-8 decoding cannot produce more UTF-16 code units than captured bytes.
9
+ // Direct string callers share this ceiling; tree construction remains bounded
10
+ // independently of the source size.
11
+ export const MAX_STATIC_HTML_CHARS = MAX_HOMEPAGE_BYTES;
12
+ const MAX_CONSTRUCTED_ATTRIBUTE_CHARS = MAX_STATIC_HTML_CHARS * 4;
13
+ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
14
+ export function parseBoundedHtml(value) {
15
+ const supplied = typeof value === 'string' ? value : '';
16
+ const stop = new Error('Static HTML construction limit reached.');
17
+ let document;
18
+ let nodes = 0;
19
+ let elements = 0;
20
+ let depth = 0;
21
+ let attributeCharacters = 0;
22
+ let constructionLimitReached = false;
23
+ function reserveNode() {
24
+ if (nodes >= MAX_STATIC_HTML_NODES)
25
+ throw stop;
26
+ nodes += 1;
27
+ }
28
+ function reserveAttributes(attributes) {
29
+ for (const attribute of attributes) {
30
+ attributeCharacters += attribute.name.length + attribute.value.length;
31
+ if (attributeCharacters > MAX_CONSTRUCTED_ATTRIBUTE_CHARS)
32
+ throw stop;
33
+ }
34
+ }
35
+ const boundedAdapter = {
36
+ ...adapter,
37
+ createDocument() {
38
+ reserveNode();
39
+ document = adapter.createDocument();
40
+ return document;
41
+ },
42
+ createDocumentFragment() { reserveNode(); return adapter.createDocumentFragment(); },
43
+ createElement(name, namespace, attributes) {
44
+ // Allow the three mandatory document elements in addition to source tags.
45
+ // Reconstructed formatting elements also consume this finite allowance.
46
+ if (elements >= MAX_STATIC_HTML_TAGS + 3)
47
+ throw stop;
48
+ elements += 1;
49
+ reserveAttributes(attributes);
50
+ reserveNode();
51
+ return adapter.createElement(name, namespace, attributes);
52
+ },
53
+ createCommentNode(text) { reserveNode(); return adapter.createCommentNode(text); },
54
+ insertText(parent, text) {
55
+ const previous = parent.childNodes.at(-1);
56
+ if (!previous || !adapter.isTextNode(previous))
57
+ reserveNode();
58
+ adapter.insertText(parent, text);
59
+ },
60
+ insertTextBefore(parent, text, reference) {
61
+ const previous = parent.childNodes[parent.childNodes.indexOf(reference) - 1];
62
+ if (!previous || !adapter.isTextNode(previous))
63
+ reserveNode();
64
+ adapter.insertTextBefore(parent, text, reference);
65
+ },
66
+ setDocumentType(parent, name, publicId, systemId) {
67
+ if (!parent.childNodes.some((node) => node.nodeName === '#documentType'))
68
+ reserveNode();
69
+ adapter.setDocumentType(parent, name, publicId, systemId);
70
+ },
71
+ adoptAttributes(element, attributes) {
72
+ reserveAttributes(attributes);
73
+ adapter.adoptAttributes(element, attributes);
74
+ },
75
+ onItemPush() {
76
+ depth += 1;
77
+ if (depth > MAX_STATIC_HTML_DEPTH)
78
+ throw stop;
79
+ },
80
+ onItemPop() { depth -= 1; },
81
+ };
82
+ try {
83
+ parse(supplied.slice(0, MAX_STATIC_HTML_CHARS), {
84
+ treeAdapter: boundedAdapter,
85
+ sourceCodeLocationInfo: true,
86
+ scriptingEnabled: true,
87
+ });
88
+ }
89
+ catch (error) {
90
+ if (error !== stop)
91
+ throw error;
92
+ constructionLimitReached = true;
93
+ }
94
+ // Parser or adapter failures are not silently re-labelled as empty evidence.
95
+ if (!document)
96
+ throw new Error('Static HTML document was not initialised.');
97
+ return { document, inputLimitReached: supplied.length > MAX_STATIC_HTML_CHARS, constructionLimitReached };
98
+ }
99
+ export function isHtmlElement(node) {
100
+ return adapter.isElementNode(node) && node.namespaceURI === HTML_NAMESPACE;
101
+ }
102
+ // Iterative native-tree order, without entering inert template fragments.
103
+ // Foreign elements are retained for structure, not interpreted as HTML forms.
104
+ export function* htmlTreeEvents(document) {
105
+ const pending = [{ node: document, exit: false }];
106
+ while (pending.length) {
107
+ const { node, exit } = pending.pop();
108
+ if (adapter.isElementNode(node)) {
109
+ yield { kind: exit ? 'end' : 'start', element: node };
110
+ if (exit)
111
+ continue;
112
+ pending.push({ node, exit: true });
113
+ if (isHtmlElement(node) && node.tagName === 'template')
114
+ continue;
115
+ }
116
+ else if (adapter.isTextNode(node)) {
117
+ yield { kind: 'text', text: node.value };
118
+ }
119
+ if ('childNodes' in node) {
120
+ for (let index = node.childNodes.length - 1; index >= 0; index -= 1) {
121
+ pending.push({ node: node.childNodes[index], exit: false });
122
+ }
123
+ }
124
+ }
125
+ }
@@ -1,290 +1,2 @@
1
- export const MAX_BOUNDED_JSON_DEPTH = 48;
2
- export const MAX_BOUNDED_JSON_KEYS = 50_000;
3
- export const MAX_BOUNDED_JSON_VALUES = 100_000;
4
- export const MAX_BOUNDED_JSON_CONTAINER_ITEMS = 10_000;
5
- export const UNSAFE_JSON_OBJECT_KEYS = Object.freeze(['__proto__']);
6
- function syntaxError() {
7
- throw new TypeError('Artefact input is not valid JSON.');
8
- }
9
- export function isSafeJsonObjectKey(value) {
10
- return !UNSAFE_JSON_OBJECT_KEYS.includes(value);
11
- }
12
- export function assertBoundedJsonStructure(value, label = 'JSON value', limits = {}) {
13
- const maximumDepth = limits.maximumDepth ?? MAX_BOUNDED_JSON_DEPTH;
14
- const maximumKeys = limits.maximumKeys ?? MAX_BOUNDED_JSON_KEYS;
15
- const maximumValues = limits.maximumValues ?? MAX_BOUNDED_JSON_VALUES;
16
- const maximumContainerItems = limits.maximumContainerItems ?? MAX_BOUNDED_JSON_CONTAINER_ITEMS;
17
- let keys = 0;
18
- let values = 0;
19
- const ancestors = new Set();
20
- const visit = (current, depth) => {
21
- values += 1;
22
- if (values > maximumValues) {
23
- throw new TypeError(`${label} exceeds the ${maximumValues}-value limit.`);
24
- }
25
- if (depth > maximumDepth) {
26
- throw new TypeError(`${label} exceeds the ${maximumDepth}-level nesting limit.`);
27
- }
28
- if (current === null || typeof current === 'string' || typeof current === 'boolean')
29
- return;
30
- if (typeof current === 'number') {
31
- if (!Number.isFinite(current))
32
- throw new TypeError(`${label} contains a non-JSON number.`);
33
- return;
34
- }
35
- if (typeof current !== 'object')
36
- throw new TypeError(`${label} contains a non-JSON value.`);
37
- if (ancestors.has(current))
38
- throw new TypeError(`${label} contains a cyclic object reference.`);
39
- let prototype;
40
- let ownKeys;
41
- let descriptors;
42
- try {
43
- prototype = Object.getPrototypeOf(current);
44
- ownKeys = Reflect.ownKeys(current);
45
- descriptors = Object.getOwnPropertyDescriptors(current);
46
- }
47
- catch {
48
- throw new TypeError(`${label} could not be inspected safely.`);
49
- }
50
- if (ownKeys.some((key) => typeof key !== 'string')) {
51
- throw new TypeError(`${label} contains a symbol key.`);
52
- }
53
- if (ownKeys.some((key) => {
54
- const descriptor = descriptors[String(key)];
55
- return !descriptor || !Object.hasOwn(descriptor, 'value');
56
- })) {
57
- throw new TypeError(`${label} contains an accessor property.`);
58
- }
59
- ancestors.add(current);
60
- if (Array.isArray(current)) {
61
- if (prototype !== Array.prototype) {
62
- throw new TypeError(`${label} contains an array with a custom prototype.`);
63
- }
64
- const length = descriptors.length?.value;
65
- if (!Number.isSafeInteger(length) || length < 0 || length > maximumContainerItems) {
66
- throw new TypeError(`${label} contains a container with more than ${maximumContainerItems} items.`);
67
- }
68
- const indexes = ownKeys.filter((key) => key !== 'length');
69
- if (indexes.length !== length
70
- || indexes.some((key, index) => key !== String(index) || descriptors[key]?.enumerable !== true)) {
71
- throw new TypeError(`${label} contains a sparse, extended, or non-enumerable array.`);
72
- }
73
- for (const key of indexes)
74
- visit(descriptors[key].value, depth + 1);
75
- }
76
- else {
77
- if (prototype !== Object.prototype && prototype !== null) {
78
- throw new TypeError(`${label} contains an object with a custom prototype.`);
79
- }
80
- const objectKeys = ownKeys;
81
- if (objectKeys.length > maximumContainerItems) {
82
- throw new TypeError(`${label} contains a container with more than ${maximumContainerItems} items.`);
83
- }
84
- if (objectKeys.some((key) => descriptors[key]?.enumerable !== true)) {
85
- throw new TypeError(`${label} contains a non-enumerable object property.`);
86
- }
87
- keys += objectKeys.length;
88
- if (keys > maximumKeys) {
89
- throw new TypeError(`${label} exceeds the ${maximumKeys}-key limit.`);
90
- }
91
- for (const key of objectKeys) {
92
- if (!isSafeJsonObjectKey(key))
93
- throw new TypeError(`${label} contains an unsafe object key.`);
94
- visit(descriptors[key].value, depth + 1);
95
- }
96
- }
97
- ancestors.delete(current);
98
- };
99
- visit(value, 0);
100
- }
101
- export function scanBoundedJson(raw, limits = {}, observeString) {
102
- const maximumDepth = limits.maximumDepth ?? MAX_BOUNDED_JSON_DEPTH;
103
- const maximumKeys = limits.maximumKeys ?? MAX_BOUNDED_JSON_KEYS;
104
- const maximumValues = limits.maximumValues ?? MAX_BOUNDED_JSON_VALUES;
105
- const maximumContainerItems = limits.maximumContainerItems ?? MAX_BOUNDED_JSON_CONTAINER_ITEMS;
106
- let index = 0;
107
- let keys = 0;
108
- let values = 0;
109
- const whitespace = () => {
110
- while (index < raw.length && /[\t\n\r ]/u.test(raw[index]))
111
- index += 1;
112
- };
113
- const stringToken = () => {
114
- const start = index;
115
- index += 1;
116
- while (index < raw.length) {
117
- const character = raw[index];
118
- if (character === '"') {
119
- index += 1;
120
- try {
121
- return { value: JSON.parse(raw.slice(start, index)), offset: start };
122
- }
123
- catch {
124
- syntaxError();
125
- }
126
- }
127
- if (character === '\\') {
128
- index += 1;
129
- if (index >= raw.length)
130
- syntaxError();
131
- if (raw[index] === 'u') {
132
- if (!/^[a-fA-F0-9]{4}$/u.test(raw.slice(index + 1, index + 5)))
133
- syntaxError();
134
- index += 5;
135
- }
136
- else {
137
- if (!/["\\/bfnrt]/u.test(raw[index]))
138
- syntaxError();
139
- index += 1;
140
- }
141
- continue;
142
- }
143
- if (character.charCodeAt(0) <= 0x1f)
144
- syntaxError();
145
- index += 1;
146
- }
147
- return syntaxError();
148
- };
149
- const literal = (token) => {
150
- if (raw.slice(index, index + token.length) !== token)
151
- syntaxError();
152
- index += token.length;
153
- };
154
- const number = () => {
155
- const match = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/u.exec(raw.slice(index));
156
- if (!match)
157
- syntaxError();
158
- if (!Number.isFinite(Number(match[0]))) {
159
- throw new TypeError('Artefact JSON contains a non-finite number.');
160
- }
161
- index += match[0].length;
162
- };
163
- const value = (depth, propertyKey) => {
164
- values += 1;
165
- if (values > maximumValues) {
166
- throw new TypeError(`Artefact JSON exceeds the ${maximumValues}-value limit.`);
167
- }
168
- if (depth > maximumDepth) {
169
- throw new TypeError(`Artefact JSON exceeds the ${maximumDepth}-level nesting limit.`);
170
- }
171
- whitespace();
172
- const character = raw[index];
173
- if (character === '"') {
174
- const parsedString = stringToken();
175
- observeString?.({ ...parsedString, kind: 'value', ...(propertyKey === undefined ? {} : { propertyKey }) });
176
- return;
177
- }
178
- if (character === '{') {
179
- index += 1;
180
- whitespace();
181
- const seen = new Set();
182
- if (raw[index] === '}') {
183
- index += 1;
184
- return;
185
- }
186
- while (index < raw.length) {
187
- if (raw[index] !== '"')
188
- syntaxError();
189
- const parsedKey = stringToken();
190
- const key = parsedKey.value;
191
- if (seen.size >= maximumContainerItems) {
192
- throw new TypeError(`Artefact JSON contains a container with more than ${maximumContainerItems} items.`);
193
- }
194
- keys += 1;
195
- if (keys > maximumKeys) {
196
- throw new TypeError(`Artefact JSON exceeds the ${maximumKeys}-key limit.`);
197
- }
198
- if (seen.has(key))
199
- throw new TypeError('Artefact JSON contains a duplicate object key.');
200
- if (!isSafeJsonObjectKey(key))
201
- throw new TypeError('Artefact JSON contains an unsafe object key.');
202
- seen.add(key);
203
- observeString?.({ ...parsedKey, kind: 'key' });
204
- whitespace();
205
- if (raw[index] !== ':')
206
- syntaxError();
207
- index += 1;
208
- value(depth + 1, key);
209
- whitespace();
210
- if (raw[index] === '}') {
211
- index += 1;
212
- return;
213
- }
214
- if (raw[index] !== ',')
215
- syntaxError();
216
- index += 1;
217
- whitespace();
218
- }
219
- syntaxError();
220
- }
221
- if (character === '[') {
222
- index += 1;
223
- whitespace();
224
- if (raw[index] === ']') {
225
- index += 1;
226
- return;
227
- }
228
- let items = 0;
229
- while (index < raw.length) {
230
- items += 1;
231
- if (items > maximumContainerItems) {
232
- throw new TypeError(`Artefact JSON contains a container with more than ${maximumContainerItems} items.`);
233
- }
234
- value(depth + 1);
235
- whitespace();
236
- if (raw[index] === ']') {
237
- index += 1;
238
- return;
239
- }
240
- if (raw[index] !== ',')
241
- syntaxError();
242
- index += 1;
243
- whitespace();
244
- }
245
- syntaxError();
246
- }
247
- if (character === 't') {
248
- literal('true');
249
- return;
250
- }
251
- if (character === 'f') {
252
- literal('false');
253
- return;
254
- }
255
- if (character === 'n') {
256
- literal('null');
257
- return;
258
- }
259
- number();
260
- };
261
- whitespace();
262
- value(0);
263
- whitespace();
264
- if (index !== raw.length)
265
- syntaxError();
266
- }
267
- export function parseBoundedJson(raw, options) {
268
- const label = options.label ?? 'Artefact input';
269
- if (typeof raw !== 'string')
270
- throw new TypeError(`${label} must be UTF-8 JSON text.`);
271
- const bytes = new TextEncoder().encode(raw).byteLength;
272
- if (bytes < 1 || bytes > options.maximumBytes) {
273
- throw new TypeError(`${label} must be between 1 byte and ${options.maximumBytes} bytes.`);
274
- }
275
- scanBoundedJson(raw, options.limits);
276
- try {
277
- return JSON.parse(raw);
278
- }
279
- catch {
280
- return syntaxError();
281
- }
282
- }
283
- export function parseBoundedJsonObject(raw, options) {
284
- const label = options.label ?? 'Artefact input';
285
- const parsed = parseBoundedJson(raw, options);
286
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
287
- throw new TypeError(`${label} must contain one JSON object.`);
288
- }
289
- return parsed;
290
- }
1
+ // Retained public import; the runtime-neutral implementation lives in the domain layer.
2
+ export * from "../packages/analysis/bounded-json.mjs";