@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.
- package/NOTICE +8 -2
- package/README.md +19 -15
- package/bin/whoisleuth.mjs +8 -1
- package/cli/archive-content-digest.mjs +14 -0
- package/cli/archive-inspect.mjs +20 -53
- package/cli/argument-values.mjs +55 -0
- package/cli/arguments.mjs +24 -408
- package/cli/artifact-validation/case-response.mjs +65 -44
- package/cli/artifact-validation/investigation-capsule.mjs +52 -31
- package/cli/artifact-validation/lookup-evidence.mjs +15 -9
- package/cli/artifact-validation/signed-review.mjs +136 -68
- package/cli/artifact-validation/structure-primitives.mjs +1 -105
- package/cli/artifact-verify.mjs +92 -24
- package/cli/assurance-arguments.mjs +56 -0
- package/cli/assurance-command-definitions.mjs +179 -0
- package/cli/assurance-command-runner.mjs +40 -5
- package/cli/bagit-folder.mjs +57 -0
- package/cli/bagit-review.mjs +23 -0
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +1 -1
- package/cli/bulk-command-runner.mjs +4 -2
- package/cli/bulk-output.mjs +29 -3
- package/cli/bulk.mjs +2 -3
- package/cli/case-command.mjs +212 -0
- package/cli/case-pack.mjs +50 -32
- package/cli/ci-report.mjs +6 -3
- package/cli/collection-arguments.mjs +123 -0
- package/cli/collection-command-definitions.mjs +330 -0
- package/cli/collection-preflight.mjs +8 -1
- package/cli/command-argument-grammar.mjs +8 -206
- package/cli/command-definition.mjs +329 -0
- package/cli/command-reference.mjs +94 -1357
- package/cli/command-seeds.mjs +18 -0
- package/cli/compare.mjs +1 -1
- package/cli/completion.mjs +298 -79
- package/cli/config-profile.mjs +8 -6
- package/cli/ct-event-intake.mjs +16 -16
- package/cli/discover.mjs +5 -4
- package/cli/discovery-observation-snapshot.mjs +46 -18
- package/cli/discovery-scan-command-runner.mjs +5 -3
- package/cli/discovery-scan.mjs +7 -4
- package/cli/discovery-snapshot.mjs +2 -2
- package/cli/discovery-workflow.mjs +5 -5
- package/cli/domain-control-monitor.mjs +50 -20
- package/cli/domain-control-observations.mjs +151 -103
- package/cli/errors.mjs +37 -6
- package/cli/evidence-arguments.mjs +46 -0
- package/cli/evidence-command-definitions.mjs +69 -0
- package/cli/evidence-command-runner.mjs +18 -8
- package/cli/evidence-signing.mjs +5 -4
- package/cli/external-observation-mapping.mjs +9 -10
- package/cli/formatters/terminal-command-formats.mjs +6 -0
- package/cli/formatters/terminal-lookup.mjs +24 -46
- package/cli/formatters/terminal-shared.mjs +5 -3
- package/cli/formatters/terminal-technology.mjs +114 -0
- package/cli/history-arguments.mjs +39 -0
- package/cli/history-command-definitions.mjs +88 -0
- package/cli/indicator-set-command.mjs +33 -0
- package/cli/inline-command-families.mjs +6 -46
- package/cli/inline-command-runner.mjs +4 -31
- package/cli/input.mjs +2 -2
- package/cli/intake-command.mjs +107 -0
- package/cli/interactive-launcher.mjs +2 -160
- package/cli/interchange-report.mjs +8 -10
- package/cli/investigation-artifacts.mjs +176 -0
- package/cli/investigation-checkpoint.mjs +10 -0
- package/cli/investigation-folder.mjs +114 -0
- package/cli/investigation-manifest.mjs +1 -112
- package/cli/investigation-package-review.mjs +103 -0
- package/cli/investigation-plan.mjs +38 -147
- package/cli/investigation-recipes.mjs +177 -0
- package/cli/investigation-run.mjs +256 -35
- package/cli/local-document-checkpoint.mjs +170 -0
- package/cli/local-mmdb-review.mjs +197 -17
- package/cli/lookalike-calibration.mjs +1 -1
- package/cli/lookup-brief.mjs +2 -2
- package/cli/lookup-browser.mjs +5 -12
- package/cli/lookup-command-runner.mjs +33 -39
- package/cli/lookup-diff.mjs +47 -12
- package/cli/lookup-plan.mjs +16 -14
- package/cli/lookup-reconcile.mjs +15 -12
- package/cli/lookup-timeline.mjs +7 -6
- package/cli/mail-header-review.mjs +24 -247
- package/cli/mail-review.mjs +75 -21
- package/cli/manual.mjs +1 -0
- package/cli/network-arguments.mjs +51 -0
- package/cli/network-command-definitions.mjs +130 -0
- package/cli/network-command-runner.mjs +45 -31
- package/cli/offline-evidence-review.mjs +39 -6
- package/cli/output-file.mjs +31 -5
- package/cli/posture.mjs +1 -1
- package/cli/registry-doctor.mjs +3 -1
- package/cli/registry-fixture-scaffold.mjs +1 -1
- package/cli/registry-support.mjs +1 -1
- package/cli/retained-artifact-diff.mjs +10 -9
- package/cli/review-arguments.mjs +131 -0
- package/cli/review-command-definitions.mjs +273 -0
- package/cli/review-command-runner.mjs +104 -149
- package/cli/risk-calibration.mjs +8 -8
- package/cli/runner.mjs +74 -37
- package/cli/saved-lookup.mjs +2 -2
- package/cli/selected-input-worker.mjs +63 -0
- package/cli/sharing-review.mjs +8 -80
- package/cli/signer-trust.mjs +104 -0
- package/cli/source-reliability.mjs +75 -84
- package/cli/support-arguments.mjs +49 -0
- package/cli/support-command-definitions.mjs +209 -0
- package/cli/support-command-runner.mjs +2 -2
- package/cli/terminal-input.mjs +168 -0
- package/cli/workflow-arguments.mjs +86 -0
- package/cli/workflow-command-definitions.mjs +78 -0
- package/cli/workflow-command-runner.mjs +75 -13
- package/docs/cli-reference.md +103 -30
- package/docs/cli.md +510 -25
- package/docs/contextual-reviews.md +155 -0
- package/lib/abort.mjs +4 -1
- package/lib/analyst-taxonomy.mjs +2 -40
- package/lib/application-version.mjs +1 -1
- package/lib/availability.mjs +27 -754
- package/lib/bounded-contract-normalizers.mjs +4 -0
- package/lib/bounded-file.mjs +3 -1
- package/lib/bounded-html-document.mjs +125 -0
- package/lib/bounded-json.mjs +2 -290
- package/lib/browser-library-profile.mjs +99 -130
- package/lib/browser-library-worker.mjs +112 -0
- package/lib/candidate-provenance-bounds.mjs +2 -0
- package/lib/classify.mjs +1 -1
- package/lib/client-behavior-profile.mjs +3 -3
- package/lib/ct-query.mjs +2 -27
- package/lib/ct-response-bounds.mjs +2 -9
- package/lib/ct-search.mjs +89 -46
- package/lib/dmarc-discovery.mjs +62 -0
- package/lib/dns-convergence-review.mjs +21 -8
- package/lib/dns-delegation-health.mjs +36 -22
- package/lib/dns-inheritance-review.mjs +207 -0
- package/lib/dns-intelligence.mjs +44 -29
- package/lib/dns-mx.mjs +1 -1
- package/lib/dns-query-coverage.mjs +40 -0
- package/lib/dnssec-evidence-validation.mjs +11 -3
- package/lib/domain-assurance.mjs +1 -1
- package/lib/domain-change-packet.mjs +18 -14
- package/lib/domain-change-review.mjs +40 -16
- package/lib/domain-control-flight-recorder.mjs +301 -124
- package/lib/domain-control-manifest.mjs +126 -58
- package/lib/domain-posture-analysis.mjs +41 -11
- package/lib/domain-posture-parsers.mjs +88 -6
- package/lib/domain-posture.mjs +120 -32
- package/lib/domain-registration.mjs +392 -0
- package/lib/evidence-export-privacy.mjs +568 -0
- package/lib/evidence-export.mjs +12 -593
- package/lib/evidence-report.mjs +7 -2
- package/lib/external-intelligence-risk.mjs +2 -124
- package/lib/favicon.mjs +117 -41
- package/lib/generated/cisa-kev-catalog.mjs +50 -4
- package/lib/generated/registrar-standing-catalogue.mjs +18 -10
- package/lib/generated/retire-browser-catalog.mjs +2742 -1034
- package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
- package/lib/html-signals.mjs +87 -85
- package/lib/http-evidence-bounds.mjs +2 -7
- package/lib/http-intelligence.mjs +52 -27
- package/lib/idn-confusable-policy.mjs +6 -4
- package/lib/idn-confusables.mjs +1 -1
- package/lib/interchange-fidelity-registry.mjs +10 -0
- package/lib/lookup-cache.mjs +34 -11
- package/lib/lookup-child-profile-contract.mjs +24 -15
- package/lib/lookup-contract-primitives.mjs +1 -1
- package/lib/lookup-diagnostics.mjs +2 -1
- package/lib/lookup-network-evidence-bounds.mjs +1 -10
- package/lib/lookup-progress.mjs +79 -13
- package/lib/lookup-response-contract.mjs +1077 -0
- package/lib/lookup-response.mjs +11 -24
- package/lib/lookup-source-progress.mjs +24 -35
- package/lib/lookup.mjs +49 -28
- package/lib/observed-network-context.mjs +43 -27
- package/lib/opportunity-scoring.mjs +2 -107
- package/lib/outbound-identity.mjs +1 -1
- package/lib/outbound-request-bounds.mjs +4 -1
- package/lib/page-fingerprints.mjs +87 -134
- package/lib/page-role-profile.mjs +12 -8
- package/lib/perceptual-hash-comparison.mjs +2 -34
- package/lib/perceptual-hash.mjs +79 -24
- package/lib/portable-generator.mjs +2 -29
- package/lib/project-metadata.mjs +2 -8
- package/lib/rdap-bootstrap.mjs +72 -22
- package/lib/rdap-client.mjs +26 -14
- package/lib/rdap-normalization.mjs +33 -18
- package/lib/rdap-search-workbench.mjs +1 -1
- package/lib/rdap-transport.mjs +47 -20
- package/lib/rdap-types.mjs +2 -1
- package/lib/rdap.mjs +18 -3
- package/lib/registrable-domain.mjs +2 -36
- package/lib/registrar-standing-contract.mjs +2 -1
- package/lib/registrar-standing.mjs +9 -13
- package/lib/registry-capabilities.mjs +7 -3
- package/lib/registry-capability-catalogue.mjs +29 -20
- package/lib/registry-dates.mjs +2 -190
- package/lib/registry-insights.mjs +56 -18
- package/lib/response-policy.mjs +86 -48
- package/lib/risk-calibration-summary.mjs +2 -338
- package/lib/risk-scoring.mjs +2 -345
- package/lib/rpki-evidence.mjs +8 -5
- package/lib/safe-fetch.mjs +54 -47
- package/lib/scoring-evidence-quality.mjs +2 -107
- package/lib/security-txt.mjs +10 -7
- package/lib/semantic-version.mjs +2 -54
- package/lib/service-binding-dns.mjs +35 -0
- package/lib/smtp-transport-review.mjs +75 -34
- package/lib/sslbl-certificates.generated.mjs +141 -138
- package/lib/static-html-analysis.mjs +117 -146
- package/lib/structured-data-identity.mjs +2 -1
- package/lib/technology-markup.mjs +100 -0
- package/lib/threat-intelligence-manifests.mjs +11 -139
- package/lib/threat-intelligence-runtime.mjs +24 -52
- package/lib/threat-intelligence-targets.mjs +6 -34
- package/lib/threat-intelligence-types.mjs +2 -36
- package/lib/typosquat-generator.mjs +29 -16
- package/lib/urlhaus-intelligence.mjs +8 -3
- package/lib/urlscan-intelligence.mjs +1 -1
- package/lib/website-enrichment.mjs +388 -0
- package/lib/website-security-posture.mjs +30 -6
- package/lib/website-technology.mjs +111 -71
- package/lib/whois-authority.mjs +55 -5
- package/lib/whois-chain.mjs +9 -2
- package/lib/whois-common-formats.mjs +5 -5
- package/lib/whois-contracts.mjs +2 -1
- package/lib/whois-dialects.mjs +2 -2
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-parser-support.mjs +8 -1
- package/lib/whois-parser.mjs +5 -3
- package/lib/whois-transport.mjs +23 -7
- package/package.json +14 -14
- package/packages/analysis/analyst-taxonomy.mjs +40 -0
- package/packages/analysis/bounded-json.mjs +330 -0
- package/packages/analysis/candidate-provenance-bounds.mjs +4 -0
- package/packages/analysis/cli-argument-grammar.mjs +216 -0
- package/packages/analysis/ct-query.mjs +27 -0
- package/packages/analysis/ct-response-bounds.mjs +9 -0
- package/packages/analysis/external-intelligence-risk.mjs +124 -0
- package/packages/analysis/http-evidence-bounds.mjs +8 -0
- package/packages/analysis/lookup-network-evidence-bounds.mjs +10 -0
- package/packages/analysis/opportunity-scoring.mjs +107 -0
- package/packages/analysis/perceptual-hash-comparison.mjs +34 -0
- package/packages/analysis/portable-generator.mjs +29 -0
- package/packages/analysis/project-metadata.mjs +8 -0
- package/packages/analysis/registrable-domain.mjs +56 -0
- package/packages/analysis/risk-calibration-summary.mjs +352 -0
- package/packages/analysis/risk-scoring.mjs +345 -0
- package/packages/analysis/scoring-evidence-quality.mjs +107 -0
- package/packages/analysis/semantic-version.mjs +54 -0
- package/packages/analysis/threat-intelligence-types.mjs +75 -0
- package/packages/cases/case-attachment-model.mjs +83 -0
- package/packages/cases/case-evidence-links.mjs +135 -0
- package/packages/cases/case-evidence-model.mjs +229 -69
- package/packages/cases/case-evidence-quality.mjs +5 -0
- package/packages/cases/case-export-input.mjs +55 -0
- package/packages/cases/case-incident-context.mjs +52 -0
- package/packages/cases/case-markdown.mjs +11 -0
- package/packages/cases/case-migration-model.mjs +98 -48
- package/packages/cases/case-model.mjs +1 -0
- package/packages/cases/case-recheck-model.mjs +149 -0
- package/packages/cases/case-record-contracts.mjs +1 -1
- package/packages/cases/case-record-core.mjs +4 -3
- package/packages/cases/case-record-operations.mjs +123 -133
- package/packages/cases/case-record-projection.mjs +30 -20
- package/packages/cases/case-record-version-input.mjs +19 -0
- package/packages/cases/case-report.mjs +114 -67
- package/packages/cases/case-requested-evidence.mjs +146 -0
- package/packages/cases/case-response-actions.mjs +756 -0
- package/packages/cases/case-response-model.mjs +49 -1129
- package/packages/cases/case-response-outcomes.mjs +338 -0
- package/packages/cases/case-response-packet-render.mjs +157 -0
- package/packages/cases/case-response-packet-types.mjs +1 -0
- package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
- package/packages/cases/case-response-packet.mjs +58 -593
- package/packages/cases/case-response-records.mjs +93 -0
- package/packages/cases/case-response-review-inputs.mjs +382 -0
- package/packages/cases/case-response-values.mjs +85 -0
- package/packages/cases/case-review-package.mjs +45 -0
- package/packages/cases/case-selection.mjs +46 -0
- package/packages/cases/case-storage-model.mjs +26 -1
- package/packages/cases/case-workflow-metadata.mjs +225 -0
- package/packages/cases/case-workflow-migration.mjs +43 -0
- package/packages/cases/http-summary.mjs +6 -1
- package/packages/cases/response-route-freshness.mjs +16 -0
- package/packages/comparison/capture-context.mjs +61 -0
- package/packages/comparison/comparison-ledger-bulk.mjs +4 -1
- package/packages/comparison/favicon-similarity.mjs +1 -1
- package/packages/comparison/page-similarity.mjs +3 -2
- package/packages/comparison/relationship-evidence.mjs +103 -14
- package/packages/comparison/relationship-provenance.mjs +99 -0
- package/packages/contracts/analyst-interchange.mjs +234 -0
- package/packages/contracts/analyst-review-state-contract.mjs +11 -0
- package/packages/contracts/analyst-review-state.mjs +5 -4
- package/packages/contracts/case-portability.mjs +370 -114
- package/packages/contracts/case-views-contract.mjs +12 -0
- package/packages/contracts/case-views.mjs +26 -0
- package/packages/contracts/cli-command-semantics.mjs +3 -0
- package/packages/contracts/cli-grammar.mjs +6 -0
- package/packages/contracts/cli-lookup-brief.mjs +2 -0
- package/packages/contracts/cli-lookup.mjs +0 -1
- package/packages/contracts/context-review.mjs +24 -0
- package/packages/contracts/document-intake.mjs +11 -0
- package/packages/contracts/domain-control-flight-recorder.mjs +98 -44
- package/packages/contracts/domain-control-manifest.mjs +128 -74
- package/packages/contracts/domain-control-monitor.mjs +59 -45
- package/packages/contracts/domain-control-review.mjs +200 -121
- package/packages/contracts/domain-name.mjs +21 -0
- package/packages/contracts/external-observation-interchange.mjs +2 -1
- package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
- package/packages/contracts/har-review.mjs +2 -0
- package/packages/contracts/http-url.mjs +2 -0
- package/packages/contracts/identity-events.mjs +4 -0
- package/packages/contracts/investigation-package-limits.mjs +24 -0
- package/packages/contracts/investigation-portability.mjs +56 -27
- package/packages/contracts/investigation-run.mjs +18 -0
- package/packages/contracts/ip-address.mjs +44 -0
- package/packages/contracts/lookup-evidence.mjs +35 -0
- package/packages/contracts/mail-authentication.mjs +5 -0
- package/packages/contracts/message-intake.mjs +31 -0
- package/packages/contracts/offline-comparison.mjs +4 -2
- package/packages/contracts/page-fingerprints.mjs +15 -0
- package/packages/contracts/passive-mail.mjs +10 -0
- package/packages/contracts/risk-calibration.mjs +0 -1
- package/packages/contracts/schema-compatibility.mjs +1 -1
- package/packages/contracts/schema-lifecycle.mjs +83 -153
- package/packages/contracts/selected-file-limits.mjs +6 -0
- package/packages/contracts/vulnerability-identifiers.mjs +5 -0
- package/packages/contracts/web-capture.mjs +10 -0
- package/packages/contracts/workspace-portability.mjs +219 -238
- package/packages/evidence/artifact-integrity.mjs +8 -2
- package/packages/evidence/artifact-structure.mjs +105 -0
- package/packages/evidence/collection-quality.mjs +67 -0
- package/packages/evidence/decision-fact.mjs +4 -1
- package/packages/evidence/dns-query-outcome.mjs +72 -0
- package/packages/evidence/domain-control-runtime.mjs +155 -27
- package/packages/evidence/domain-posture-context.mjs +79 -0
- package/packages/evidence/email-recipient.mjs +18 -0
- package/packages/evidence/image-regions.mjs +56 -0
- package/packages/evidence/latest-observations.mjs +28 -0
- package/packages/evidence/lookup-target.mjs +108 -0
- package/packages/evidence/observation.mjs +18 -1
- package/packages/evidence/passphrase-encryption.mjs +44 -0
- package/packages/evidence/png-pixels.mjs +117 -0
- package/packages/evidence/public-address-policy.mjs +83 -0
- package/packages/evidence/record-identity.mjs +72 -0
- package/packages/evidence/registry-dates.mjs +190 -0
- package/packages/evidence/retained-file.mjs +69 -0
- package/packages/evidence/review-age.mjs +14 -0
- package/packages/interchange/bagit.mjs +342 -0
- package/packages/interchange/bounded-zip-extraction.mjs +278 -0
- package/packages/interchange/crc32.mjs +17 -0
- package/packages/interchange/defensive-indicator-export.mjs +264 -0
- package/packages/interchange/external-findings-import.mjs +93 -61
- package/packages/interchange/managed-indicator-export.mjs +35 -0
- package/packages/interchange/managed-indicator-set.mjs +210 -0
- package/packages/interchange/misp-indicator-export.mjs +93 -0
- package/packages/interchange/sharing-policy.mjs +56 -0
- package/packages/interchange/stix-indicator-export.mjs +129 -0
- package/packages/interchange/web-capture-import.mjs +389 -0
- package/packages/investigation/acquisition-decision-packet.mjs +1 -1
- package/packages/investigation/bulk-mail-exposure.mjs +9 -14
- package/packages/investigation/capture-coverage.mjs +57 -0
- package/packages/investigation/connector-provenance-review.mjs +100 -0
- package/packages/investigation/context-review.mjs +35 -0
- package/packages/investigation/document-intake.mjs +81 -0
- package/packages/investigation/docx-intake.mjs +133 -0
- package/packages/investigation/domain-history-review.mjs +100 -0
- package/packages/investigation/domain-transition-review.mjs +47 -0
- package/packages/investigation/evidence-coverage-ledger.mjs +9 -1
- package/packages/investigation/har-intake.mjs +68 -0
- package/packages/investigation/identity-event-intake.mjs +131 -0
- package/packages/investigation/identity-incident-review.mjs +39 -0
- package/packages/investigation/incident-sequence-review.mjs +58 -0
- package/packages/investigation/intake-report.mjs +19 -0
- package/packages/investigation/investigation-bagit.mjs +14 -0
- package/packages/investigation/investigation-capsule.mjs +33 -5
- package/packages/investigation/investigation-manifest.mjs +200 -0
- package/packages/investigation/investigation-package-crypto.mjs +98 -0
- package/packages/investigation/investigation-package.mjs +200 -0
- package/packages/investigation/link-intake.mjs +179 -0
- package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
- package/packages/investigation/lookup-asset-graph.mjs +161 -141
- package/packages/investigation/lookup-claim-readiness.mjs +18 -0
- package/packages/investigation/lookup-investigation-brief.mjs +3 -3
- package/packages/investigation/lookup-task-guidance.mjs +58 -0
- package/packages/investigation/mail-authentication-review.mjs +222 -0
- package/packages/investigation/mail-header-identity.mjs +209 -0
- package/packages/investigation/message-intake.mjs +195 -0
- package/packages/investigation/page-behaviour.mjs +113 -0
- package/packages/investigation/pdf-intake.mjs +155 -0
- package/packages/investigation/platform-continuity-review.mjs +60 -0
- package/packages/investigation/qr-intake.mjs +56 -0
- package/packages/investigation/requested-action-hints.mjs +11 -0
- package/packages/investigation/selected-input-review.mjs +17 -0
- package/packages/investigation/storefront-review.mjs +70 -0
- package/packages/monitoring/analyst-review-state.mjs +38 -90
- package/packages/workspace/brand-profile-model.mjs +160 -22
- package/packages/workspace/bulk-columns.mjs +17 -0
- package/packages/workspace/bulk-review-model.mjs +39 -31
- package/packages/workspace/bulk-session-model.mjs +112 -27
- package/packages/workspace/bulk-sort.mjs +1 -0
- package/packages/workspace/campaign-model.mjs +1 -1
- package/packages/workspace/case-views.mjs +102 -0
- package/packages/workspace/detection-rule-model.mjs +1 -1
- package/packages/workspace/hostile-input.mjs +29 -8
- package/packages/workspace/investigation-guide.mjs +2 -2
- package/packages/workspace/investigation-template-model.mjs +45 -6
- package/packages/workspace/page-baseline.mjs +20 -17
- package/packages/workspace/relationship-observation-model.mjs +54 -35
- package/packages/workspace/shortlist-model.mjs +14 -5
- package/packages/workspace/watchlist-history.mjs +30 -14
- package/packages/workspace/watchlist-store.mjs +24 -6
- package/packages/workspace/website-snapshot-model.mjs +98 -33
- package/packages/workspace/workspace-archive-crypto.mjs +41 -61
- package/packages/workspace/workspace-archive.mjs +90 -25
- package/third-party-notices.txt +577 -17
- package/lib/hostname.mjs +0 -38
- package/packages/investigation/bulk-review-export.mjs +0 -63
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DOMAIN_CONTROL_MANIFEST_COMPATIBILITY } from "../packages/contracts/domain-control-manifest.mjs";
|
|
2
|
+
import { MANAGED_INDICATOR_SET_COMPATIBILITY } from "../packages/contracts/analyst-interchange.mjs";
|
|
2
3
|
import { CLI_CASE_PACK_COMPATIBILITY, ENCRYPTED_WORKSPACE_ARCHIVE_COMPATIBILITY, WORKSPACE_ARCHIVE_COMPATIBILITY, } from "../packages/contracts/case-portability.mjs";
|
|
3
4
|
import { LOOKUP_CLAIM_PASSPORT_COMPATIBILITY } from "../packages/contracts/investigation-portability.mjs";
|
|
4
5
|
import { BRAND_PROFILE_EXPORT_COMPATIBILITY } from "../packages/contracts/workspace-portability.mjs";
|
|
@@ -16,6 +17,15 @@ function compatibilityReference(descriptor) {
|
|
|
16
17
|
});
|
|
17
18
|
}
|
|
18
19
|
export const INTERCHANGE_ARTIFACT_CONTRACTS = Object.freeze([
|
|
20
|
+
Object.freeze({
|
|
21
|
+
id: 'managed_indicators', ...compatibilityReference(MANAGED_INDICATOR_SET_COMPATIBILITY),
|
|
22
|
+
versionField: 'version', nestedSchemaPath: Object.freeze([]),
|
|
23
|
+
browser: Object.freeze({ import: 'supported', export: 'supported' }),
|
|
24
|
+
cli: Object.freeze({ read: 'supported', write: 'supported', verify: 'supported' }),
|
|
25
|
+
fidelity: 'semantic_exact_after_normalisation', requiredAssurance: 'whole_integrity',
|
|
26
|
+
preservedFieldGroups: Object.freeze(['stable_indicator_identities', 'original_observations', 'review_windows', 'withdrawal_records', 'preceding_revision_digest']),
|
|
27
|
+
excludedFieldGroups: Object.freeze(['raw_responses', 'browser_local_records', 'credentials', 'remote_publication', 'author_authentication', 'unavailable_revision_history']),
|
|
28
|
+
}),
|
|
19
29
|
Object.freeze({
|
|
20
30
|
id: 'lookup_claim_passport',
|
|
21
31
|
...compatibilityReference(LOOKUP_CLAIM_PASSPORT_COMPATIBILITY),
|
package/lib/lookup-cache.mjs
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// In-memory, so (like the bootstrap cache and the rate limiter) this
|
|
11
11
|
// applies globally on server.mts's one long-lived process, but only within
|
|
12
12
|
// a single warm container on Netlify Functions.
|
|
13
|
+
import { abortable } from "./abort.mjs";
|
|
13
14
|
const TTL_MS = 3 * 60 * 1000; // 3 minutes
|
|
14
15
|
const SWEEP_INTERVAL_MS = 5 * 60 * 1000;
|
|
15
16
|
// Bounds worst-case memory regardless of the TTL/sweep - without this, a
|
|
@@ -72,7 +73,7 @@ function getCached(key) {
|
|
|
72
73
|
deleteEntry(key);
|
|
73
74
|
return undefined;
|
|
74
75
|
}
|
|
75
|
-
return entry.value;
|
|
76
|
+
return structuredClone(entry.value);
|
|
76
77
|
}
|
|
77
78
|
function setCached(key, value) {
|
|
78
79
|
sweepExpiredEntries(Date.now());
|
|
@@ -80,9 +81,16 @@ function setCached(key, value) {
|
|
|
80
81
|
const size = approxByteSize(value);
|
|
81
82
|
// A value whose serialized size cannot be measured cannot participate in a
|
|
82
83
|
// byte-bounded cache. Return it to the current caller without retaining it.
|
|
83
|
-
if (size === null)
|
|
84
|
-
return;
|
|
85
|
-
|
|
84
|
+
if (size === null || size > MAX_TOTAL_BYTES)
|
|
85
|
+
return { value, owned: false };
|
|
86
|
+
let snapshot;
|
|
87
|
+
try {
|
|
88
|
+
snapshot = structuredClone(value);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return { value, owned: false };
|
|
92
|
+
}
|
|
93
|
+
store.set(key, { value: snapshot, expiresAt: Date.now() + TTL_MS, size });
|
|
86
94
|
totalBytes += size;
|
|
87
95
|
while (store.size > 0 && (store.size > MAX_ENTRIES || totalBytes > MAX_TOTAL_BYTES)) {
|
|
88
96
|
const oldestKey = store.keys().next().value;
|
|
@@ -90,6 +98,12 @@ function setCached(key, value) {
|
|
|
90
98
|
break;
|
|
91
99
|
deleteEntry(oldestKey);
|
|
92
100
|
}
|
|
101
|
+
return { value: snapshot, owned: true };
|
|
102
|
+
}
|
|
103
|
+
// Stored data and coalesced results belong to the cache, not to a caller. Each
|
|
104
|
+
// consumer receives its own copy, including the first and concurrent callers.
|
|
105
|
+
function callerResult(result) {
|
|
106
|
+
return result.owned ? structuredClone(result.value) : result.value;
|
|
93
107
|
}
|
|
94
108
|
// Concurrent callers for the same key (e.g. a bulk scan's concurrent
|
|
95
109
|
// workers, or a fast scan immediately followed by a deep-check, hitting the
|
|
@@ -103,25 +117,34 @@ const inFlight = new Map();
|
|
|
103
117
|
// The cache intentionally holds heterogeneous public lookup results. Callers
|
|
104
118
|
// supply distinct result contracts, so this internal compatibility boundary
|
|
105
119
|
// stays dynamically typed rather than asserting one shared payload shape.
|
|
106
|
-
async function cached(key, factory) {
|
|
120
|
+
async function cached(key, factory, signal) {
|
|
121
|
+
signal?.throwIfAborted();
|
|
107
122
|
const hit = getCached(key);
|
|
108
123
|
if (hit !== undefined)
|
|
109
124
|
return hit;
|
|
125
|
+
// A deadline-scoped caller may reuse completed public data, but must own
|
|
126
|
+
// its cancellable work rather than aborting another caller's shared request.
|
|
127
|
+
if (signal) {
|
|
128
|
+
const value = await abortable(factory, signal);
|
|
129
|
+
signal.throwIfAborted();
|
|
130
|
+
return callerResult(setCached(key, value));
|
|
131
|
+
}
|
|
110
132
|
const pending = inFlight.get(key);
|
|
111
133
|
if (pending)
|
|
112
|
-
return pending;
|
|
113
|
-
|
|
134
|
+
return callerResult(await pending);
|
|
135
|
+
// Register pending work before invoking the factory, including factories
|
|
136
|
+
// that throw synchronously. A rejected promise must never poison the key.
|
|
137
|
+
const promise = Promise.resolve().then(async () => {
|
|
114
138
|
try {
|
|
115
139
|
const value = await factory();
|
|
116
|
-
setCached(key, value);
|
|
117
|
-
return value;
|
|
140
|
+
return setCached(key, value);
|
|
118
141
|
}
|
|
119
142
|
finally {
|
|
120
143
|
inFlight.delete(key);
|
|
121
144
|
}
|
|
122
|
-
})
|
|
145
|
+
});
|
|
123
146
|
inFlight.set(key, promise);
|
|
124
|
-
return promise;
|
|
147
|
+
return callerResult(await promise);
|
|
125
148
|
}
|
|
126
149
|
// Expired entries are swept lazily during cache activity. This preserves the
|
|
127
150
|
// long-running Express cleanup behavior without creating an import-time timer,
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// Browser-safe version and collection boundaries shared by Lookup child
|
|
2
2
|
// profile producers and the client response trust boundary.
|
|
3
3
|
import { MAX_OBSERVATION_LIMITATIONS, MAX_OBSERVATION_LIMITATION_LENGTH, OBSERVATION_VERSION, normalizeExplicitIsoTimestamp, } from "../packages/evidence/observation.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { PAGE_FINGERPRINT_PARSERS, PAGE_IDENTITY_VERSION, } from "../packages/contracts/workspace-portability.mjs";
|
|
5
5
|
import { validPagePublicationMetadata } from "./homepage-metadata-contract.mjs";
|
|
6
|
+
import { PAGE_FINGERPRINT_TOKEN_LIMITS } from "../packages/contracts/page-fingerprints.mjs";
|
|
6
7
|
import { MAX_LOOKUP_TLS_ALT_NAMES, MAX_LOOKUP_TLS_CERTIFICATE_POLICIES, MAX_LOOKUP_TLS_CHAIN_CERTIFICATES, MAX_LOOKUP_TLS_FINDINGS, MAX_LOOKUP_TLS_NAME_VALUES, TLS_PROFILE_VERSION, } from "./lookup-network-evidence-bounds.mjs";
|
|
7
8
|
import { isJsonObject, normalizedDomain, validBoundedString, validHttpProvenanceUrl, validObservationDiagnostics, validOptionalNullableText, validStringArray, validUint, } from "./lookup-contract-primitives.mjs";
|
|
8
9
|
import { TECHNOLOGY_EVIDENCE_ROLE_ORDER } from "./technology-evidence-role.mjs";
|
|
9
|
-
|
|
10
|
-
export const
|
|
10
|
+
import { MAX_HOMEPAGE_BYTES } from "./outbound-request-bounds.mjs";
|
|
11
|
+
export const TECHNOLOGY_PROFILE_VERSION = 12;
|
|
12
|
+
export const SUPPORTED_TECHNOLOGY_PROFILE_VERSIONS = Object.freeze([10, 11, TECHNOLOGY_PROFILE_VERSION]);
|
|
11
13
|
export const MAX_TECHNOLOGY_FINDINGS = 24;
|
|
12
14
|
export const MAX_EVIDENCE_PER_TECHNOLOGY = 4;
|
|
13
15
|
export const MAX_TECHNOLOGY_EVIDENCE_DESCRIPTION_LENGTH = 180;
|
|
14
16
|
export const BROWSER_LIBRARY_PROFILE_VERSION = 2;
|
|
15
17
|
export const MAX_LIBRARY_FINDINGS = 16;
|
|
16
|
-
export const WEBSITE_SECURITY_POSTURE_VERSION =
|
|
18
|
+
export const WEBSITE_SECURITY_POSTURE_VERSION = 3;
|
|
17
19
|
export const MAX_SECURITY_POSTURE_FINDINGS = 32;
|
|
18
20
|
export const CREDENTIAL_SURFACE_PROFILE_VERSION = 1;
|
|
19
21
|
export const STRUCTURED_DATA_IDENTITY_VERSION = 1;
|
|
@@ -169,6 +171,7 @@ function validFingerprintIdentifier(value) {
|
|
|
169
171
|
&& /^[A-Z0-9-]{1,64}$/u.test(value.value);
|
|
170
172
|
}
|
|
171
173
|
function validPageFingerprintProfile(value) {
|
|
174
|
+
const tokenLimit = PAGE_FINGERPRINT_TOKEN_LIMITS[value.fingerprintVersion];
|
|
172
175
|
const normalizedHtml = value.normalizedHtml;
|
|
173
176
|
const visibleText = value.visibleText;
|
|
174
177
|
const domStructure = value.domStructure;
|
|
@@ -181,7 +184,7 @@ function validPageFingerprintProfile(value) {
|
|
|
181
184
|
|| normalizedHtml.algorithm !== 'sha256'
|
|
182
185
|
|| typeof normalizedHtml.value !== 'string'
|
|
183
186
|
|| !SHA256_RE.test(normalizedHtml.value)
|
|
184
|
-
|| !validUint(normalizedHtml.tokenCount,
|
|
187
|
+
|| !validUint(normalizedHtml.tokenCount, tokenLimit)
|
|
185
188
|
|| typeof normalizedHtml.truncated !== 'boolean'
|
|
186
189
|
|| !(visibleText === null || isJsonObject(visibleText)
|
|
187
190
|
&& hasExactKeys(visibleText, ['algorithm', 'value', 'tokenCount', 'featureCount', 'truncated'])
|
|
@@ -197,16 +200,16 @@ function validPageFingerprintProfile(value) {
|
|
|
197
200
|
|| domStructure.algorithm !== 'sha256'
|
|
198
201
|
|| typeof domStructure.value !== 'string'
|
|
199
202
|
|| !SHA256_RE.test(domStructure.value)
|
|
200
|
-
|| !validUint(domStructure.nodeCount,
|
|
201
|
-
|| domStructure.parser !==
|
|
203
|
+
|| !validUint(domStructure.nodeCount, tokenLimit)
|
|
204
|
+
|| domStructure.parser !== PAGE_FINGERPRINT_PARSERS[value.fingerprintVersion]
|
|
202
205
|
|| typeof domStructure.truncated !== 'boolean'
|
|
203
206
|
|| !(domStructure.similarity === undefined || domStructure.similarity === null || isJsonObject(domStructure.similarity)
|
|
204
207
|
&& hasExactKeys(domStructure.similarity, ['algorithm', 'value', 'tokenCount', 'featureCount', 'truncated'])
|
|
205
208
|
&& domStructure.similarity.algorithm === 'simhash64-v1'
|
|
206
209
|
&& typeof domStructure.similarity.value === 'string'
|
|
207
210
|
&& SIMHASH64_RE.test(domStructure.similarity.value)
|
|
208
|
-
&& validUint(domStructure.similarity.tokenCount,
|
|
209
|
-
&& validUint(domStructure.similarity.featureCount,
|
|
211
|
+
&& validUint(domStructure.similarity.tokenCount, tokenLimit)
|
|
212
|
+
&& validUint(domStructure.similarity.featureCount, tokenLimit)
|
|
210
213
|
&& typeof domStructure.similarity.truncated === 'boolean')
|
|
211
214
|
|| !(formStructure === null || isJsonObject(formStructure)
|
|
212
215
|
&& hasExactKeys(formStructure, ['algorithm', 'value', 'formCount', 'controlCount', 'truncated'])
|
|
@@ -461,7 +464,7 @@ function browserLibraryProfileContractState(value) {
|
|
|
461
464
|
|| !isJsonObject(knownExploitedCatalog)
|
|
462
465
|
|| !hasOnlyKeys(knownExploitedCatalog, ['name', 'version', 'releasedAt'])
|
|
463
466
|
|| !['name', 'version', 'releasedAt'].every((field) => validBoundedString(knownExploitedCatalog[field], 160))
|
|
464
|
-
|| normalizeExplicitIsoTimestamp(knownExploitedCatalog.releasedAt)
|
|
467
|
+
|| normalizeExplicitIsoTimestamp(knownExploitedCatalog.releasedAt) === null
|
|
465
468
|
|| !Array.isArray(profile.findings)
|
|
466
469
|
|| profile.findings.length > MAX_LIBRARY_FINDINGS)
|
|
467
470
|
return 'invalid';
|
|
@@ -488,6 +491,8 @@ function browserLibraryProfileContractState(value) {
|
|
|
488
491
|
|| !validExactStringArray(candidate.knownExploitedIdentifiers, 16, 80)
|
|
489
492
|
|| !validExactStringArray(candidate.weaknessClasses, 12, 80))
|
|
490
493
|
return 'invalid';
|
|
494
|
+
// Published profiles could retain legacy upstream identifier spellings.
|
|
495
|
+
// Keep that reader boundary; current catalogue writers validate syntax.
|
|
491
496
|
if (findingIds.has(candidate.id)
|
|
492
497
|
|| !candidate.advisoryIdentifiers.every((identifier) => typeof identifier === 'string'
|
|
493
498
|
&& /^(?:CVE-[0-9X-]+|GHSA-[A-Z0-9-]+)$/u.test(identifier))
|
|
@@ -495,7 +500,9 @@ function browserLibraryProfileContractState(value) {
|
|
|
495
500
|
&& /^CVE-[0-9X-]+$/u.test(identifier))
|
|
496
501
|
|| !candidate.weaknessClasses.every((identifier) => typeof identifier === 'string'
|
|
497
502
|
&& /^CWE-[0-9]+$/u.test(identifier))
|
|
498
|
-
|
|
503
|
+
// One advisory can have several identifiers, including CVE and GHSA
|
|
504
|
+
// aliases. Their count is not the number of matching advisory records.
|
|
505
|
+
|| (Number(candidate.advisoryCount) === 0 && candidate.advisoryIdentifiers.length > 0)
|
|
499
506
|
|| Number(candidate.knownExploitedCount) < candidate.knownExploitedIdentifiers.length)
|
|
500
507
|
return 'invalid';
|
|
501
508
|
findingIds.add(candidate.id);
|
|
@@ -503,7 +510,7 @@ function browserLibraryProfileContractState(value) {
|
|
|
503
510
|
return 'supported';
|
|
504
511
|
}
|
|
505
512
|
function pageFingerprintContractState(value) {
|
|
506
|
-
const versionState = childVersionState(value, 'fingerprintVersion',
|
|
513
|
+
const versionState = childVersionState(value, 'fingerprintVersion', Object.keys(PAGE_FINGERPRINT_PARSERS).map(Number));
|
|
507
514
|
if (versionState !== 'supported')
|
|
508
515
|
return versionState;
|
|
509
516
|
const profile = value;
|
|
@@ -520,7 +527,9 @@ function pageFingerprintContractState(value) {
|
|
|
520
527
|
|| typeof profile.exact.value !== 'string'
|
|
521
528
|
|| !SHA256_RE.test(profile.exact.value)
|
|
522
529
|
|| !['complete-body', 'captured-prefix'].includes(String(profile.exact.scope))
|
|
523
|
-
|
|
530
|
+
// The published first-generation writer had a fixed 300,000-byte ceiling.
|
|
531
|
+
// Current native fingerprints share the collector's source-size policy.
|
|
532
|
+
|| !validUint(profile.exact.bytes, profile.fingerprintVersion === 1 ? 300_000 : MAX_HOMEPAGE_BYTES)
|
|
524
533
|
|| !['captured-response-bytes', 'decoded-markup'].includes(String(profile.exact.source))
|
|
525
534
|
|| !validPageFingerprintProfile(profile)
|
|
526
535
|
|| profile.complete !== (profile.truncated !== true))
|
|
@@ -647,7 +656,7 @@ function tlsProfileContractState(value) {
|
|
|
647
656
|
return 'supported';
|
|
648
657
|
}
|
|
649
658
|
function securityPostureContractState(value) {
|
|
650
|
-
const versionState = childVersionState(value, 'postureVersion', [WEBSITE_SECURITY_POSTURE_VERSION]);
|
|
659
|
+
const versionState = childVersionState(value, 'postureVersion', [2, WEBSITE_SECURITY_POSTURE_VERSION]);
|
|
651
660
|
if (versionState !== 'supported')
|
|
652
661
|
return versionState;
|
|
653
662
|
const profile = value;
|
|
@@ -855,4 +864,4 @@ function sanitizeLookupChildProfiles(value) {
|
|
|
855
864
|
}
|
|
856
865
|
return output;
|
|
857
866
|
}
|
|
858
|
-
export { sanitizeLookupChildProfiles, validSecurityPostureFinding, validTlsChainCertificate, validTlsFinding, };
|
|
867
|
+
export { sanitizeLookupChildProfiles, technologyProfileContractState, validSecurityPostureFinding, validTlsChainCertificate, validTlsFinding, };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// child-profile contracts. These checks validate without normalising or
|
|
3
3
|
// copying untrusted values.
|
|
4
4
|
import { MAX_OBSERVATION_DIAGNOSTICS } from "../packages/evidence/observation.mjs";
|
|
5
|
-
import { MAX_HTTP_PROVENANCE_URL } from "
|
|
5
|
+
import { MAX_HTTP_PROVENANCE_URL } from "../packages/analysis/http-evidence-bounds.mjs";
|
|
6
6
|
const LOOKUP_CONTROL_CHAR_RE = /[\u0000-\u001f\u007f-\u009f]|\p{Default_Ignorable_Code_Point}/u;
|
|
7
7
|
const MAX_LOOKUP_CONTRACT_HOST_LENGTH = 253;
|
|
8
8
|
function isJsonObject(value) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Lookup diagnostics, bounded source errors and optional deep-lookup timing.
|
|
2
2
|
import { OPERATION_BUDGET_ERROR_CODE } from "./operation-budget.mjs";
|
|
3
3
|
import { FEATURE_DISABLED_ERROR_CODE } from "./feature-policy.mjs";
|
|
4
|
+
import { LOOKUP_SOURCE_STATES, lookupDiagnosticStates } from "./lookup-progress.mjs";
|
|
4
5
|
const LOOKUP_DIAGNOSTICS_VERSION = 8;
|
|
5
6
|
const LOOKUP_LEGACY_DIAGNOSTICS_VERSION = 7;
|
|
6
7
|
const LOOKUP_TIMING_VERSION = 1;
|
|
@@ -93,4 +94,4 @@ function createLookupTimingTracker(enabled, now) {
|
|
|
93
94
|
},
|
|
94
95
|
};
|
|
95
96
|
}
|
|
96
|
-
export { LOOKUP_DIAGNOSTICS_VERSION, LOOKUP_LEGACY_DIAGNOSTICS_VERSION, LOOKUP_TIMING_VERSION, MAX_LOOKUP_TIMING_MS, LOOKUP_ERROR_CODES, boundedSourceDetail, createLookupTimingTracker, errorMessage, };
|
|
97
|
+
export { LOOKUP_DIAGNOSTICS_VERSION, LOOKUP_LEGACY_DIAGNOSTICS_VERSION, LOOKUP_SOURCE_STATES, LOOKUP_TIMING_VERSION, MAX_LOOKUP_TIMING_MS, LOOKUP_ERROR_CODES, boundedSourceDetail, createLookupTimingTracker, errorMessage, lookupDiagnosticStates, };
|
|
@@ -1,10 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// the Lookup response trust boundary, and the display projection.
|
|
3
|
-
export const MAX_LOOKUP_DNS_RECORDS_PER_TYPE = 16;
|
|
4
|
-
export const MAX_LOOKUP_REVERSE_DNS_PTR_RECORDS = 8;
|
|
5
|
-
export const TLS_PROFILE_VERSION = 3;
|
|
6
|
-
export const MAX_LOOKUP_TLS_CHAIN_CERTIFICATES = 8;
|
|
7
|
-
export const MAX_LOOKUP_TLS_ALT_NAMES = 50;
|
|
8
|
-
export const MAX_LOOKUP_TLS_NAME_VALUES = 4;
|
|
9
|
-
export const MAX_LOOKUP_TLS_FINDINGS = 5;
|
|
10
|
-
export const MAX_LOOKUP_TLS_CERTIFICATE_POLICIES = 16;
|
|
1
|
+
export * from "../packages/analysis/lookup-network-evidence-bounds.mjs";
|
package/lib/lookup-progress.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Portable bounded NDJSON Lookup progress contract. Partial events are
|
|
2
2
|
// presentation-only; only a validated final ordinary Lookup result may cross
|
|
3
3
|
// the persistence boundary.
|
|
4
|
+
import { scanBoundedJson, MAX_BOUNDED_JSON_DEPTH, MAX_BOUNDED_JSON_KEYS, MAX_BOUNDED_JSON_VALUES } from "../packages/analysis/bounded-json.mjs";
|
|
4
5
|
export const LOOKUP_PROGRESS_SCHEMA = 'whoisleuth.lookup-progress';
|
|
5
6
|
export const LOOKUP_PROGRESS_VERSION = 1;
|
|
6
7
|
export const MAX_LOOKUP_PROGRESS_SOURCES = 16;
|
|
@@ -31,7 +32,56 @@ const SOURCE_STATES = new Set([
|
|
|
31
32
|
'unavailable',
|
|
32
33
|
'rate_limited',
|
|
33
34
|
]);
|
|
35
|
+
// Final diagnostic states are distinct from the provisional progress subset.
|
|
36
|
+
export const LOOKUP_SOURCE_STATES = Object.freeze([
|
|
37
|
+
'success', 'partial', 'not_found', 'skipped', 'error', 'unsupported',
|
|
38
|
+
'not_applicable', 'unavailable', 'rate_limited', 'complete', 'disabled', 'stale',
|
|
39
|
+
]);
|
|
40
|
+
const FINAL_SOURCE_STATES = new Set(LOOKUP_SOURCE_STATES);
|
|
41
|
+
export function lookupDiagnosticStates(value) {
|
|
42
|
+
const record = (input) => input && typeof input === 'object' && !Array.isArray(input)
|
|
43
|
+
? input : {};
|
|
44
|
+
const diagnostics = record(value);
|
|
45
|
+
const candidates = {
|
|
46
|
+
rdap: record(diagnostics.rdap).status,
|
|
47
|
+
registrar_rdap: record(record(diagnostics.rdap).registrar).status,
|
|
48
|
+
whois: record(diagnostics.whois).status,
|
|
49
|
+
availability: record(diagnostics.availability).status,
|
|
50
|
+
reverse_dns: record(diagnostics.reverseDns).status,
|
|
51
|
+
network_context: record(diagnostics.network).status,
|
|
52
|
+
security_txt: record(diagnostics.securityTxt).status,
|
|
53
|
+
sslbl: record(diagnostics.sslbl).status,
|
|
54
|
+
};
|
|
55
|
+
return Object.freeze(Object.fromEntries(Object.entries(candidates).map(([source, state]) => [
|
|
56
|
+
source, typeof state === 'string' && FINAL_SOURCE_STATES.has(state) ? state : null,
|
|
57
|
+
])));
|
|
58
|
+
}
|
|
34
59
|
const SOURCE_ID_RE = /^[a-z][a-z0-9_]{0,39}$/u;
|
|
60
|
+
/** The existing Lookup recipe, shared by offline plans and source progress.
|
|
61
|
+
* This describes admitted families, not provider health or request counts. */
|
|
62
|
+
export function plannedLookupSources(targetType, mode, options = {}) {
|
|
63
|
+
const sources = ['rdap'];
|
|
64
|
+
if (mode === 'fast') {
|
|
65
|
+
if (targetType === 'domain')
|
|
66
|
+
sources.push('domain_evidence');
|
|
67
|
+
return Object.freeze(sources);
|
|
68
|
+
}
|
|
69
|
+
sources.push('whois');
|
|
70
|
+
if (targetType === 'domain') {
|
|
71
|
+
sources.push('domain_evidence', 'registrar_rdap', 'network_context');
|
|
72
|
+
if (options.securityTxt)
|
|
73
|
+
sources.push('security_txt');
|
|
74
|
+
if (options.externalIntelligence)
|
|
75
|
+
sources.push('external_intelligence');
|
|
76
|
+
if (options.malwareHostIntelligence)
|
|
77
|
+
sources.push('malware_host_intelligence');
|
|
78
|
+
if (options.malwareIocIntelligence)
|
|
79
|
+
sources.push('malware_ioc_intelligence');
|
|
80
|
+
}
|
|
81
|
+
else if (targetType === 'ipv4' || targetType === 'ipv6')
|
|
82
|
+
sources.push('reverse_dns');
|
|
83
|
+
return Object.freeze(sources);
|
|
84
|
+
}
|
|
35
85
|
const TEXT_ENCODER = new TextEncoder();
|
|
36
86
|
function byteLength(value) {
|
|
37
87
|
return TEXT_ENCODER.encode(value).byteLength;
|
|
@@ -241,20 +291,25 @@ export function createLookupProgressNdjsonDecoder(onEvent) {
|
|
|
241
291
|
throw new TypeError('NDJSON decoder requires an event consumer.');
|
|
242
292
|
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
243
293
|
let pending = '';
|
|
294
|
+
let pendingBytes = 0;
|
|
244
295
|
let totalBytes = 0;
|
|
245
296
|
let eventCount = 0;
|
|
246
297
|
function consumeLine(line) {
|
|
247
298
|
if (!line)
|
|
248
299
|
return;
|
|
249
|
-
if (byteLength(line) > MAX_LOOKUP_PROGRESS_FINAL_BYTES + MAX_LOOKUP_PROGRESS_FRAGMENT_BYTES) {
|
|
250
|
-
throw new TypeError('Lookup progress line exceeds its byte bound.');
|
|
251
|
-
}
|
|
252
300
|
eventCount += 1;
|
|
253
301
|
if (eventCount > MAX_LOOKUP_PROGRESS_EVENTS) {
|
|
254
302
|
throw new TypeError('Lookup progress stream contains too many events.');
|
|
255
303
|
}
|
|
256
304
|
let parsed;
|
|
257
305
|
try {
|
|
306
|
+
// The ordinary result keeps its original limits after decoding. Allow
|
|
307
|
+
// only the fixed progress envelope overhead around that same document.
|
|
308
|
+
scanBoundedJson(line, {
|
|
309
|
+
maximumDepth: MAX_BOUNDED_JSON_DEPTH + 1,
|
|
310
|
+
maximumKeys: MAX_BOUNDED_JSON_KEYS + 8,
|
|
311
|
+
maximumValues: MAX_BOUNDED_JSON_VALUES + MAX_LOOKUP_PROGRESS_SOURCES + 16,
|
|
312
|
+
});
|
|
258
313
|
parsed = JSON.parse(line);
|
|
259
314
|
}
|
|
260
315
|
catch {
|
|
@@ -269,16 +324,26 @@ export function createLookupProgressNdjsonDecoder(onEvent) {
|
|
|
269
324
|
if (totalBytes > MAX_LOOKUP_PROGRESS_STREAM_BYTES) {
|
|
270
325
|
throw new TypeError('Lookup progress stream exceeds its total byte bound.');
|
|
271
326
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
327
|
+
// Count incoming bytes once, before decoding or accumulation. LF cannot
|
|
328
|
+
// occur inside a valid UTF-8 sequence; the fatal decoder still receives
|
|
329
|
+
// every byte, including framing, so chunk boundaries cannot hide errors.
|
|
330
|
+
for (let start = 0; start < chunk.length;) {
|
|
331
|
+
const newline = chunk.indexOf(10, start);
|
|
332
|
+
const end = newline < 0 ? chunk.length : newline;
|
|
333
|
+
pendingBytes += end - start;
|
|
334
|
+
const maximum = MAX_LOOKUP_PROGRESS_FINAL_BYTES + MAX_LOOKUP_PROGRESS_FRAGMENT_BYTES;
|
|
335
|
+
const trailingCr = end > start ? chunk[end - 1] === 13 : pending.endsWith('\r');
|
|
336
|
+
if (pendingBytes > maximum + (trailingCr ? 1 : 0)) {
|
|
337
|
+
throw new TypeError('Lookup progress line exceeds its byte bound.');
|
|
338
|
+
}
|
|
339
|
+
const decoded = decoder.decode(chunk.subarray(start, end + (newline < 0 ? 0 : 1)), { stream: true });
|
|
340
|
+
pending += newline < 0 ? decoded : decoded.slice(0, -1);
|
|
341
|
+
if (newline >= 0) {
|
|
342
|
+
consumeLine(pending.replace(/\r$/u, ''));
|
|
343
|
+
pending = '';
|
|
344
|
+
pendingBytes = 0;
|
|
345
|
+
}
|
|
346
|
+
start = end + 1;
|
|
282
347
|
}
|
|
283
348
|
}
|
|
284
349
|
function finish() {
|
|
@@ -286,6 +351,7 @@ export function createLookupProgressNdjsonDecoder(onEvent) {
|
|
|
286
351
|
if (pending)
|
|
287
352
|
consumeLine(pending.replace(/\r$/u, ''));
|
|
288
353
|
pending = '';
|
|
354
|
+
pendingBytes = 0;
|
|
289
355
|
}
|
|
290
356
|
return Object.freeze({ push, finish });
|
|
291
357
|
}
|