@slicedearth/whoisleuth-cli 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/DISCLOSURE +1 -1
  2. package/NOTICE +2 -2
  3. package/cli/argument-values.mjs +55 -0
  4. package/cli/arguments.mjs +24 -470
  5. package/cli/artifact-validation/case-response.mjs +67 -9
  6. package/cli/artifact-validation/lookup-evidence.mjs +18 -4
  7. package/cli/artifact-validation/signed-review.mjs +61 -64
  8. package/cli/artifact-verify.mjs +11 -2
  9. package/cli/assurance-arguments.mjs +56 -0
  10. package/cli/assurance-command-definitions.mjs +179 -0
  11. package/cli/assurance-command-runner.mjs +1 -1
  12. package/cli/bounded-json.mjs +1 -1
  13. package/cli/bulk-checkpoint.mjs +1 -1
  14. package/cli/bulk-command-runner.mjs +6 -8
  15. package/cli/bulk.mjs +2 -3
  16. package/cli/case-command.mjs +99 -6
  17. package/cli/case-pack.mjs +19 -4
  18. package/cli/ci-report.mjs +6 -3
  19. package/cli/collection-arguments.mjs +123 -0
  20. package/cli/collection-command-definitions.mjs +330 -0
  21. package/cli/collection-preflight.mjs +8 -1
  22. package/cli/command-argument-grammar.mjs +8 -210
  23. package/cli/command-definition.mjs +330 -0
  24. package/cli/command-reference.mjs +55 -1388
  25. package/cli/command-seeds.mjs +18 -0
  26. package/cli/compare.mjs +1 -1
  27. package/cli/config-profile.mjs +1 -1
  28. package/cli/ct-event-intake.mjs +4 -4
  29. package/cli/discover.mjs +2 -2
  30. package/cli/discovery-observation-snapshot.mjs +2 -2
  31. package/cli/discovery-scan-command-runner.mjs +7 -9
  32. package/cli/discovery-scan.mjs +1 -1
  33. package/cli/discovery-snapshot.mjs +2 -2
  34. package/cli/discovery-workflow.mjs +4 -4
  35. package/cli/domain-control-monitor.mjs +6 -6
  36. package/cli/domain-control-observations.mjs +2 -1
  37. package/cli/domain-feed.mjs +61 -0
  38. package/cli/errors.mjs +5 -6
  39. package/cli/evidence-arguments.mjs +46 -0
  40. package/cli/evidence-command-definitions.mjs +69 -0
  41. package/cli/evidence-command-runner.mjs +2 -3
  42. package/cli/evidence-signing.mjs +3 -2
  43. package/cli/external-observation-mapping.mjs +2 -2
  44. package/cli/formatters/json.mjs +1 -13
  45. package/cli/formatters/terminal-command-formats.mjs +6 -0
  46. package/cli/formatters/terminal-lookup.mjs +6 -0
  47. package/cli/history-arguments.mjs +39 -0
  48. package/cli/history-command-definitions.mjs +88 -0
  49. package/cli/indicator-set-command.mjs +32 -0
  50. package/cli/inline-command-families.mjs +6 -47
  51. package/cli/inline-command-runner.mjs +4 -31
  52. package/cli/input.mjs +2 -2
  53. package/cli/intake-command.mjs +147 -0
  54. package/cli/interchange-report.mjs +4 -2
  55. package/cli/investigation-artifacts.mjs +3 -4
  56. package/cli/investigation-package-review.mjs +2 -0
  57. package/cli/investigation-plan.mjs +32 -154
  58. package/cli/investigation-recipes.mjs +177 -0
  59. package/cli/investigation-run.mjs +78 -12
  60. package/cli/local-mmdb-review.mjs +220 -17
  61. package/cli/lookalike-calibration.mjs +1 -1
  62. package/cli/lookup-brief.mjs +2 -2
  63. package/cli/lookup-command-runner.mjs +19 -35
  64. package/cli/lookup-diff.mjs +10 -9
  65. package/cli/lookup-plan.mjs +10 -14
  66. package/cli/lookup-reconcile.mjs +6 -5
  67. package/cli/lookup-timeline.mjs +7 -6
  68. package/cli/mail-header-review.mjs +24 -247
  69. package/cli/mail-review.mjs +9 -7
  70. package/cli/network-arguments.mjs +51 -0
  71. package/cli/network-command-definitions.mjs +130 -0
  72. package/cli/network-command-runner.mjs +43 -31
  73. package/cli/offline-evidence-review.mjs +85 -6
  74. package/cli/posture.mjs +1 -1
  75. package/cli/registry-fixture-scaffold.mjs +1 -1
  76. package/cli/retained-artifact-diff.mjs +8 -7
  77. package/cli/review-arguments.mjs +132 -0
  78. package/cli/review-command-definitions.mjs +279 -0
  79. package/cli/review-command-runner.mjs +72 -159
  80. package/cli/risk-calibration.mjs +14 -12
  81. package/cli/runner.mjs +20 -32
  82. package/cli/saved-lookup.mjs +2 -2
  83. package/cli/selected-input-worker.mjs +73 -0
  84. package/cli/sharing-review.mjs +5 -64
  85. package/cli/source-reliability.mjs +2 -2
  86. package/cli/support-arguments.mjs +49 -0
  87. package/cli/support-command-definitions.mjs +209 -0
  88. package/cli/support-command-runner.mjs +4 -4
  89. package/cli/watchlist-review.mjs +43 -0
  90. package/cli/workflow-arguments.mjs +100 -0
  91. package/cli/workflow-command-definitions.mjs +103 -0
  92. package/cli/workflow-command-runner.mjs +40 -1
  93. package/docs/cli-reference.md +27 -0
  94. package/docs/cli.md +322 -4
  95. package/docs/contextual-reviews.md +178 -0
  96. package/docs/infrastructure-observations.md +126 -0
  97. package/docs/offline-intake-context.md +213 -0
  98. package/lib/analyst-taxonomy.mjs +2 -40
  99. package/lib/application-version.mjs +1 -1
  100. package/lib/availability.mjs +13 -789
  101. package/lib/bounded-contract-normalizers.mjs +4 -0
  102. package/lib/bounded-json.mjs +2 -330
  103. package/lib/browser-library-profile.mjs +62 -84
  104. package/lib/candidate-provenance-bounds.mjs +2 -0
  105. package/lib/classify.mjs +1 -1
  106. package/lib/credential-form-attribution.mjs +136 -0
  107. package/lib/credential-surface-profile.mjs +6 -2
  108. package/lib/ct-query.mjs +2 -27
  109. package/lib/ct-response-bounds.mjs +2 -9
  110. package/lib/ct-search.mjs +83 -47
  111. package/lib/dmarc-discovery.mjs +62 -0
  112. package/lib/dns-convergence-review.mjs +21 -8
  113. package/lib/dns-delegation-health.mjs +35 -15
  114. package/lib/dns-inheritance-review.mjs +53 -63
  115. package/lib/dns-intelligence.mjs +27 -11
  116. package/lib/dns-query-coverage.mjs +40 -0
  117. package/lib/domain-assurance.mjs +1 -1
  118. package/lib/domain-change-packet.mjs +12 -13
  119. package/lib/domain-change-review.mjs +32 -14
  120. package/lib/domain-control-flight-recorder.mjs +103 -27
  121. package/lib/domain-control-manifest.mjs +25 -17
  122. package/lib/domain-posture-analysis.mjs +75 -7
  123. package/lib/domain-posture-parsers.mjs +9 -0
  124. package/lib/domain-posture.mjs +45 -25
  125. package/lib/domain-registration.mjs +392 -0
  126. package/lib/evidence-export-privacy.mjs +27 -17
  127. package/lib/evidence-export.mjs +3 -3
  128. package/lib/evidence-report.mjs +5 -1
  129. package/lib/external-intelligence-risk.mjs +2 -124
  130. package/lib/favicon.mjs +13 -4
  131. package/lib/generated/cisa-kev-catalog.mjs +41 -3
  132. package/lib/generated/registrar-standing-catalogue.mjs +18 -10
  133. package/lib/generated/retire-browser-catalog.mjs +332 -19
  134. package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
  135. package/lib/html-signals.mjs +13 -2
  136. package/lib/http-evidence-bounds.mjs +2 -8
  137. package/lib/http-intelligence.mjs +13 -1
  138. package/lib/idn-confusable-policy.mjs +6 -4
  139. package/lib/idn-confusables.mjs +1 -1
  140. package/lib/interchange-fidelity-registry.mjs +10 -0
  141. package/lib/lookup-child-profile-contract.mjs +28 -11
  142. package/lib/lookup-contract-primitives.mjs +1 -1
  143. package/lib/lookup-diagnostics.mjs +1 -23
  144. package/lib/lookup-network-evidence-bounds.mjs +1 -10
  145. package/lib/lookup-progress.mjs +50 -1
  146. package/lib/lookup-response-contract.mjs +70 -40
  147. package/lib/lookup-response.mjs +5 -15
  148. package/lib/lookup-source-progress.mjs +8 -22
  149. package/lib/lookup.mjs +1 -1
  150. package/lib/operation-budget.mjs +4 -0
  151. package/lib/opportunity-scoring.mjs +2 -107
  152. package/lib/outbound-identity.mjs +1 -1
  153. package/lib/page-fingerprints.mjs +18 -1
  154. package/lib/perceptual-hash-comparison.mjs +2 -34
  155. package/lib/perceptual-hash.mjs +36 -10
  156. package/lib/portable-generator.mjs +2 -29
  157. package/lib/project-metadata.mjs +2 -8
  158. package/lib/rdap-client.mjs +11 -7
  159. package/lib/registrable-domain.mjs +2 -56
  160. package/lib/registrar-standing-contract.mjs +1 -1
  161. package/lib/registrar-standing.mjs +9 -13
  162. package/lib/registry-capabilities.mjs +2 -2
  163. package/lib/registry-capability-catalogue.mjs +27 -19
  164. package/lib/risk-calibration-summary.mjs +2 -352
  165. package/lib/risk-scoring.mjs +2 -345
  166. package/lib/scoring-evidence-quality.mjs +2 -107
  167. package/lib/semantic-version.mjs +2 -54
  168. package/lib/service-binding-dns.mjs +46 -2
  169. package/lib/smtp-transport-review.mjs +48 -19
  170. package/lib/sslbl-certificates.generated.mjs +142 -139
  171. package/lib/static-html-analysis.mjs +32 -4
  172. package/lib/structured-data-identity.mjs +1 -1
  173. package/lib/technology-evidence-role.mjs +1 -51
  174. package/lib/threat-intelligence-manifests.mjs +1 -1
  175. package/lib/threat-intelligence-runtime.mjs +1 -1
  176. package/lib/threat-intelligence-targets.mjs +1 -1
  177. package/lib/threat-intelligence-types.mjs +2 -75
  178. package/lib/tls-intelligence.mjs +30 -5
  179. package/lib/typosquat-generator.mjs +11 -2
  180. package/lib/website-enrichment.mjs +404 -0
  181. package/lib/whois-authority.mjs +43 -5
  182. package/lib/whois-chain.mjs +12 -3
  183. package/lib/whois-common-formats.mjs +3 -1
  184. package/lib/whois-contacts.mjs +13 -2
  185. package/lib/whois-normalization.mjs +3 -1
  186. package/lib/whois-parser.mjs +5 -3
  187. package/package.json +15 -14
  188. package/packages/analysis/analyst-taxonomy.mjs +40 -0
  189. package/packages/analysis/bounded-json.mjs +330 -0
  190. package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
  191. package/packages/analysis/cli-argument-grammar.mjs +216 -0
  192. package/packages/analysis/ct-query.mjs +27 -0
  193. package/packages/analysis/ct-response-bounds.mjs +9 -0
  194. package/packages/analysis/external-intelligence-risk.mjs +124 -0
  195. package/packages/analysis/http-evidence-bounds.mjs +8 -0
  196. package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
  197. package/packages/analysis/opportunity-scoring.mjs +107 -0
  198. package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
  199. package/packages/analysis/portable-generator.mjs +29 -0
  200. package/packages/analysis/project-metadata.mjs +8 -0
  201. package/packages/analysis/registrable-domain.mjs +56 -0
  202. package/packages/analysis/risk-calibration-summary.mjs +352 -0
  203. package/packages/analysis/risk-scoring.mjs +348 -0
  204. package/packages/analysis/scoring-evidence-quality.mjs +107 -0
  205. package/packages/analysis/semantic-version.mjs +54 -0
  206. package/packages/analysis/threat-intelligence-types.mjs +75 -0
  207. package/packages/cases/case-containment-handoff.mjs +195 -0
  208. package/packages/cases/case-evidence-links.mjs +151 -0
  209. package/packages/cases/case-evidence-model.mjs +89 -12
  210. package/packages/cases/case-evidence-quality.mjs +5 -0
  211. package/packages/cases/case-export-input.mjs +7 -5
  212. package/packages/cases/case-incident-context.mjs +52 -0
  213. package/packages/cases/case-markdown.mjs +11 -0
  214. package/packages/cases/case-migration-model.mjs +40 -24
  215. package/packages/cases/case-object-coverage.mjs +33 -0
  216. package/packages/cases/case-packet-correction.mjs +106 -0
  217. package/packages/cases/case-recheck-model.mjs +124 -36
  218. package/packages/cases/case-record-contracts.mjs +1 -1
  219. package/packages/cases/case-record-core.mjs +1 -1
  220. package/packages/cases/case-record-operations.mjs +111 -118
  221. package/packages/cases/case-record-projection.mjs +23 -4
  222. package/packages/cases/case-record-version-input.mjs +41 -0
  223. package/packages/cases/case-report.mjs +93 -27
  224. package/packages/cases/case-requested-evidence.mjs +146 -0
  225. package/packages/cases/case-response-actions.mjs +159 -1
  226. package/packages/cases/case-response-model.mjs +16 -1
  227. package/packages/cases/case-response-object.mjs +119 -0
  228. package/packages/cases/case-response-outcomes.mjs +108 -20
  229. package/packages/cases/case-response-packet-render.mjs +35 -12
  230. package/packages/cases/case-response-packet.mjs +55 -13
  231. package/packages/cases/case-response-records.mjs +0 -1
  232. package/packages/cases/case-response-review-inputs.mjs +60 -8
  233. package/packages/cases/case-storage-model.mjs +1 -1
  234. package/packages/cases/case-workflow-metadata.mjs +217 -0
  235. package/packages/cases/case-workflow-migration.mjs +43 -0
  236. package/packages/cases/http-summary.mjs +6 -1
  237. package/packages/comparison/favicon-similarity.mjs +1 -1
  238. package/packages/comparison/page-similarity.mjs +1 -1
  239. package/packages/comparison/relationship-evidence.mjs +21 -9
  240. package/packages/comparison/relationship-provenance.mjs +7 -1
  241. package/packages/contracts/analyst-interchange.mjs +35 -15
  242. package/packages/contracts/candidate-watch-review.mjs +54 -0
  243. package/packages/contracts/case-portability.mjs +360 -49
  244. package/packages/contracts/cli-command-semantics.mjs +5 -0
  245. package/packages/contracts/cli-grammar.mjs +6 -0
  246. package/packages/contracts/cli-lookup-brief.mjs +2 -0
  247. package/packages/contracts/context-review.mjs +25 -0
  248. package/packages/contracts/document-intake.mjs +11 -0
  249. package/packages/contracts/domain-control-flight-recorder.mjs +30 -14
  250. package/packages/contracts/domain-control-manifest.mjs +1 -1
  251. package/packages/contracts/domain-control-monitor.mjs +13 -2
  252. package/packages/contracts/domain-control-review.mjs +36 -15
  253. package/packages/contracts/external-observation-interchange.mjs +96 -28
  254. package/packages/contracts/har-review.mjs +2 -0
  255. package/packages/contracts/identity-events.mjs +4 -0
  256. package/packages/contracts/investigation-run.mjs +2 -0
  257. package/packages/contracts/ip-address.mjs +10 -0
  258. package/packages/contracts/lookup-evidence.mjs +3 -2
  259. package/packages/contracts/mail-authentication.mjs +5 -0
  260. package/packages/contracts/message-intake.mjs +37 -0
  261. package/packages/contracts/offline-comparison.mjs +4 -1
  262. package/packages/contracts/passive-mail.mjs +10 -0
  263. package/packages/contracts/risk-calibration.mjs +44 -29
  264. package/packages/contracts/schema-lifecycle.mjs +3 -1
  265. package/packages/contracts/web-capture.mjs +2 -2
  266. package/packages/contracts/workspace-portability.mjs +144 -28
  267. package/packages/evidence/collection-quality.mjs +67 -0
  268. package/packages/evidence/credential-form-attribution.mjs +60 -0
  269. package/packages/evidence/dns-query-outcome.mjs +72 -0
  270. package/packages/evidence/email-recipient.mjs +18 -0
  271. package/packages/evidence/image-regions.mjs +15 -0
  272. package/packages/evidence/lookup-target.mjs +5 -1
  273. package/packages/evidence/observation.mjs +2 -0
  274. package/packages/evidence/png-pixels.mjs +117 -0
  275. package/packages/evidence/public-address-policy.mjs +83 -0
  276. package/packages/evidence/record-identity.mjs +60 -16
  277. package/packages/evidence/technology-evidence-role.mjs +51 -0
  278. package/packages/interchange/defensive-indicator-export.mjs +264 -0
  279. package/packages/interchange/external-findings-import.mjs +36 -29
  280. package/packages/interchange/json-output.mjs +14 -0
  281. package/packages/interchange/managed-indicator-export.mjs +36 -0
  282. package/packages/interchange/managed-indicator-set.mjs +223 -0
  283. package/packages/interchange/misp-indicator-export.mjs +93 -0
  284. package/packages/interchange/sharing-policy.mjs +56 -0
  285. package/packages/interchange/stix-indicator-export.mjs +129 -0
  286. package/packages/interchange/web-capture-import.mjs +22 -15
  287. package/packages/investigation/acquisition-decision-packet.mjs +79 -1
  288. package/packages/investigation/bulk-mail-exposure.mjs +7 -13
  289. package/packages/investigation/capture-coverage.mjs +57 -0
  290. package/packages/investigation/connector-provenance-review.mjs +100 -0
  291. package/packages/investigation/context-review.mjs +35 -0
  292. package/packages/investigation/document-intake.mjs +87 -0
  293. package/packages/investigation/docx-intake.mjs +133 -0
  294. package/packages/investigation/domain-history-review.mjs +132 -0
  295. package/packages/investigation/domain-transition-review.mjs +49 -0
  296. package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
  297. package/packages/investigation/har-intake.mjs +68 -0
  298. package/packages/investigation/identity-event-intake.mjs +131 -0
  299. package/packages/investigation/identity-incident-review.mjs +39 -0
  300. package/packages/investigation/incident-sequence-review.mjs +58 -0
  301. package/packages/investigation/infrastructure-observation.mjs +234 -0
  302. package/packages/investigation/intake-context.mjs +284 -0
  303. package/packages/investigation/intake-indicators.mjs +116 -0
  304. package/packages/investigation/intake-phones.mjs +155 -0
  305. package/packages/investigation/intake-report.mjs +20 -0
  306. package/packages/investigation/investigation-manifest.mjs +35 -9
  307. package/packages/investigation/link-intake.mjs +209 -0
  308. package/packages/investigation/lookup-asset-graph.mjs +1 -1
  309. package/packages/investigation/mail-authentication-review.mjs +222 -0
  310. package/packages/investigation/mail-header-identity.mjs +209 -0
  311. package/packages/investigation/message-intake.mjs +235 -0
  312. package/packages/investigation/page-behaviour.mjs +113 -0
  313. package/packages/investigation/pdf-intake.mjs +155 -0
  314. package/packages/investigation/platform-continuity-review.mjs +60 -0
  315. package/packages/investigation/qr-intake.mjs +56 -0
  316. package/packages/investigation/requested-action-hints.mjs +11 -0
  317. package/packages/investigation/selected-input-review.mjs +17 -0
  318. package/packages/investigation/storefront-review.mjs +70 -0
  319. package/packages/monitoring/domain-feed.mjs +321 -0
  320. package/packages/workspace/brand-candidate-workflow.mjs +351 -0
  321. package/packages/workspace/brand-keyword-campaign.mjs +231 -0
  322. package/packages/workspace/brand-profile-model.mjs +48 -4
  323. package/packages/workspace/bulk-review-model.mjs +28 -26
  324. package/packages/workspace/bulk-session-model.mjs +27 -13
  325. package/packages/workspace/campaign-model.mjs +1 -1
  326. package/packages/workspace/candidate-watch-handoff.mjs +195 -0
  327. package/packages/workspace/detection-rule-model.mjs +11 -3
  328. package/packages/workspace/hostile-input.mjs +1 -1
  329. package/packages/workspace/investigation-guide.mjs +2 -2
  330. package/packages/workspace/investigation-template-model.mjs +26 -2
  331. package/packages/workspace/page-baseline.mjs +2 -2
  332. package/packages/workspace/relationship-observation-model.mjs +17 -13
  333. package/packages/workspace/shortlist-model.mjs +24 -7
  334. package/packages/workspace/watchlist-history.mjs +59 -16
  335. package/packages/workspace/watchlist-store.mjs +152 -11
  336. package/packages/workspace/website-snapshot-model.mjs +28 -3
  337. package/packages/workspace/workspace-archive-crypto.mjs +1 -1
  338. package/packages/workspace/workspace-archive.mjs +5 -4
  339. package/third-party-notices.txt +549 -16
  340. package/lib/hostname.mjs +0 -2
@@ -4,6 +4,7 @@
4
4
  import { htmlTreeEvents, isHtmlElement, parseBoundedHtml, MAX_STATIC_HTML_CHARS, MAX_STATIC_HTML_TAGS, MAX_STATIC_HTML_TREE_EVENTS, } from "./bounded-html-document.mjs";
5
5
  import { MAX_CSP_META_POLICIES, MAX_RESPONSE_POLICY_HEADER_BYTES, } from "./response-policy.mjs";
6
6
  import { MAX_FAVICON_BYTES, MAX_FAVICON_CANDIDATES } from "./outbound-request-bounds.mjs";
7
+ import { CREDENTIAL_CATEGORY_NAMES, MAX_CREDENTIAL_FORMS, MAX_CREDENTIAL_INPUTS } from "../packages/evidence/credential-form-attribution.mjs";
7
8
  import { MAX_PAGE_PUBLICATION_DECLARATIONS, MAX_PAGE_PUBLICATION_META_ELEMENTS, MAX_PAGE_PUBLICATION_ROBOTS_DIRECTIVES, } from "./homepage-metadata-contract.mjs";
8
9
  const MAX_STATIC_STRUCTURE_TOKENS = MAX_STATIC_HTML_TREE_EVENTS;
9
10
  const MAX_TAG_LENGTH = 4_096;
@@ -14,8 +15,8 @@ const MAX_SCRIPT_MEDIA_TYPE_LENGTH = 120;
14
15
  const MAX_INLINE_SCRIPT_TOTAL_CHARS = 65_536;
15
16
  const MAX_INLINE_SCRIPT_CHARS = MAX_INLINE_SCRIPT_TOTAL_CHARS;
16
17
  const MAX_STATIC_VISIBLE_TEXT_CHARS = MAX_STATIC_HTML_CHARS;
17
- const MAX_STATIC_FORMS = 50;
18
- const MAX_STATIC_INPUTS = 500;
18
+ const MAX_STATIC_FORMS = MAX_CREDENTIAL_FORMS;
19
+ const MAX_STATIC_INPUTS = MAX_CREDENTIAL_INPUTS;
19
20
  const MAX_STATIC_PUBLICATION_META_ELEMENTS = MAX_PAGE_PUBLICATION_META_ELEMENTS;
20
21
  const MAX_STATIC_PUBLICATION_DECLARATIONS = MAX_PAGE_PUBLICATION_DECLARATIONS;
21
22
  const MAX_STATIC_ROBOTS_DIRECTIVES = MAX_PAGE_PUBLICATION_ROBOTS_DIRECTIVES;
@@ -197,6 +198,29 @@ function stylesheetCandidate(attributes) {
197
198
  truncated: rel.truncated || href.truncated || disabled.truncated || media.truncated,
198
199
  };
199
200
  }
201
+ export function staticControlDisabled(elements, index, firstLegends) {
202
+ const hasDisabled = (element) => element.attributes.some(attribute => attribute.name === 'disabled');
203
+ if (hasDisabled(elements[index]))
204
+ return true;
205
+ for (let parent = elements[index]?.parent ?? null; parent !== null; parent = elements[parent]?.parent ?? null) {
206
+ const fieldset = elements[parent];
207
+ if (!fieldset.html || fieldset.name !== 'fieldset' || !hasDisabled(fieldset))
208
+ continue;
209
+ const legend = firstLegends.get(parent);
210
+ let underLegend = false;
211
+ for (let current = index; current !== null; current = elements[current]?.parent ?? null) {
212
+ if (current === legend) {
213
+ underLegend = true;
214
+ break;
215
+ }
216
+ if (current === parent)
217
+ break;
218
+ }
219
+ if (!underLegend)
220
+ return true;
221
+ }
222
+ return false;
223
+ }
200
224
  function analyzeStaticHtml(value, options = {}) {
201
225
  const parsed = parseBoundedHtml(value);
202
226
  const { inputLimitReached } = parsed;
@@ -209,6 +233,7 @@ function analyzeStaticHtml(value, options = {}) {
209
233
  const iconCounts = [0, 0];
210
234
  const tokens = [];
211
235
  const elementStack = [];
236
+ const firstLegends = new Map();
212
237
  const visibleTextParts = [];
213
238
  const structureTokens = [];
214
239
  const scripts = [];
@@ -386,6 +411,8 @@ function analyzeStaticHtml(value, options = {}) {
386
411
  };
387
412
  elementStack.push(elements.length);
388
413
  elements.push(element);
414
+ if (htmlElement && tagName === 'legend' && element.parent !== null && !firstLegends.has(element.parent))
415
+ firstLegends.set(element.parent, elements.length - 1);
389
416
  appendToken({ kind: 'start', element });
390
417
  // Foreign drawing attributes are not HTML technology, form or resource
391
418
  // inputs. Their per-element omission still reaches fingerprinting, but
@@ -628,7 +655,8 @@ function analyzeStaticHtml(value, options = {}) {
628
655
  }
629
656
  else {
630
657
  forms.inputsObserved += 1;
631
- const categories = inputCategories(token.attrs);
658
+ const categories = staticControlDisabled(elements, elements.length - 1, firstLegends)
659
+ ? { values: [], truncated: false } : inputCategories(token.attrs);
632
660
  if (categories.truncated)
633
661
  forms.truncated = true;
634
662
  if (categories.values.length)
@@ -716,4 +744,4 @@ function analyzeStaticHtml(value, options = {}) {
716
744
  inlineCharactersExamined,
717
745
  };
718
746
  }
719
- export { MAX_ATTRIBUTES_PER_TAG, MAX_INLINE_SCRIPT_CHARS, MAX_INLINE_SCRIPT_TOTAL_CHARS, MAX_SCRIPT_ELEMENTS, MAX_SCRIPT_MEDIA_TYPE_LENGTH, MAX_STATIC_FORMS, MAX_STATIC_HTML_CHARS, MAX_STATIC_INPUTS, MAX_STATIC_PUBLICATION_META_ELEMENTS, MAX_STATIC_PUBLICATION_DECLARATIONS, MAX_STATIC_ROBOTS_DIRECTIVES, MAX_STATIC_HTML_TAGS, MAX_STATIC_STRUCTURE_TOKENS, MAX_STATIC_VISIBLE_TEXT_CHARS, MAX_TAG_LENGTH, analyzeStaticHtml, };
747
+ export { MAX_ATTRIBUTES_PER_TAG, MAX_INLINE_SCRIPT_CHARS, MAX_INLINE_SCRIPT_TOTAL_CHARS, MAX_SCRIPT_ELEMENTS, MAX_SCRIPT_REFERENCE_LENGTH, MAX_SCRIPT_MEDIA_TYPE_LENGTH, MAX_STATIC_FORMS, MAX_STATIC_HTML_CHARS, MAX_STATIC_INPUTS, MAX_STATIC_PUBLICATION_META_ELEMENTS, MAX_STATIC_PUBLICATION_DECLARATIONS, MAX_STATIC_ROBOTS_DIRECTIVES, MAX_STATIC_HTML_TAGS, MAX_STATIC_STRUCTURE_TOKENS, MAX_STATIC_VISIBLE_TEXT_CHARS, MAX_TAG_LENGTH, analyzeStaticHtml, inputCategories as classifyStaticCredentialInput, };
@@ -6,7 +6,7 @@
6
6
  import { domainToASCII } from 'node:url';
7
7
  import { createObservation } from "../packages/evidence/observation.mjs";
8
8
  import { MAX_STRUCTURED_DATA_ENTITIES, MAX_STRUCTURED_DATA_SAME_AS_HOSTS, STRUCTURED_DATA_IDENTITY_VERSION, } from "./lookup-child-profile-contract.mjs";
9
- import { isUriShapedLabel } from "./portable-generator.mjs";
9
+ import { isUriShapedLabel } from "../packages/analysis/portable-generator.mjs";
10
10
  import { analyzeStaticHtml, } from "./static-html-analysis.mjs";
11
11
  const MAX_STRUCTURED_DATA_SCRIPTS = 8;
12
12
  const MAX_STRUCTURED_DATA_SCRIPT_CHARS = 16_384;
@@ -1,51 +1 @@
1
- const TECHNOLOGY_EVIDENCE_ROLE_ORDER = Object.freeze([
2
- 'observed_edge',
3
- 'application_platform',
4
- 'framework_runtime',
5
- 'embedded_dependency',
6
- ]);
7
- const TECHNOLOGY_EVIDENCE_ROLE_SET = new Set(TECHNOLOGY_EVIDENCE_ROLE_ORDER);
8
- function record(value) {
9
- return value && typeof value === 'object' && !Array.isArray(value)
10
- ? value
11
- : {};
12
- }
13
- function technologyEvidenceRoles(value) {
14
- const finding = record(value);
15
- const roles = new Set();
16
- let hasExplicitRole = false;
17
- for (const role of Array.isArray(finding.roles) ? finding.roles.slice(0, 4) : []) {
18
- if (typeof role === 'string' && TECHNOLOGY_EVIDENCE_ROLE_SET.has(role)) {
19
- roles.add(role);
20
- hasExplicitRole = true;
21
- }
22
- }
23
- const evidenceValues = Array.isArray(finding.evidence) ? finding.evidence.slice(0, 4) : [];
24
- for (const evidenceValue of evidenceValues) {
25
- const evidence = record(evidenceValue);
26
- if (typeof evidence.role === 'string'
27
- && TECHNOLOGY_EVIDENCE_ROLE_SET.has(evidence.role)) {
28
- roles.add(evidence.role);
29
- hasExplicitRole = true;
30
- }
31
- }
32
- if (!hasExplicitRole) {
33
- const resourceOriginOnly = evidenceValues.length > 0
34
- && evidenceValues.every((evidence) => (String(record(evidence).source).toLowerCase() === 'resource origin'));
35
- if (evidenceValues.some((evidence) => String(record(evidence).source).toLowerCase() === 'resource origin')) {
36
- roles.add('embedded_dependency');
37
- }
38
- if (!resourceOriginOnly) {
39
- const category = String(finding.category).toLowerCase();
40
- if (category === 'delivery platform')
41
- roles.add('observed_edge');
42
- else if (['application runtime', 'web framework', 'static site generator', 'web server'].includes(category)) {
43
- roles.add('framework_runtime');
44
- }
45
- else
46
- roles.add('application_platform');
47
- }
48
- }
49
- return TECHNOLOGY_EVIDENCE_ROLE_ORDER.filter((role) => roles.has(role));
50
- }
51
- export { TECHNOLOGY_EVIDENCE_ROLE_ORDER, technologyEvidenceRoles, };
1
+ export { TECHNOLOGY_EVIDENCE_ROLE_ORDER, technologyEvidenceRoles, } from "../packages/evidence/technology-evidence-role.mjs";
@@ -1,7 +1,7 @@
1
1
  import { registerCuratedConnector, registerThreatIntelligenceProvider, isCuratedConnectorDefinition, isThreatIntelligenceProvider, } from "./threat-intelligence-definition-registry.mjs";
2
2
  import { CURATED_CONNECTOR_CONTRACT_VERSION, MAX_CACHE_TTL_MS, MAX_CONNECTOR_ENTITIES, MAX_CONNECTOR_RELATIONSHIPS, MAX_RESPONSE_BYTES, MAX_TIMEOUT_MS, THREAT_INTELLIGENCE_CONTRACT_VERSION, } from "./threat-intelligence-runtime.mjs";
3
3
  import { boundedInteger, boundedHttpsUrl, enumValue, exactKeys, isRecord, strictBoundedString, } from "./bounded-contract-normalizers.mjs";
4
- import { THREAT_INTELLIGENCE_CAPABILITIES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, CURATED_CONNECTOR_KINDS, CURATED_CONNECTOR_COLLECTIONS, CURATED_CONNECTOR_CREDENTIAL_MODES, CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_TARGET_EXPOSURES, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, } from "./threat-intelligence-types.mjs";
4
+ import { THREAT_INTELLIGENCE_CAPABILITIES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, CURATED_CONNECTOR_KINDS, CURATED_CONNECTOR_COLLECTIONS, CURATED_CONNECTOR_CREDENTIAL_MODES, CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_TARGET_EXPOSURES, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, } from "../packages/analysis/threat-intelligence-types.mjs";
5
5
  import { normalizeLegacyIsoTimestamp } from "../packages/evidence/observation.mjs";
6
6
  const MAX_PROVIDER_ID_LENGTH = 40;
7
7
  const MAX_PROVIDER_LABEL_LENGTH = 100;
@@ -5,7 +5,7 @@
5
5
  // output budgets before producing separately attributed normalized evidence.
6
6
  import { createObservation } from "../packages/evidence/observation.mjs";
7
7
  import { latestObservationCohort } from "../packages/evidence/latest-observations.mjs";
8
- import { CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_RELATIONSHIP_CLASSIFICATIONS, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, THREAT_INTELLIGENCE_CONTRACT_VERSION, THREAT_INTELLIGENCE_CATEGORIES, THREAT_INTELLIGENCE_CONFIDENCES, THREAT_INTELLIGENCE_ENVELOPE_VERSION, THREAT_INTELLIGENCE_RESULT_STATES, THREAT_INTELLIGENCE_SCHEMA, THREAT_INTELLIGENCE_SEVERITIES, } from "./threat-intelligence-types.mjs";
8
+ import { CURATED_CONNECTOR_RELATIONSHIP_TYPES, CURATED_CONNECTOR_RELATIONSHIP_CLASSIFICATIONS, CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS as CONNECTOR_RELATIONSHIP_ENDPOINTS, THREAT_INTELLIGENCE_CONTRACT_VERSION, THREAT_INTELLIGENCE_CATEGORIES, THREAT_INTELLIGENCE_CONFIDENCES, THREAT_INTELLIGENCE_ENVELOPE_VERSION, THREAT_INTELLIGENCE_RESULT_STATES, THREAT_INTELLIGENCE_SCHEMA, THREAT_INTELLIGENCE_SEVERITIES, } from "../packages/analysis/threat-intelligence-types.mjs";
9
9
  import { assertCuratedConnectorDefinition, assertThreatIntelligenceProvider, } from "./threat-intelligence-definition-registry.mjs";
10
10
  import { boundedHttpsUrl, boundedString, isRecord, isoTimestamp, strictBoundedString, } from "./bounded-contract-normalizers.mjs";
11
11
  import { CURATED_CONNECTOR_ENTITY_TYPES, MAX_THREAT_INTELLIGENCE_URL_LENGTH, normalizeCuratedConnectorTarget, normalizeThreatIntelligenceTarget, } from "./threat-intelligence-targets.mjs";
@@ -2,7 +2,7 @@
2
2
  // and curated connectors. These helpers never perform network requests.
3
3
  import { classifyQuery } from "./classify.mjs";
4
4
  import { enumValue, exactKeys, strictBoundedString, } from "./bounded-contract-normalizers.mjs";
5
- import { CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_TARGET_EXPOSURES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, } from "./threat-intelligence-types.mjs";
5
+ import { CURATED_CONNECTOR_ENTITY_VALUES, CURATED_CONNECTOR_TARGET_EXPOSURES, THREAT_INTELLIGENCE_TARGET_EXPOSURES, } from "../packages/analysis/threat-intelligence-types.mjs";
6
6
  export const MAX_THREAT_INTELLIGENCE_URL_LENGTH = 2048;
7
7
  const TARGET_EXPOSURES = Object.freeze({
8
8
  domain: Object.freeze(['registrable_domain']),
@@ -1,75 +1,2 @@
1
- // Provider-neutral types shared by threat-intelligence and curated connector
2
- // manifests, fixture harnesses, normalizers, and result builders.
3
- export const THREAT_INTELLIGENCE_SCHEMA = 'whoisleuth.threat-intelligence-result';
4
- export const THREAT_INTELLIGENCE_CONTRACT_VERSION = 1;
5
- export const THREAT_INTELLIGENCE_ENVELOPE_VERSION = 1;
6
- export const THREAT_INTELLIGENCE_TARGET_EXPOSURES = Object.freeze([
7
- 'registrable_domain', 'hostname', 'origin', 'full_url',
8
- ]);
9
- export const THREAT_INTELLIGENCE_CAPABILITIES = Object.freeze([
10
- 'domain_lookup', 'url_lookup', 'indicator_search',
11
- ]);
12
- export const THREAT_INTELLIGENCE_CATEGORIES = Object.freeze([
13
- 'phishing',
14
- 'malware',
15
- 'spam',
16
- 'suspicious',
17
- 'abuse',
18
- 'unknown',
19
- ]);
20
- export const THREAT_INTELLIGENCE_SEVERITIES = Object.freeze([
21
- 'critical',
22
- 'high',
23
- 'medium',
24
- 'low',
25
- 'unknown',
26
- ]);
27
- export const THREAT_INTELLIGENCE_CONFIDENCES = Object.freeze([
28
- 'high',
29
- 'medium',
30
- 'low',
31
- 'unknown',
32
- ]);
33
- export const THREAT_INTELLIGENCE_RESULT_STATES = Object.freeze([
34
- 'success',
35
- 'partial',
36
- 'not_found',
37
- 'unsupported',
38
- 'skipped',
39
- 'rate_limited',
40
- 'unavailable',
41
- 'error',
42
- ]);
43
- export const CURATED_CONNECTOR_KINDS = Object.freeze(['discovery', 'enrichment']);
44
- export const CURATED_CONNECTOR_COLLECTIONS = Object.freeze(['passive', 'active', 'third_party']);
45
- export const CURATED_CONNECTOR_CREDENTIAL_MODES = Object.freeze(['none', 'optional', 'required']);
46
- export const CURATED_CONNECTOR_ENTITY_VALUES = Object.freeze([
47
- 'domain', 'hostname', 'url', 'ipv4', 'ipv6', 'asn', 'certificate',
48
- ]);
49
- export const CURATED_CONNECTOR_RELATIONSHIP_TYPES = Object.freeze([
50
- 'domain_resolves_to_ip', 'domain_uses_nameserver', 'domain_uses_mail_server',
51
- 'domain_presented_certificate', 'certificate_names_domain', 'ip_hosts_domain',
52
- 'domain_related_to_domain',
53
- ]);
54
- export const CURATED_CONNECTOR_RELATIONSHIP_CLASSIFICATIONS = Object.freeze(['direct', 'normalized', 'derived']);
55
- export const CURATED_CONNECTOR_TARGET_EXPOSURES = Object.freeze({
56
- domain: Object.freeze(['registrable_domain']),
57
- hostname: Object.freeze(['hostname']),
58
- url: THREAT_INTELLIGENCE_TARGET_EXPOSURES,
59
- ipv4: Object.freeze(['ip_address']),
60
- ipv6: Object.freeze(['ip_address']),
61
- asn: Object.freeze(['asn']),
62
- certificate: Object.freeze(['certificate_fingerprint']),
63
- });
64
- function relationshipEndpoints(from, to) {
65
- return Object.freeze({ from: Object.freeze(from), to: Object.freeze(to) });
66
- }
67
- export const CURATED_CONNECTOR_RELATIONSHIP_ENDPOINTS = Object.freeze({
68
- domain_resolves_to_ip: relationshipEndpoints(['domain', 'hostname'], ['ipv4', 'ipv6']),
69
- domain_uses_nameserver: relationshipEndpoints(['domain'], ['hostname']),
70
- domain_uses_mail_server: relationshipEndpoints(['domain'], ['hostname']),
71
- domain_presented_certificate: relationshipEndpoints(['domain', 'hostname'], ['certificate']),
72
- certificate_names_domain: relationshipEndpoints(['certificate'], ['domain', 'hostname']),
73
- ip_hosts_domain: relationshipEndpoints(['ipv4', 'ipv6'], ['domain', 'hostname']),
74
- domain_related_to_domain: relationshipEndpoints(['domain', 'hostname'], ['domain', 'hostname']),
75
- });
1
+ // Retained public import; the runtime-neutral implementation lives in the domain layer.
2
+ export * from "../packages/analysis/threat-intelligence-types.mjs";
@@ -705,6 +705,7 @@ function skippedTlsObservation(detail = 'TLS evidence collection is disabled by
705
705
  };
706
706
  }
707
707
  async function collectTlsIntelligence(hostname, options = {}) {
708
+ options.signal?.throwIfAborted();
708
709
  const normalizedHostname = normalizeTlsHostname(hostname);
709
710
  const now = options.now || Date.now;
710
711
  const observedAt = options.observedAt || (() => new Date().toISOString());
@@ -725,9 +726,11 @@ async function collectTlsIntelligence(hostname, options = {}) {
725
726
  resolutionDeadline = setTimer(() => reject(new Error('TLS resolution timed out')), timeoutMs);
726
727
  }),
727
728
  ]);
729
+ options.signal?.throwIfAborted();
728
730
  records = normalizePublicAddressRecords(resolved);
729
731
  }
730
732
  catch (error) {
733
+ options.signal?.throwIfAborted();
731
734
  return failedTlsObservation(error, { sniHost: normalizedHostname, observedAt: observedAt(), durationMs: now() - started });
732
735
  }
733
736
  finally {
@@ -755,18 +758,29 @@ async function collectTlsIntelligence(hostname, options = {}) {
755
758
  durationMs: now() - started,
756
759
  });
757
760
  }
758
- return new Promise((resolve) => {
761
+ return new Promise((resolve, reject) => {
759
762
  let socket;
760
763
  let deadline;
761
764
  let settled = false;
762
- const finish = (result) => {
763
- if (settled)
764
- return;
765
- settled = true;
765
+ const cleanup = () => {
766
766
  if (deadline !== undefined)
767
767
  clearTimer(deadline);
768
+ options.signal?.removeEventListener('abort', cancelled);
768
769
  if (socket && typeof socket.destroy === 'function')
769
770
  socket.destroy();
771
+ };
772
+ const cancelled = () => {
773
+ if (settled)
774
+ return;
775
+ settled = true;
776
+ cleanup();
777
+ reject(options.signal.reason);
778
+ };
779
+ const finish = (result) => {
780
+ if (settled)
781
+ return;
782
+ settled = true;
783
+ cleanup();
770
784
  resolve(result);
771
785
  };
772
786
  const fail = (error) => finish(failedTlsObservation(error, {
@@ -776,6 +790,11 @@ async function collectTlsIntelligence(hostname, options = {}) {
776
790
  observedAt: observedAt(),
777
791
  durationMs: now() - started,
778
792
  }));
793
+ options.signal?.addEventListener('abort', cancelled, { once: true });
794
+ if (options.signal?.aborted) {
795
+ cancelled();
796
+ return;
797
+ }
779
798
  try {
780
799
  socket = connect({
781
800
  host: selected.address,
@@ -787,6 +806,8 @@ async function collectTlsIntelligence(hostname, options = {}) {
787
806
  checkServerIdentity: () => undefined,
788
807
  ALPNProtocols: ['h2', 'http/1.1'],
789
808
  }, () => {
809
+ if (settled)
810
+ return;
790
811
  try {
791
812
  const peerCertificate = socket.getPeerCertificate(true);
792
813
  let hostnameError = null;
@@ -816,6 +837,10 @@ async function collectTlsIntelligence(hostname, options = {}) {
816
837
  fail(error);
817
838
  }
818
839
  });
840
+ if (settled) {
841
+ socket.destroy();
842
+ return;
843
+ }
819
844
  socket.once('error', fail);
820
845
  deadline = setTimer(() => fail(new Error('TLS handshake timed out')), remainingMs);
821
846
  if (settled && deadline !== undefined)
@@ -2,7 +2,7 @@
2
2
  // objects so a later scan can explain why each domain was generated; callers
3
3
  // that only need the historical string list can use the compatibility wrapper.
4
4
  import { advancedConfusableVariantsForAscii, confusableCharactersForAscii, estimateAdvancedConfusableVariants, MAX_ADVANCED_CONFUSABLE_VARIANTS, wholeLabelConfusableVariantsForAscii, } from "./idn-confusables.mjs";
5
- import { canonicalRegistrableDomain, normalizeDiscoverySuffix } from "./registrable-domain.mjs";
5
+ import { canonicalRegistrableDomain, normalizeDiscoverySuffix } from "../packages/analysis/registrable-domain.mjs";
6
6
  import { MAX_DOMAIN_NAME_LENGTH, MAX_DOMAIN_LABEL_LENGTH } from "../packages/contracts/domain-name.mjs";
7
7
  export { MAX_ADVANCED_CONFUSABLE_VARIANTS };
8
8
  function buildKeyboardAdjacency(rows) {
@@ -775,12 +775,21 @@ export function generateTyposquatCandidateSet(rawInput, fallbackTlds, options =
775
775
  const advanced = advancedConfusableVariantsForAscii(name);
776
776
  const rejectedBefore = state.rejectedVariantCount;
777
777
  let rejectedByEncoding = 0;
778
+ const encodedLabels = new Set();
779
+ let duplicatesAfterEncoding = 0;
778
780
  for (const variant of advanced.variants) {
779
781
  const ascii = toAsciiLabel(variant.unicodeLabel);
780
782
  if (!ascii) {
781
783
  rejectedByEncoding += 1;
782
784
  continue;
783
785
  }
786
+ // Different Unicode spellings can identify the same IDNA domain. They
787
+ // are duplicate candidates, not work omitted by a resource budget.
788
+ if (encodedLabels.has(ascii)) {
789
+ duplicatesAfterEncoding += 1;
790
+ continue;
791
+ }
792
+ encodedLabels.add(ascii);
784
793
  addVariant(state, ascii, 'unicode_homoglyph_depth_2');
785
794
  }
786
795
  const generated = [...state.variants.values()]
@@ -788,7 +797,7 @@ export function generateTyposquatCandidateSet(rawInput, fallbackTlds, options =
788
797
  .length;
789
798
  const rejectedByValidation = state.rejectedVariantCount - rejectedBefore;
790
799
  const omittedByBudget = advanced.omittedByBudget
791
- + Math.max(0, advanced.variants.length - rejectedByEncoding - rejectedByValidation - generated);
800
+ + Math.max(0, advanced.variants.length - duplicatesAfterEncoding - rejectedByEncoding - rejectedByValidation - generated);
792
801
  advancedConfusable = {
793
802
  generated,
794
803
  omittedByPolicy: advanced.omittedByPolicy + rejectedByEncoding + rejectedByValidation,