@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
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
import { compareRegistrySources } from "./registry-comparison.mjs";
|
|
8
8
|
import { inspectRdapCapabilities } from "./rdap-capabilities.mjs";
|
|
9
9
|
import { isRecord, recordOrEmpty } from "./json-record.mjs";
|
|
10
|
+
import { MAX_RDAP_REDACTIONS, MAX_RDAP_STATUSES } from "./rdap-types.mjs";
|
|
11
|
+
import { MAX_WHOIS_STATUSES } from "./whois-contracts.mjs";
|
|
10
12
|
export const REGISTRY_INSIGHTS_VERSION = 1;
|
|
11
13
|
export const MAX_REGISTRY_INSIGHT_STATUSES = 40;
|
|
12
14
|
export const MAX_REGISTRY_INSIGHT_ISSUES = 12;
|
|
@@ -37,6 +39,20 @@ function strings(value, maximum = MAX_REGISTRY_INSIGHT_STATUSES) {
|
|
|
37
39
|
function normalizedStatus(value) {
|
|
38
40
|
return text(value, 160).toLowerCase().replace(/[^\p{L}\p{N}]+/gu, '');
|
|
39
41
|
}
|
|
42
|
+
function statusInventory(value, maximum) {
|
|
43
|
+
if (!Array.isArray(value))
|
|
44
|
+
return { values: [], partial: value !== undefined };
|
|
45
|
+
const unique = new Set();
|
|
46
|
+
let partial = value.length > maximum;
|
|
47
|
+
for (const item of value.slice(0, maximum)) {
|
|
48
|
+
if (typeof item !== 'string' || item.length > 160 || CONTROL_RE.test(item) || !item.trim()) {
|
|
49
|
+
partial = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
unique.add(item.trim());
|
|
53
|
+
}
|
|
54
|
+
return { values: [...unique], partial };
|
|
55
|
+
}
|
|
40
56
|
function sourceState(status, partial) {
|
|
41
57
|
if (partial || status === 'partial')
|
|
42
58
|
return 'partial';
|
|
@@ -45,9 +61,13 @@ function sourceState(status, partial) {
|
|
|
45
61
|
? 'complete'
|
|
46
62
|
: 'unavailable';
|
|
47
63
|
}
|
|
48
|
-
function
|
|
49
|
-
const redactions = Array.isArray(parsed.redactions) ? parsed.redactions.slice(0,
|
|
50
|
-
return redactions.
|
|
64
|
+
function registrantRedactionBlob(parsed) {
|
|
65
|
+
const redactions = Array.isArray(parsed.redactions) ? parsed.redactions.slice(0, MAX_RDAP_REDACTIONS) : [];
|
|
66
|
+
return redactions.filter((item) => {
|
|
67
|
+
// A path or reason can mention a role without selecting it. Do not infer
|
|
68
|
+
// registrant scope from arbitrary JSONPath expressions or other prose.
|
|
69
|
+
return /^(?:registry )?registrant(?:\b|$)/iu.test(text(record(item).name, 240));
|
|
70
|
+
}).map((item) => {
|
|
51
71
|
const value = record(item);
|
|
52
72
|
return [
|
|
53
73
|
value.name,
|
|
@@ -92,7 +112,8 @@ function disclosure(source, parsedValue, status) {
|
|
|
92
112
|
? parsed.chainStatus === 'partial' || strings(parsed.fieldsTruncated, 20).length > 0
|
|
93
113
|
: parsed.serverTruncated === true
|
|
94
114
|
|| parsed.entitiesTruncated === true
|
|
95
|
-
|| parsed.statusesTruncated === true
|
|
115
|
+
|| parsed.statusesTruncated === true
|
|
116
|
+
|| parsed.redactionsTruncated === true;
|
|
96
117
|
const publicationState = sourceState(status, partial);
|
|
97
118
|
if (publicationState === 'unavailable') {
|
|
98
119
|
return {
|
|
@@ -110,7 +131,7 @@ function disclosure(source, parsedValue, status) {
|
|
|
110
131
|
parsed.registrantPhone,
|
|
111
132
|
parsed.registrantAddress,
|
|
112
133
|
].map((part) => text(part, 320)).filter(Boolean).join(' ');
|
|
113
|
-
const redactions =
|
|
134
|
+
const redactions = registrantRedactionBlob(parsed);
|
|
114
135
|
const combined = `${registrant} ${redactions}`.trim();
|
|
115
136
|
if (WITHHELD_RE.test(combined)) {
|
|
116
137
|
return { source, state: 'withheld', detail: 'The publication indicates that registrant data was withheld or not disclosed.' };
|
|
@@ -124,6 +145,13 @@ function disclosure(source, parsedValue, status) {
|
|
|
124
145
|
if (registrant) {
|
|
125
146
|
return { source, state: 'public', detail: 'The source published at least one usable registrant contact field.' };
|
|
126
147
|
}
|
|
148
|
+
if (Array.isArray(parsed.redactions) && parsed.redactions.length > 0) {
|
|
149
|
+
return {
|
|
150
|
+
source,
|
|
151
|
+
state: 'unavailable',
|
|
152
|
+
detail: 'Redaction declarations do not establish registrant scope, so missing registrant fields remain unclassified.',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
127
155
|
if (publicationState === 'partial') {
|
|
128
156
|
return {
|
|
129
157
|
source,
|
|
@@ -137,9 +165,9 @@ function disclosure(source, parsedValue, status) {
|
|
|
137
165
|
detail: 'The source completed without a usable registrant contact or an explicit disclosure marker.',
|
|
138
166
|
};
|
|
139
167
|
}
|
|
140
|
-
function lifecycle(
|
|
141
|
-
const rawStatuses =
|
|
142
|
-
const normalized =
|
|
168
|
+
function lifecycle(statuses, evidenceState) {
|
|
169
|
+
const rawStatuses = statuses.slice(0, MAX_REGISTRY_INSIGHT_STATUSES);
|
|
170
|
+
const normalized = statuses.map(normalizedStatus);
|
|
143
171
|
const has = (value) => normalized.includes(value);
|
|
144
172
|
const observedState = (value) => has(value) ? true : evidenceState === 'complete' ? false : null;
|
|
145
173
|
const pendingDelete = observedState('pendingdelete');
|
|
@@ -200,16 +228,20 @@ function lifecycle(statusesValue, evidenceState) {
|
|
|
200
228
|
serverStatuses: serverLocks,
|
|
201
229
|
},
|
|
202
230
|
acquisitionPath,
|
|
203
|
-
limitation: 'EPP statuses describe the current registry lifecycle. They do not guarantee deletion, release timing, transfer completion, eligibility, price, or acquisition success.'
|
|
231
|
+
limitation: 'EPP statuses describe the current registry lifecycle. They do not guarantee deletion, release timing, transfer completion, eligibility, price, or acquisition success.'
|
|
232
|
+
+ (statuses.length > rawStatuses.length ? ` The display lists ${rawStatuses.length} of ${statuses.length} distinct statuses; lifecycle decisions use the full admitted inventory.` : ''),
|
|
204
233
|
};
|
|
205
234
|
}
|
|
206
|
-
function publicationDiagnostic(source, parsedValue, status, observedAt) {
|
|
235
|
+
function publicationDiagnostic(source, parsedValue, status, observedAt, statuses) {
|
|
207
236
|
const usablePublication = isRecord(parsedValue);
|
|
208
237
|
const parsed = record(parsedValue);
|
|
209
238
|
const partial = parsed.serverTruncated === true
|
|
210
239
|
|| parsed.chainStatus === 'partial'
|
|
211
240
|
|| parsed.entitiesTruncated === true
|
|
212
241
|
|| parsed.statusesTruncated === true
|
|
242
|
+
|| statuses.partial
|
|
243
|
+
|| parsed.eventsTruncated === true
|
|
244
|
+
|| parsed.redactionsTruncated === true
|
|
213
245
|
|| strings(parsed.fieldsTruncated, 20).length > 0;
|
|
214
246
|
const state = usablePublication ? sourceState(status, partial) : 'unavailable';
|
|
215
247
|
const issues = [];
|
|
@@ -219,14 +251,18 @@ function publicationDiagnostic(source, parsedValue, status, observedAt) {
|
|
|
219
251
|
issues.push('The RDAP server declared a truncated response.');
|
|
220
252
|
if (parsed.entitiesTruncated === true)
|
|
221
253
|
issues.push('The normalised entity inventory was capped.');
|
|
222
|
-
if (parsed.statusesTruncated === true)
|
|
223
|
-
issues.push('The normalised status inventory
|
|
254
|
+
if (parsed.statusesTruncated === true || statuses.partial)
|
|
255
|
+
issues.push('The normalised status inventory contains omitted or rejected values.');
|
|
256
|
+
if (parsed.eventsTruncated === true)
|
|
257
|
+
issues.push('The normalised event inventory contains omitted or rejected fields.');
|
|
258
|
+
if (parsed.redactionsTruncated === true)
|
|
259
|
+
issues.push('The normalised redaction inventory contains omitted or rejected declarations.');
|
|
224
260
|
if (strings(parsed.fieldsTruncated, 20).length)
|
|
225
261
|
issues.push('One or more normalised WHOIS fields were capped.');
|
|
226
262
|
if (parsed.chainStatus === 'partial')
|
|
227
263
|
issues.push('The WHOIS referral chain was incomplete or conflicting.');
|
|
228
264
|
const conformance = strings(parsed.conformance, 30);
|
|
229
|
-
const redactions = Array.isArray(parsed.redactions) ? parsed.redactions.slice(0,
|
|
265
|
+
const redactions = Array.isArray(parsed.redactions) ? parsed.redactions.slice(0, MAX_RDAP_REDACTIONS) : [];
|
|
230
266
|
return {
|
|
231
267
|
source,
|
|
232
268
|
state,
|
|
@@ -277,14 +313,16 @@ export function buildRegistryInsights(input) {
|
|
|
277
313
|
: counts.equivalent
|
|
278
314
|
? 'consistent'
|
|
279
315
|
: 'unavailable';
|
|
316
|
+
const rdapStatuses = statusInventory(rdap.statuses, MAX_RDAP_STATUSES);
|
|
317
|
+
const whoisStatuses = statusInventory(whois.statuses, MAX_WHOIS_STATUSES);
|
|
280
318
|
const publications = [
|
|
281
|
-
publicationDiagnostic('registry_rdap', input.rdapParsed, input.rdapStatus, input.rdapFetchedAt),
|
|
282
|
-
publicationDiagnostic('whois', input.whoisParsed, input.whoisStatus, input.whoisQueriedAt),
|
|
283
|
-
publicationDiagnostic('registrar_rdap', input.registrarRdapParsed, input.registrarRdapStatus, input.registrarRdapFetchedAt),
|
|
319
|
+
publicationDiagnostic('registry_rdap', input.rdapParsed, input.rdapStatus, input.rdapFetchedAt, rdapStatuses),
|
|
320
|
+
publicationDiagnostic('whois', input.whoisParsed, input.whoisStatus, input.whoisQueriedAt, whoisStatuses),
|
|
321
|
+
publicationDiagnostic('registrar_rdap', input.registrarRdapParsed, input.registrarRdapStatus, input.registrarRdapFetchedAt, statusInventory(registrarRdap.statuses, MAX_RDAP_STATUSES)),
|
|
284
322
|
];
|
|
285
323
|
const lifecycleCandidates = [
|
|
286
|
-
{ statuses:
|
|
287
|
-
{ statuses:
|
|
324
|
+
{ statuses: rdapStatuses.values, state: publications[0].state },
|
|
325
|
+
{ statuses: whoisStatuses.values, state: publications[1].state },
|
|
288
326
|
].filter((candidate) => candidate.state !== 'unavailable');
|
|
289
327
|
const lifecycleSource = lifecycleCandidates.find((candidate) => candidate.statuses.length)
|
|
290
328
|
?? lifecycleCandidates[0]
|
package/lib/response-policy.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Privacy-minimized interpretation of selected response policies. Header and
|
|
2
2
|
// cookie values are read transiently from the already-selected homepage
|
|
3
3
|
// response and reduced to fixed signal identifiers plus bounded counts.
|
|
4
|
-
export const RESPONSE_POLICY_VERSION =
|
|
4
|
+
export const RESPONSE_POLICY_VERSION = 3;
|
|
5
5
|
export const MAX_RESPONSE_POLICY_HEADER_BYTES = 8 * 1024;
|
|
6
6
|
export const MAX_CSP_META_POLICIES = 4;
|
|
7
7
|
export const MAX_RESPONSE_POLICY_DIRECTIVES = 64;
|
|
@@ -13,7 +13,9 @@ export const MIN_RECOMMENDED_HSTS_SECONDS = 180 * 24 * 60 * 60;
|
|
|
13
13
|
const CONTROL_RE = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/;
|
|
14
14
|
const DIRECTIVE_NAME_RE = /^[a-z][a-z0-9-]{0,63}$/;
|
|
15
15
|
const ATTRIBUTE_NAME_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]{1,128}$/;
|
|
16
|
-
|
|
16
|
+
// CSP source-list grammar, not an entropy or digest-strength judgement. Invalid
|
|
17
|
+
// source expressions are ignored by browsers and cannot suppress unsafe-inline.
|
|
18
|
+
const CSP_NONCE_OR_HASH_RE = /^'(?:nonce-|sha(?:256|384|512)-)[A-Za-z0-9+/_-]+={0,2}'$/i;
|
|
17
19
|
const REFERRER_POLICIES = new Set([
|
|
18
20
|
'no-referrer',
|
|
19
21
|
'no-referrer-when-downgrade',
|
|
@@ -42,7 +44,8 @@ function readHeader(headers, name) {
|
|
|
42
44
|
return { state: 'absent', value: null };
|
|
43
45
|
if (CONTROL_RE.test(raw))
|
|
44
46
|
return { state: 'malformed', value: null };
|
|
45
|
-
if (
|
|
47
|
+
if (raw.length > MAX_RESPONSE_POLICY_HEADER_BYTES
|
|
48
|
+
|| new TextEncoder().encode(raw).byteLength > MAX_RESPONSE_POLICY_HEADER_BYTES) {
|
|
46
49
|
return { state: 'partial', value: null };
|
|
47
50
|
}
|
|
48
51
|
return { state: 'present', value: raw.trim() };
|
|
@@ -55,61 +58,96 @@ function addSignal(signals, id, count) {
|
|
|
55
58
|
function unavailableHeaderState(header) {
|
|
56
59
|
return header.state === 'present' ? 'malformed' : header.state;
|
|
57
60
|
}
|
|
58
|
-
function
|
|
61
|
+
function inlineControl(sources, scriptsBlocked) {
|
|
62
|
+
if (scriptsBlocked)
|
|
63
|
+
return 'restricted';
|
|
64
|
+
if (!sources)
|
|
65
|
+
return 'uncontrolled';
|
|
66
|
+
return sources.includes("'unsafe-inline'")
|
|
67
|
+
&& !sources.includes("'strict-dynamic'")
|
|
68
|
+
&& !sources.some((token) => CSP_NONCE_OR_HASH_RE.test(token))
|
|
69
|
+
? 'unqualified'
|
|
70
|
+
: 'restricted';
|
|
71
|
+
}
|
|
72
|
+
function intersectInlineControls(controls) {
|
|
73
|
+
if (controls.includes('restricted'))
|
|
74
|
+
return 'restricted';
|
|
75
|
+
if (controls.includes('unknown'))
|
|
76
|
+
return 'unknown';
|
|
77
|
+
return controls.includes('unqualified') ? 'unqualified' : 'uncontrolled';
|
|
78
|
+
}
|
|
79
|
+
function analyzeCsp(header, signals, fromMeta = false) {
|
|
59
80
|
if (header.state !== 'present' || !header.value) {
|
|
60
81
|
return { state: unavailableHeaderState(header), inlineControl: 'unknown' };
|
|
61
82
|
}
|
|
62
|
-
|
|
83
|
+
// Fetch combines repeated policy headers with commas. Each policy is
|
|
84
|
+
// independently enforced; later policies cannot loosen an earlier one.
|
|
85
|
+
const policies = [];
|
|
86
|
+
let directiveCount = 0;
|
|
63
87
|
let tokenCount = 0;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
88
|
+
for (const rawPolicy of (fromMeta ? [header.value] : header.value.split(','))) {
|
|
89
|
+
const directives = new Map();
|
|
90
|
+
for (const rawDirective of rawPolicy.split(';')) {
|
|
91
|
+
const normalized = rawDirective.trim();
|
|
92
|
+
if (!normalized)
|
|
93
|
+
continue;
|
|
94
|
+
const [rawName = '', ...rawTokens] = normalized.split(/[\t\n\f\r ]+/u);
|
|
95
|
+
const name = rawName.toLowerCase();
|
|
96
|
+
if (!DIRECTIVE_NAME_RE.test(name))
|
|
97
|
+
return { state: 'malformed', inlineControl: 'unknown' };
|
|
98
|
+
// Duplicate directives use their first value. Count even ignored input
|
|
99
|
+
// against the aggregate work bound rather than resetting per policy.
|
|
100
|
+
directiveCount += 1;
|
|
101
|
+
tokenCount += rawTokens.length;
|
|
102
|
+
if (directiveCount > MAX_RESPONSE_POLICY_DIRECTIVES || tokenCount > MAX_RESPONSE_POLICY_TOKENS) {
|
|
103
|
+
return { state: 'partial', inlineControl: 'unknown' };
|
|
104
|
+
}
|
|
105
|
+
if (!directives.has(name))
|
|
106
|
+
directives.set(name, rawTokens.map((token) => token.toLowerCase()));
|
|
78
107
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (tokens.length !== rawTokens.length)
|
|
82
|
-
partial = true;
|
|
83
|
-
tokenCount += tokens.length;
|
|
84
|
-
directives.set(name, tokens);
|
|
108
|
+
if (directives.size)
|
|
109
|
+
policies.push(directives);
|
|
85
110
|
}
|
|
86
|
-
if (!
|
|
111
|
+
if (!policies.length)
|
|
87
112
|
return { state: 'malformed', inlineControl: 'unknown' };
|
|
88
|
-
|
|
89
|
-
const scriptSources = directives.get('script-src') || defaultSources;
|
|
90
|
-
if (!defaultSources)
|
|
113
|
+
if (policies.every((policy) => !policy.has('default-src')))
|
|
91
114
|
addSignal(signals, 'csp_default_source_missing');
|
|
92
|
-
if (!
|
|
115
|
+
if (policies.every((policy) => !policy.has('base-uri')))
|
|
93
116
|
addSignal(signals, 'csp_base_uri_missing');
|
|
94
|
-
if (!
|
|
117
|
+
if (policies.every((policy) => !policy.has('object-src') && !policy.has('default-src'))) {
|
|
95
118
|
addSignal(signals, 'csp_object_source_unbounded');
|
|
96
|
-
let inlineControl = scriptSources ? 'restricted' : 'uncontrolled';
|
|
97
|
-
if (scriptSources) {
|
|
98
|
-
if (scriptSources.some((token) => ['*', 'http:', 'https:', 'data:', 'blob:'].includes(token))) {
|
|
99
|
-
addSignal(signals, 'csp_permissive_script_source');
|
|
100
|
-
}
|
|
101
|
-
if (scriptSources.includes("'unsafe-eval'"))
|
|
102
|
-
addSignal(signals, 'csp_unsafe_eval');
|
|
103
|
-
if (scriptSources.includes("'unsafe-inline'")
|
|
104
|
-
&& !scriptSources.some((token) => CSP_NONCE_OR_HASH_RE.test(token))) {
|
|
105
|
-
addSignal(signals, 'csp_unsafe_inline');
|
|
106
|
-
inlineControl = 'unqualified';
|
|
107
|
-
}
|
|
108
119
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
const contexts = policies.map((policy) => {
|
|
121
|
+
const scripts = policy.get('script-src') ?? policy.get('default-src');
|
|
122
|
+
const elements = policy.get('script-src-elem') ?? scripts;
|
|
123
|
+
const attributes = policy.get('script-src-attr') ?? scripts;
|
|
124
|
+
const sandbox = fromMeta ? undefined : policy.get('sandbox');
|
|
125
|
+
const scriptsBlocked = sandbox !== undefined && !sandbox.includes('allow-scripts');
|
|
126
|
+
return {
|
|
127
|
+
blocks: inlineControl(elements, scriptsBlocked),
|
|
128
|
+
attributes: inlineControl(attributes, scriptsBlocked),
|
|
129
|
+
// script-src-elem/attr never govern evaluation, even when they contain
|
|
130
|
+
// an unsafe-eval token. An absent script/default source is uncontrolled.
|
|
131
|
+
evaluation: !scriptsBlocked && (!scripts || scripts.includes("'unsafe-eval'")),
|
|
132
|
+
declaresEvaluation: scripts?.includes("'unsafe-eval'") === true,
|
|
133
|
+
broadSource: !scriptsBlocked && (!elements || (!elements.includes("'strict-dynamic'")
|
|
134
|
+
&& elements.some((token) => ['*', 'http:', 'https:', 'data:', 'blob:'].includes(token)))),
|
|
135
|
+
declaresBroadSource: elements !== undefined,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
if (contexts.every((context) => context.broadSource) && contexts.some((context) => context.declaresBroadSource)) {
|
|
139
|
+
addSignal(signals, 'csp_permissive_script_source');
|
|
140
|
+
}
|
|
141
|
+
if (contexts.every((context) => context.evaluation) && contexts.some((context) => context.declaresEvaluation)) {
|
|
142
|
+
addSignal(signals, 'csp_unsafe_eval');
|
|
143
|
+
}
|
|
144
|
+
const blocks = intersectInlineControls(contexts.map((context) => context.blocks));
|
|
145
|
+
if (blocks === 'unqualified')
|
|
146
|
+
addSignal(signals, 'csp_unsafe_inline');
|
|
147
|
+
if (intersectInlineControls(contexts.map((context) => context.attributes)) === 'unqualified') {
|
|
148
|
+
addSignal(signals, 'csp_unsafe_inline_attributes');
|
|
149
|
+
}
|
|
150
|
+
return { state: 'parsed', inlineControl: blocks };
|
|
113
151
|
}
|
|
114
152
|
export function analyzeCspMetaPolicies(value, sourceTruncated = false) {
|
|
115
153
|
const candidates = Array.isArray(value) ? value : [];
|
|
@@ -127,7 +165,7 @@ export function analyzeCspMetaPolicies(value, sourceTruncated = false) {
|
|
|
127
165
|
}
|
|
128
166
|
const policyContent = policy.content;
|
|
129
167
|
const content = readHeader({ get: () => policyContent }, 'content-security-policy');
|
|
130
|
-
const analysis = analyzeCsp(content, []);
|
|
168
|
+
const analysis = analyzeCsp(content, [], true);
|
|
131
169
|
if (analysis.state !== 'parsed') {
|
|
132
170
|
truncated = true;
|
|
133
171
|
continue;
|