@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,10 +1,22 @@
|
|
|
1
|
+
import { runIntakeCommand } from "./intake-command.mjs";
|
|
2
|
+
import { runIndicatorSetCommand } from "./indicator-set-command.mjs";
|
|
3
|
+
import { CONTEXT_REVIEW_KINDS } from "../packages/contracts/context-review.mjs";
|
|
4
|
+
import { readBoundedRegularFile } from "../lib/bounded-file.mjs";
|
|
5
|
+
import { MAX_ENCRYPTED_INVESTIGATION_PACKAGE_BYTES } from "../packages/contracts/investigation-package-limits.mjs";
|
|
6
|
+
import { verifyOfflineInvestigationFolder, verifyOfflineInvestigationPackage } from "./investigation-package-review.mjs";
|
|
7
|
+
import { readInvestigationFolder } from "./investigation-folder.mjs";
|
|
8
|
+
import { readBagItFolder } from "./bagit-folder.mjs";
|
|
9
|
+
import { verifyOfflineBagIt } from "./bagit-review.mjs";
|
|
10
|
+
import { MAX_BAGIT_ZIP_BYTES } from "../packages/interchange/bagit.mjs";
|
|
1
11
|
import { MAX_OFFLINE_ARTIFACT_BYTES, formatOfflineArtifactVerification, isCompleteOfflineArtifactVerification, verifyOfflineArtifact, } from "./artifact-verify.mjs";
|
|
2
12
|
import { MAX_CASE_PACK_INPUT_BYTES, buildCliCasePack, formatCliCasePack } from "./case-pack.mjs";
|
|
3
13
|
import { MAX_COMPARE_INPUT_BYTES, compareLookupDocument, parseCliLookupDocument, } from "./compare.mjs";
|
|
4
14
|
import { boundedCliErrorMessage, CliUsageError } from "./errors.mjs";
|
|
15
|
+
import { LOCAL_MMDB_REVIEW_SCHEMA, LOCAL_MMDB_REVIEW_VERSION } from "./local-mmdb-review.mjs";
|
|
5
16
|
import EXIT_CODES from "./exit-codes.mjs";
|
|
6
17
|
import { MAX_OFFLINE_EVIDENCE_INPUT_BYTES, buildOfflineEvidenceReview, buildOfflineEvidenceReviewWithLocalResources, formatOfflineEvidenceReview, } from "./offline-evidence-review.mjs";
|
|
7
18
|
import { buildCliLookupBrief, formatCliLookupBrief } from "./lookup-brief.mjs";
|
|
19
|
+
import { runCaseCommand } from "./case-command.mjs";
|
|
8
20
|
import { MAX_MAIL_REVIEW_INPUT_BYTES, buildCliMailReview, formatCliMailReview, } from "./mail-review.mjs";
|
|
9
21
|
import { MAX_MAIL_HEADER_INPUT_BYTES, buildCliMailHeaderReview, formatCliMailHeaderReview, } from "./mail-header-review.mjs";
|
|
10
22
|
import { buildCliPageComparison, formatCliPageComparison } from "./page-compare.mjs";
|
|
@@ -15,21 +27,67 @@ import { formatTerminalCompare } from "./formatters/terminal.mjs";
|
|
|
15
27
|
import { MAX_SAVED_LOOKUP_INPUT_BYTES } from "./saved-lookup.mjs";
|
|
16
28
|
import { REVIEW_INLINE_COMMANDS } from "./inline-command-families.mjs";
|
|
17
29
|
import { runDiscriminatedCommandHandler } from "./discriminated-command-handlers.mjs";
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
/** Readers retain their command-specific bounds and injection points. Only
|
|
31
|
+
* usage-error presentation and empty-input handling are shared. */
|
|
32
|
+
async function readReviewInput(read, label, emptyMessage) {
|
|
20
33
|
let input;
|
|
21
34
|
try {
|
|
22
|
-
input =
|
|
23
|
-
? await dependencies.readArtifactInput(args.source)
|
|
24
|
-
: await context.readInput(args.source, MAX_OFFLINE_ARTIFACT_BYTES, 'Artefact input');
|
|
35
|
+
input = await read();
|
|
25
36
|
}
|
|
26
37
|
catch (error) {
|
|
27
38
|
if (error instanceof CliUsageError)
|
|
28
39
|
throw error;
|
|
29
|
-
throw new CliUsageError(`Could not read
|
|
40
|
+
throw new CliUsageError(`Could not read ${label}: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
30
41
|
}
|
|
31
|
-
if (!input.trim())
|
|
32
|
-
throw new CliUsageError(
|
|
42
|
+
if (emptyMessage && !input.trim())
|
|
43
|
+
throw new CliUsageError(emptyMessage);
|
|
44
|
+
return input;
|
|
45
|
+
}
|
|
46
|
+
function writeReviewReport(context, args, report, format) {
|
|
47
|
+
if (!args.quiet)
|
|
48
|
+
context.writeStdout(args.output === 'json'
|
|
49
|
+
? formatJsonDocument(report)
|
|
50
|
+
: context.terminal(format(report), args.color));
|
|
51
|
+
}
|
|
52
|
+
async function runVerifyArtifactCommand(args, dependencies, context) {
|
|
53
|
+
context.setFailureLabel('Artefact verification');
|
|
54
|
+
if (args.folder) {
|
|
55
|
+
let files;
|
|
56
|
+
try {
|
|
57
|
+
files = await (args.bagit ? readBagItFolder : readInvestigationFolder)(args.folder, dependencies.signal);
|
|
58
|
+
}
|
|
59
|
+
catch (cause) {
|
|
60
|
+
dependencies.signal?.throwIfAborted();
|
|
61
|
+
const reason = cause instanceof TypeError ? boundedCliErrorMessage(cause) : 'The selected folder is unavailable or could not be read.';
|
|
62
|
+
throw new CliUsageError(`Could not read evidence folder: ${reason}`);
|
|
63
|
+
}
|
|
64
|
+
const report = await (args.bagit ? verifyOfflineBagIt : verifyOfflineInvestigationFolder)(files);
|
|
65
|
+
dependencies.signal?.throwIfAborted();
|
|
66
|
+
writeReviewReport(context, args, report, formatOfflineArtifactVerification);
|
|
67
|
+
return args.strictExit && !isCompleteOfflineArtifactVerification(report) ? EXIT_CODES.PARTIAL_FAILURE : EXIT_CODES.SUCCESS;
|
|
68
|
+
}
|
|
69
|
+
if (args.package) {
|
|
70
|
+
if (!args.source || args.source === '-')
|
|
71
|
+
throw new CliUsageError('--package requires a selected ZIP file; binary stdin is not accepted.');
|
|
72
|
+
let bytes;
|
|
73
|
+
try {
|
|
74
|
+
bytes = dependencies.readBinaryArtifactInput ? await dependencies.readBinaryArtifactInput(args.source)
|
|
75
|
+
: await readBoundedRegularFile(args.source, { maximumBytes: args.bagit ? MAX_BAGIT_ZIP_BYTES : MAX_ENCRYPTED_INVESTIGATION_PACKAGE_BYTES, minimumBytes: 22, label: 'Investigation package', ...(dependencies.signal ? { signal: dependencies.signal } : {}) });
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof CliUsageError)
|
|
79
|
+
throw error;
|
|
80
|
+
throw new CliUsageError(`Could not read package input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
81
|
+
}
|
|
82
|
+
const passphrase = args.passphraseSource ? await context.readPassphraseSource(args.passphraseSource) : undefined;
|
|
83
|
+
const report = args.bagit ? await verifyOfflineBagIt(bytes) : await verifyOfflineInvestigationPackage(bytes, passphrase);
|
|
84
|
+
dependencies.signal?.throwIfAborted();
|
|
85
|
+
writeReviewReport(context, args, report, formatOfflineArtifactVerification);
|
|
86
|
+
return args.strictExit && !isCompleteOfflineArtifactVerification(report) ? EXIT_CODES.PARTIAL_FAILURE : EXIT_CODES.SUCCESS;
|
|
87
|
+
}
|
|
88
|
+
const input = await readReviewInput(async () => dependencies.readArtifactInput
|
|
89
|
+
? await dependencies.readArtifactInput(args.source)
|
|
90
|
+
: await context.readInput(args.source, MAX_OFFLINE_ARTIFACT_BYTES, 'Artefact input'), 'artifact input', 'verify-artifact requires one JSON file or an artefact on stdin.');
|
|
33
91
|
const passphrase = args.passphraseSource ? await context.readPassphraseSource(args.passphraseSource) : null;
|
|
34
92
|
let manifest = null;
|
|
35
93
|
if (args.manifestSource && args.manifestEntryId) {
|
|
@@ -49,90 +107,44 @@ async function runVerifyArtifactCommand(args, dependencies, context) {
|
|
|
49
107
|
manifest = Object.freeze({ raw, entryId: args.manifestEntryId });
|
|
50
108
|
}
|
|
51
109
|
const report = await verifyOfflineArtifact(input, { passphrase, manifest });
|
|
52
|
-
|
|
53
|
-
context.writeStdout(args.output === 'json'
|
|
54
|
-
? formatJsonDocument(report)
|
|
55
|
-
: context.terminal(formatOfflineArtifactVerification(report), args.color));
|
|
56
|
-
}
|
|
110
|
+
writeReviewReport(context, args, report, formatOfflineArtifactVerification);
|
|
57
111
|
return args.strictExit && !isCompleteOfflineArtifactVerification(report)
|
|
58
112
|
? EXIT_CODES.PARTIAL_FAILURE
|
|
59
113
|
: EXIT_CODES.SUCCESS;
|
|
60
114
|
}
|
|
61
115
|
async function runInterchangeReportCommand(args, dependencies, context) {
|
|
62
116
|
context.setFailureLabel('Interchange fidelity report');
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
input
|
|
66
|
-
? await dependencies.readArtifactInput(args.source)
|
|
67
|
-
: await context.readInput(args.source, MAX_OFFLINE_ARTIFACT_BYTES, 'Interchange input');
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
if (error instanceof CliUsageError)
|
|
71
|
-
throw error;
|
|
72
|
-
throw new CliUsageError(`Could not read interchange input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
73
|
-
}
|
|
74
|
-
if (!input.trim())
|
|
75
|
-
throw new CliUsageError('interchange-report requires one JSON file or an artefact on stdin.');
|
|
117
|
+
const input = await readReviewInput(async () => dependencies.readArtifactInput
|
|
118
|
+
? await dependencies.readArtifactInput(args.source)
|
|
119
|
+
: await context.readInput(args.source, MAX_OFFLINE_ARTIFACT_BYTES, 'Interchange input'), 'interchange input', 'interchange-report requires one JSON file or an artefact on stdin.');
|
|
76
120
|
const passphrase = args.passphraseSource ? await context.readPassphraseSource(args.passphraseSource) : null;
|
|
77
121
|
const report = await buildInterchangeFidelityReport(input, {
|
|
78
122
|
generatedAt: context.now(),
|
|
79
123
|
passphrase,
|
|
80
124
|
});
|
|
81
|
-
|
|
82
|
-
context.writeStdout(args.output === 'json'
|
|
83
|
-
? formatJsonDocument(report)
|
|
84
|
-
: context.terminal(formatInterchangeFidelityReport(report), args.color));
|
|
85
|
-
}
|
|
125
|
+
writeReviewReport(context, args, report, formatInterchangeFidelityReport);
|
|
86
126
|
return EXIT_CODES.SUCCESS;
|
|
87
127
|
}
|
|
88
128
|
async function runSourceReportCommand(args, dependencies, context) {
|
|
89
129
|
context.setFailureLabel('Source reliability report');
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
input
|
|
93
|
-
? await dependencies.readSourceReliabilityInput(args.source)
|
|
94
|
-
: await context.readInput(args.source, MAX_SOURCE_RELIABILITY_INPUT_BYTES, 'Source reliability input');
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
if (error instanceof CliUsageError)
|
|
98
|
-
throw error;
|
|
99
|
-
throw new CliUsageError(`Could not read source reliability input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
100
|
-
}
|
|
101
|
-
if (!input.trim())
|
|
102
|
-
throw new CliUsageError('source-report requires one JSON file or lookup documents on stdin.');
|
|
130
|
+
const input = await readReviewInput(async () => dependencies.readSourceReliabilityInput
|
|
131
|
+
? await dependencies.readSourceReliabilityInput(args.source)
|
|
132
|
+
: await context.readInput(args.source, MAX_SOURCE_RELIABILITY_INPUT_BYTES, 'Source reliability input'), 'source reliability input', 'source-report requires one JSON file or lookup documents on stdin.');
|
|
103
133
|
const report = buildSourceReliabilityReport(input, context.now());
|
|
104
|
-
|
|
105
|
-
context.writeStdout(args.output === 'json'
|
|
106
|
-
? formatJsonDocument(report)
|
|
107
|
-
: context.terminal(formatSourceReliabilityReport(report), args.color));
|
|
108
|
-
}
|
|
134
|
+
writeReviewReport(context, args, report, formatSourceReliabilityReport);
|
|
109
135
|
return EXIT_CODES.SUCCESS;
|
|
110
136
|
}
|
|
111
137
|
async function runCompareCommand(args, dependencies, context) {
|
|
112
138
|
context.setFailureLabel('Registry comparison');
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
input
|
|
116
|
-
? await dependencies.readCompareInput(args.source)
|
|
117
|
-
: await context.readInput(args.source, MAX_COMPARE_INPUT_BYTES, 'Comparison input');
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
if (error instanceof CliUsageError)
|
|
121
|
-
throw error;
|
|
122
|
-
throw new CliUsageError(`Could not read comparison input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
123
|
-
}
|
|
124
|
-
if (!input.trim())
|
|
125
|
-
throw new CliUsageError('compare requires one lookup JSON file or a lookup document on stdin.');
|
|
139
|
+
const input = await readReviewInput(async () => dependencies.readCompareInput
|
|
140
|
+
? await dependencies.readCompareInput(args.source)
|
|
141
|
+
: await context.readInput(args.source, MAX_COMPARE_INPUT_BYTES, 'Comparison input'), 'comparison input', 'compare requires one lookup JSON file or a lookup document on stdin.');
|
|
126
142
|
const parsed = parseCliLookupDocument(input);
|
|
127
143
|
const loadComparison = dependencies.loadRegistryComparison || (() => import("../lib/registry-comparison.mjs"));
|
|
128
144
|
const comparisonModule = await loadComparison();
|
|
129
145
|
const result = compareLookupDocument(parsed, comparisonModule.compareRegistrySources, comparisonModule.compareRdapPublications);
|
|
130
146
|
const document = buildCliCompareDocument(result, context.now());
|
|
131
|
-
|
|
132
|
-
context.writeStdout(args.output === 'json'
|
|
133
|
-
? formatJsonDocument(document)
|
|
134
|
-
: context.terminal(formatTerminalCompare(document), args.color));
|
|
135
|
-
}
|
|
147
|
+
writeReviewReport(context, args, document, formatTerminalCompare);
|
|
136
148
|
return EXIT_CODES.SUCCESS;
|
|
137
149
|
}
|
|
138
150
|
async function runPageCompareCommand(args, dependencies, context) {
|
|
@@ -153,80 +165,36 @@ async function runPageCompareCommand(args, dependencies, context) {
|
|
|
153
165
|
throw new CliUsageError(`Could not read page comparison input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
154
166
|
}
|
|
155
167
|
const document = buildCliPageComparison(leftInput, rightInput, context.now());
|
|
156
|
-
|
|
157
|
-
context.writeStdout(args.output === 'json'
|
|
158
|
-
? formatJsonDocument(document)
|
|
159
|
-
: context.terminal(formatCliPageComparison(document), args.color));
|
|
160
|
-
}
|
|
168
|
+
writeReviewReport(context, args, document, formatCliPageComparison);
|
|
161
169
|
return EXIT_CODES.SUCCESS;
|
|
162
170
|
}
|
|
163
171
|
async function runMailReviewCommand(args, dependencies, context) {
|
|
164
172
|
context.setFailureLabel('Passive mail review');
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
input
|
|
168
|
-
? await dependencies.readMailReviewInput(args.source)
|
|
169
|
-
: await context.readInput(args.source, MAX_MAIL_REVIEW_INPUT_BYTES, 'Mail review input');
|
|
170
|
-
}
|
|
171
|
-
catch (error) {
|
|
172
|
-
if (error instanceof CliUsageError)
|
|
173
|
-
throw error;
|
|
174
|
-
throw new CliUsageError(`Could not read mail review input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
175
|
-
}
|
|
173
|
+
const input = await readReviewInput(async () => dependencies.readMailReviewInput
|
|
174
|
+
? await dependencies.readMailReviewInput(args.source)
|
|
175
|
+
: await context.readInput(args.source, MAX_MAIL_REVIEW_INPUT_BYTES, 'Mail review input'), 'mail review input');
|
|
176
176
|
const document = buildCliMailReview(input, context.now());
|
|
177
|
-
|
|
178
|
-
context.writeStdout(args.output === 'json'
|
|
179
|
-
? formatJsonDocument(document)
|
|
180
|
-
: context.terminal(formatCliMailReview(document), args.color));
|
|
181
|
-
}
|
|
177
|
+
writeReviewReport(context, args, document, formatCliMailReview);
|
|
182
178
|
return EXIT_CODES.SUCCESS;
|
|
183
179
|
}
|
|
184
180
|
async function runMailHeadersCommand(args, dependencies, context) {
|
|
185
181
|
context.setFailureLabel('Mail-header review');
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
input
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
if (error instanceof CliUsageError)
|
|
194
|
-
throw error;
|
|
195
|
-
throw new CliUsageError(`Could not read mail-header input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
196
|
-
}
|
|
197
|
-
if (!input.trim())
|
|
198
|
-
throw new CliUsageError('mail-headers requires one message or header file, or headers on stdin.');
|
|
199
|
-
const document = buildCliMailHeaderReview(input, context.now());
|
|
200
|
-
if (!args.quiet) {
|
|
201
|
-
context.writeStdout(args.output === 'json'
|
|
202
|
-
? formatJsonDocument(document)
|
|
203
|
-
: context.terminal(formatCliMailHeaderReview(document), args.color));
|
|
204
|
-
}
|
|
182
|
+
const input = await readReviewInput(async () => dependencies.readMailHeaderInput
|
|
183
|
+
? await dependencies.readMailHeaderInput(args.source)
|
|
184
|
+
: await context.readHeaderInput(args.source, MAX_MAIL_HEADER_INPUT_BYTES, 'Mail-header input'), 'mail-header input', 'mail-headers requires one message or header file, or headers on stdin.');
|
|
185
|
+
const document = buildCliMailHeaderReview(input, context.now(), args.trustedAuthHeaders);
|
|
186
|
+
writeReviewReport(context, args, document, formatCliMailHeaderReview);
|
|
205
187
|
return EXIT_CODES.SUCCESS;
|
|
206
188
|
}
|
|
207
189
|
async function runOfflineEvidenceReviewCommand(args, dependencies, context) {
|
|
208
190
|
context.setFailureLabel('Offline evidence review');
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
input
|
|
212
|
-
? await dependencies.readArtifactInput(args.source)
|
|
213
|
-
: await context.readInput(args.source, MAX_OFFLINE_EVIDENCE_INPUT_BYTES, 'Offline evidence input');
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
if (error instanceof CliUsageError)
|
|
217
|
-
throw error;
|
|
218
|
-
throw new CliUsageError(`Could not read offline evidence input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
219
|
-
}
|
|
220
|
-
if (!input.trim())
|
|
221
|
-
throw new CliUsageError('review-evidence requires one JSON file or a document on stdin.');
|
|
191
|
+
const input = await readReviewInput(async () => dependencies.readArtifactInput
|
|
192
|
+
? await dependencies.readArtifactInput(args.source)
|
|
193
|
+
: await context.readInput(args.source, MAX_OFFLINE_EVIDENCE_INPUT_BYTES, 'Offline evidence input'), 'offline evidence input', 'review-evidence requires one JSON file or a document on stdin.');
|
|
222
194
|
const document = args.mmdbSource
|
|
223
195
|
? await buildOfflineEvidenceReviewWithLocalResources(input, context.now(), { mmdbPath: args.mmdbSource })
|
|
224
196
|
: buildOfflineEvidenceReview(input, context.now());
|
|
225
|
-
|
|
226
|
-
context.writeStdout(args.output === 'json'
|
|
227
|
-
? formatJsonDocument(document)
|
|
228
|
-
: context.terminal(formatOfflineEvidenceReview(document), args.color));
|
|
229
|
-
}
|
|
197
|
+
writeReviewReport(context, args, document, formatOfflineEvidenceReview);
|
|
230
198
|
if (args.strictExit) {
|
|
231
199
|
const result = document.result && typeof document.result === 'object' && !Array.isArray(document.result)
|
|
232
200
|
? document.result
|
|
@@ -237,7 +205,9 @@ async function runOfflineEvidenceReviewCommand(args, dependencies, context) {
|
|
|
237
205
|
const zoneMismatch = document.kind === 'zone_intent' && (result.complete !== true
|
|
238
206
|
|| (result.counts && typeof result.counts === 'object' && !Array.isArray(result.counts)
|
|
239
207
|
&& ['different', 'missing', 'unexpected', 'incomplete'].some((key) => Number(result.counts[key]) > 0)));
|
|
240
|
-
|
|
208
|
+
const contextPartial = CONTEXT_REVIEW_KINDS.includes(document.kind) && result.state === 'partial';
|
|
209
|
+
const mmdbIncomplete = result.schema === LOCAL_MMDB_REVIEW_SCHEMA && result.version === LOCAL_MMDB_REVIEW_VERSION && result.completeness !== 'complete';
|
|
210
|
+
if (gate?.pass === false || zoneMismatch || contextPartial || mmdbIncomplete)
|
|
241
211
|
return EXIT_CODES.PARTIAL_FAILURE;
|
|
242
212
|
}
|
|
243
213
|
return EXIT_CODES.SUCCESS;
|
|
@@ -245,34 +215,16 @@ async function runOfflineEvidenceReviewCommand(args, dependencies, context) {
|
|
|
245
215
|
async function runBriefOrCasePackCommand(args, dependencies, context) {
|
|
246
216
|
const isBrief = args.action === 'brief';
|
|
247
217
|
context.setFailureLabel(isBrief ? 'Lookup brief' : 'Case pack');
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
input
|
|
251
|
-
? await dependencies.readArtifactInput(args.source)
|
|
252
|
-
: await context.readInput(args.source, isBrief ? MAX_SAVED_LOOKUP_INPUT_BYTES : MAX_CASE_PACK_INPUT_BYTES, isBrief ? 'Lookup brief input' : 'Case-pack input');
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
if (error instanceof CliUsageError)
|
|
256
|
-
throw error;
|
|
257
|
-
throw new CliUsageError(`Could not read ${isBrief ? 'Lookup brief' : 'case-pack'} input: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
258
|
-
}
|
|
259
|
-
if (!input.trim())
|
|
260
|
-
throw new CliUsageError(`${args.action} requires one JSON file or a document on stdin.`);
|
|
218
|
+
const input = await readReviewInput(async () => dependencies.readArtifactInput
|
|
219
|
+
? await dependencies.readArtifactInput(args.source)
|
|
220
|
+
: await context.readInput(args.source, isBrief ? MAX_SAVED_LOOKUP_INPUT_BYTES : MAX_CASE_PACK_INPUT_BYTES, isBrief ? 'Lookup brief input' : 'Case-pack input'), `${isBrief ? 'Lookup brief' : 'case-pack'} input`, `${args.action} requires one JSON file or a document on stdin.`);
|
|
261
221
|
if (args.action === 'brief') {
|
|
262
222
|
const document = buildCliLookupBrief(input, context.now());
|
|
263
|
-
|
|
264
|
-
context.writeStdout(args.output === 'json'
|
|
265
|
-
? formatJsonDocument(document)
|
|
266
|
-
: context.terminal(formatCliLookupBrief(document), args.color));
|
|
267
|
-
}
|
|
223
|
+
writeReviewReport(context, args, document, formatCliLookupBrief);
|
|
268
224
|
}
|
|
269
225
|
else {
|
|
270
226
|
const document = buildCliCasePack(input, { audience: args.audience, reviewed: args.reviewed }, context.now());
|
|
271
|
-
|
|
272
|
-
context.writeStdout(args.output === 'json'
|
|
273
|
-
? formatJsonDocument(document)
|
|
274
|
-
: context.terminal(formatCliCasePack(document), args.color));
|
|
275
|
-
}
|
|
227
|
+
writeReviewReport(context, args, document, formatCliCasePack);
|
|
276
228
|
}
|
|
277
229
|
return EXIT_CODES.SUCCESS;
|
|
278
230
|
}
|
|
@@ -284,9 +236,12 @@ const REVIEW_COMMAND_HANDLERS = Object.freeze({
|
|
|
284
236
|
'page-compare': runPageCompareCommand,
|
|
285
237
|
'mail-review': runMailReviewCommand,
|
|
286
238
|
'mail-headers': runMailHeadersCommand,
|
|
239
|
+
intake: runIntakeCommand,
|
|
287
240
|
'review-evidence': runOfflineEvidenceReviewCommand,
|
|
288
241
|
'brief': runBriefOrCasePackCommand,
|
|
242
|
+
'case': runCaseCommand,
|
|
289
243
|
'case-pack': runBriefOrCasePackCommand,
|
|
244
|
+
'indicator-set': runIndicatorSetCommand,
|
|
290
245
|
});
|
|
291
246
|
function runReviewCommand(args, dependencies, context) {
|
|
292
247
|
return runDiscriminatedCommandHandler(REVIEW_COMMAND_HANDLERS, args, dependencies, context);
|
package/cli/risk-calibration.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { decodeBoundedUtf8 } from "../lib/bounded-file.mjs";
|
|
3
|
-
import { isValidAsciiDomainName } from "../
|
|
4
|
-
import { scanBoundedJson } from "../
|
|
5
|
-
import { canonicalRegistrableDomain } from "../
|
|
6
|
-
import { ANALYST_REVIEW_REASON_VALUES, analystInteroperabilityTags } from "../
|
|
7
|
-
import { buildRiskCalibrationSummaryReport, parseRiskCalibrationSummaryReport, RISK_CALIBRATION_SUMMARY_INTERPRETATION, RISK_CALIBRATION_SUMMARY_THRESHOLDS, } from "../
|
|
3
|
+
import { isValidAsciiDomainName } from "../packages/contracts/domain-name.mjs";
|
|
4
|
+
import { scanBoundedJson } from "../packages/analysis/bounded-json.mjs";
|
|
5
|
+
import { canonicalRegistrableDomain } from "../packages/analysis/registrable-domain.mjs";
|
|
6
|
+
import { ANALYST_REVIEW_REASON_VALUES, analystInteroperabilityTags } from "../packages/analysis/analyst-taxonomy.mjs";
|
|
7
|
+
import { buildRiskCalibrationSummaryReport, parseRiskCalibrationSummaryReport, RISK_CALIBRATION_SUMMARY_INTERPRETATION, RISK_CALIBRATION_SUMMARY_THRESHOLDS, } from "../packages/analysis/risk-calibration-summary.mjs";
|
|
8
8
|
import { MAX_RISK_CALIBRATION_ACTIVITY_LENGTH, MAX_RISK_CALIBRATION_AVAILABILITY_LENGTH, MAX_RISK_CALIBRATION_DISPOSITION_LENGTH, MAX_RISK_CALIBRATION_DOMAIN_AGE_DAYS, MAX_RISK_CALIBRATION_DOMAIN_LENGTH, MAX_RISK_CALIBRATION_FINDING_CATEGORY_LENGTH, MAX_RISK_CALIBRATION_FINDINGS_PER_PROVIDER, MAX_RISK_CALIBRATION_INPUT_BYTES, MAX_RISK_CALIBRATION_JSON_CONTAINER_ITEMS, MAX_RISK_CALIBRATION_JSON_DEPTH, MAX_RISK_CALIBRATION_JSON_KEYS, MAX_RISK_CALIBRATION_JSON_VALUES, MAX_RISK_CALIBRATION_MUTATIONS, MAX_RISK_CALIBRATION_PROVIDER_ID_LENGTH, MAX_RISK_CALIBRATION_PROVIDER_STATE_LENGTH, MAX_RISK_CALIBRATION_PROVIDERS, MAX_RISK_CALIBRATION_RECORD_ID_LENGTH, MAX_RISK_CALIBRATION_RECORDS, MAX_RISK_CALIBRATION_REVIEW_REASON_LENGTH, MAX_RISK_CALIBRATION_SCAN_DEPTH_LENGTH, MAX_RISK_CALIBRATION_STRING_LENGTH, MAX_RISK_CALIBRATION_TIMESTAMP_LENGTH, RISK_CALIBRATION_DATASET_SCHEMA, RISK_CALIBRATION_DATASET_VERSION, RISK_CALIBRATION_REPORT_SCHEMA, RISK_CALIBRATION_REPORT_VERSION, RISK_CALIBRATION_DISPOSITIONS, serializeRiskCalibrationSnapshot, snapshotRiskCalibrationReportForSerialization, SUPPORTED_RISK_CALIBRATION_DATASET_VERSIONS, } from "../packages/contracts/risk-calibration.mjs";
|
|
9
|
-
import { CliUsageError } from "./
|
|
10
|
-
import { RISK_MUTATION_TYPES } from "../
|
|
11
|
-
import { THREAT_INTELLIGENCE_CONTRACT_VERSION, THREAT_INTELLIGENCE_ENVELOPE_VERSION, THREAT_INTELLIGENCE_SCHEMA, } from "../
|
|
9
|
+
import { CliUsageError } from "./errors.mjs";
|
|
10
|
+
import { RISK_MUTATION_TYPES } from "../packages/analysis/risk-scoring.mjs";
|
|
11
|
+
import { THREAT_INTELLIGENCE_CONTRACT_VERSION, THREAT_INTELLIGENCE_ENVELOPE_VERSION, THREAT_INTELLIGENCE_SCHEMA, } from "../packages/analysis/threat-intelligence-types.mjs";
|
|
12
12
|
export { MAX_RISK_CALIBRATION_INPUT_BYTES, MAX_RISK_CALIBRATION_RECORDS, MAX_RISK_CALIBRATION_STRING_LENGTH, RISK_CALIBRATION_DATASET_SCHEMA, RISK_CALIBRATION_DATASET_VERSION, RISK_CALIBRATION_REPORT_SCHEMA, RISK_CALIBRATION_REPORT_VERSION, SUPPORTED_RISK_CALIBRATION_DATASET_VERSIONS, } from "../packages/contracts/risk-calibration.mjs";
|
|
13
13
|
export const RISK_CALIBRATION_THRESHOLDS = RISK_CALIBRATION_SUMMARY_THRESHOLDS;
|
|
14
14
|
const CONTROL_RE = /[\x00-\x1f\x7f]/;
|
package/cli/runner.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { abortable } from "../lib/abort.mjs";
|
|
3
|
-
import { parseCliArguments } from "./arguments.mjs";
|
|
3
|
+
import { isCliCommandForOwner, parseCliArguments } from "./arguments.mjs";
|
|
4
4
|
import { HELP, commandDefinition, commandHelp, } from "./command-reference.mjs";
|
|
5
5
|
import { boundedCliErrorMessage, CliUsageError } from "./errors.mjs";
|
|
6
6
|
import { evidenceCommandFailureLabel, isEvidenceCommand, runEvidenceCommand, } from "./evidence-command-runner.mjs";
|
|
@@ -9,7 +9,7 @@ import { readCliHeaderInput, readCliTextInput } from "./input.mjs";
|
|
|
9
9
|
import { INLINE_CLI_COMMANDS, runInlineCommand } from "./inline-command-runner.mjs";
|
|
10
10
|
import { canLaunchInteractiveCli, launchInteractiveCli, } from "./interactive-launcher.mjs";
|
|
11
11
|
import { MAX_OFFLINE_PASSPHRASE_FILE_BYTES, } from "./artifact-verify.mjs";
|
|
12
|
-
import { cleanupPendingOutputFiles, createBufferedOutput, writePrivateFile } from "./output-file.mjs";
|
|
12
|
+
import { cleanupPendingOutputFiles, createBufferedOutput, writePrivateFile, MAX_CLI_OUTPUT_BYTES } from "./output-file.mjs";
|
|
13
13
|
import { createTerminalProgress } from "./progress.mjs";
|
|
14
14
|
import { presentTerminalOutput, terminalPresentation, } from "./terminal-presentation.mjs";
|
|
15
15
|
const require = createRequire(import.meta.url);
|
|
@@ -40,17 +40,7 @@ function isCancellation(error, signal) {
|
|
|
40
40
|
return signal?.aborted === true
|
|
41
41
|
|| Boolean(error && typeof error === 'object' && 'name' in error && error.name === 'AbortError');
|
|
42
42
|
}
|
|
43
|
-
function
|
|
44
|
-
const message = boundedCliErrorMessage(error, 'Invalid command input').toLowerCase();
|
|
45
|
-
if (message.includes('could not read'))
|
|
46
|
-
return 'input_unavailable';
|
|
47
|
-
if (message.includes('cannot be combined') || message.includes('mutually exclusive'))
|
|
48
|
-
return 'conflicting_options';
|
|
49
|
-
if (message.includes('requires') || message.includes('did not contain'))
|
|
50
|
-
return 'missing_input';
|
|
51
|
-
return 'invalid_input';
|
|
52
|
-
}
|
|
53
|
-
async function runParsedCli(args, dependencies = {}) {
|
|
43
|
+
async function runParsedCli(args, dependencies = {}, writeBinaryOutput) {
|
|
54
44
|
const stdout = dependencies.stdout || process.stdout;
|
|
55
45
|
const stderr = dependencies.stderr || process.stderr;
|
|
56
46
|
const environment = dependencies.environment || process.env;
|
|
@@ -113,7 +103,7 @@ async function runParsedCli(args, dependencies = {}) {
|
|
|
113
103
|
catch (error) {
|
|
114
104
|
if (error instanceof CliUsageError)
|
|
115
105
|
throw error;
|
|
116
|
-
throw new CliUsageError(`Could not read passphrase file: ${boundedCliErrorMessage(error, 'File could not be read')}
|
|
106
|
+
throw new CliUsageError(`Could not read passphrase file: ${boundedCliErrorMessage(error, 'File could not be read')}`, 'input_unavailable');
|
|
117
107
|
}
|
|
118
108
|
const passphrase = passphraseText.replace(/\r?\n$/u, '');
|
|
119
109
|
if (!passphrase || /[\r\n\u0000]/u.test(passphrase)) {
|
|
@@ -128,6 +118,7 @@ async function runParsedCli(args, dependencies = {}) {
|
|
|
128
118
|
terminal,
|
|
129
119
|
presentation: (color) => terminalPresentation(stdout, color, environment, palette),
|
|
130
120
|
writeStdout: (value) => write(stdout, value),
|
|
121
|
+
...(writeBinaryOutput ? { writeBinaryOutput } : {}),
|
|
131
122
|
writeStderr: (value) => write(stderr, value),
|
|
132
123
|
readSingleInput,
|
|
133
124
|
readInput,
|
|
@@ -197,25 +188,9 @@ async function runParsedCli(args, dependencies = {}) {
|
|
|
197
188
|
});
|
|
198
189
|
}
|
|
199
190
|
if (handlerOwner === 'network') {
|
|
200
|
-
if (args
|
|
201
|
-
&& args.action !== 'posture'
|
|
202
|
-
&& args.action !== 'http'
|
|
203
|
-
&& args.action !== 'tls'
|
|
204
|
-
&& args.action !== 'dnssec-validate'
|
|
205
|
-
&& args.action !== 'mail-transport') {
|
|
191
|
+
if (!isCliCommandForOwner(args, 'network')) {
|
|
206
192
|
throw new Error('Network command registry ownership is inconsistent.');
|
|
207
193
|
}
|
|
208
|
-
failureLabel = args.action === 'ct-search'
|
|
209
|
-
? 'Certificate Transparency search'
|
|
210
|
-
: args.action === 'posture'
|
|
211
|
-
? 'Domain posture audit'
|
|
212
|
-
: args.action === 'http'
|
|
213
|
-
? 'HTTP probe'
|
|
214
|
-
: args.action === 'tls'
|
|
215
|
-
? 'TLS evidence collection'
|
|
216
|
-
: args.action === 'dnssec-validate'
|
|
217
|
-
? 'DNSSEC chain validation'
|
|
218
|
-
: 'Mail transport review';
|
|
219
194
|
const { runNetworkCommand } = await import("./network-command-runner.mjs");
|
|
220
195
|
return await runNetworkCommand(args, dependencies, commandContext);
|
|
221
196
|
}
|
|
@@ -243,7 +218,7 @@ async function runParsedCli(args, dependencies = {}) {
|
|
|
243
218
|
eventProgress.current?.emit({
|
|
244
219
|
event: 'failed',
|
|
245
220
|
state: 'usage',
|
|
246
|
-
reason:
|
|
221
|
+
reason: error.reason,
|
|
247
222
|
exitCode: EXIT_CODES.USAGE,
|
|
248
223
|
});
|
|
249
224
|
if (!eventProgress.current?.enabled)
|
|
@@ -305,15 +280,72 @@ async function runCliCommand(argv, dependencies = {}) {
|
|
|
305
280
|
}
|
|
306
281
|
if (!args.destination)
|
|
307
282
|
return runParsedCli(args, dependencies);
|
|
308
|
-
const buffered = createBufferedOutput();
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
return code;
|
|
283
|
+
const buffered = createBufferedOutput({ binary: args.action === 'manifest' && args.package === true });
|
|
284
|
+
let checkpoint = null;
|
|
285
|
+
let capturedSource = null;
|
|
312
286
|
try {
|
|
313
|
-
|
|
287
|
+
if (args.action === 'workflow-run') {
|
|
288
|
+
const { prepareInvestigationCheckpoint } = await import("./investigation-checkpoint.mjs");
|
|
289
|
+
const workflowCheckpoint = await prepareInvestigationCheckpoint({
|
|
290
|
+
destination: args.destination, resumeSource: args.resumeSource, force: args.force === true,
|
|
291
|
+
...(dependencies.signal ? { signal: dependencies.signal } : {}),
|
|
292
|
+
});
|
|
293
|
+
checkpoint = workflowCheckpoint;
|
|
294
|
+
capturedSource = workflowCheckpoint.resumeInput;
|
|
295
|
+
}
|
|
296
|
+
else if (args.action === 'case') {
|
|
297
|
+
const { prepareLocalDocumentWrite } = await import("./local-document-checkpoint.mjs");
|
|
298
|
+
const { MAX_EDITABLE_CASE_INPUT_BYTES, MAX_EDITABLE_CASE_OUTPUT_BYTES } = await import("../packages/contracts/case-portability.mjs");
|
|
299
|
+
const caseCheckpoint = await prepareLocalDocumentWrite({
|
|
300
|
+
destination: args.destination, source: args.source, force: args.force === true, label: 'Case',
|
|
301
|
+
allowSourceReplacement: args.operation !== 'show',
|
|
302
|
+
maximumInputBytes: MAX_EDITABLE_CASE_INPUT_BYTES, maximumOutputBytes: MAX_EDITABLE_CASE_OUTPUT_BYTES,
|
|
303
|
+
...(dependencies.signal ? { signal: dependencies.signal } : {}),
|
|
304
|
+
});
|
|
305
|
+
checkpoint = caseCheckpoint;
|
|
306
|
+
capturedSource = caseCheckpoint.sourceInput;
|
|
307
|
+
}
|
|
308
|
+
else if (args.action === 'indicator-set') {
|
|
309
|
+
const { prepareLocalDocumentWrite } = await import("./local-document-checkpoint.mjs");
|
|
310
|
+
const { MAX_MANAGED_INDICATOR_PLAN_BYTES, MAX_MANAGED_INDICATOR_SET_BYTES } = await import("../packages/contracts/analyst-interchange.mjs");
|
|
311
|
+
const indicatorCheckpoint = await prepareLocalDocumentWrite({
|
|
312
|
+
destination: args.destination, source: args.source,
|
|
313
|
+
force: args.force === true, label: 'Indicator revision', allowSourceReplacement: false,
|
|
314
|
+
maximumInputBytes: args.operation === 'revise' ? MAX_MANAGED_INDICATOR_PLAN_BYTES : MAX_MANAGED_INDICATOR_SET_BYTES,
|
|
315
|
+
maximumOutputBytes: MAX_CLI_OUTPUT_BYTES,
|
|
316
|
+
...(dependencies.signal ? { signal: dependencies.signal } : {}),
|
|
317
|
+
});
|
|
318
|
+
checkpoint = indicatorCheckpoint;
|
|
319
|
+
capturedSource = indicatorCheckpoint.sourceInput;
|
|
320
|
+
}
|
|
321
|
+
const code = await runParsedCli(args, {
|
|
322
|
+
...dependencies, stdout: buffered.stream,
|
|
323
|
+
...(args.action === 'workflow-run' && args.resumeSource && capturedSource !== null
|
|
324
|
+
? { workflowResumeInput: capturedSource }
|
|
325
|
+
: {}),
|
|
326
|
+
...(args.action === 'case' && capturedSource !== null ? { caseFileInput: capturedSource } : {}),
|
|
327
|
+
...(args.action === 'indicator-set' && capturedSource !== null ? { readArtifactInput: async () => capturedSource } : {}),
|
|
328
|
+
}, buffered.writeBinary);
|
|
329
|
+
if (code !== EXIT_CODES.SUCCESS && code !== EXIT_CODES.PARTIAL_FAILURE)
|
|
330
|
+
return code;
|
|
331
|
+
const content = buffered.value();
|
|
332
|
+
if (checkpoint) {
|
|
333
|
+
if (typeof content !== 'string')
|
|
334
|
+
throw new TypeError('Local document output must be text.');
|
|
335
|
+
await checkpoint.publish(content);
|
|
336
|
+
}
|
|
337
|
+
else
|
|
338
|
+
await writePrivateFile(args.destination, content, {
|
|
339
|
+
force: args.force === true,
|
|
340
|
+
beforePublish: async () => { dependencies.signal?.throwIfAborted(); },
|
|
341
|
+
});
|
|
314
342
|
return code;
|
|
315
343
|
}
|
|
316
344
|
catch (error) {
|
|
345
|
+
if (isCancellation(error, dependencies.signal)) {
|
|
346
|
+
write(stderr, 'Cancelled by analyst.\n');
|
|
347
|
+
return EXIT_CODES.CANCELLED;
|
|
348
|
+
}
|
|
317
349
|
if (error instanceof CliUsageError) {
|
|
318
350
|
write(stderr, `Usage error: ${boundedCliErrorMessage(error, 'Output file could not be written')}\n`);
|
|
319
351
|
return EXIT_CODES.USAGE;
|
|
@@ -321,6 +353,11 @@ async function runCliCommand(argv, dependencies = {}) {
|
|
|
321
353
|
write(stderr, `Output failed: ${boundedCliErrorMessage(error, 'Output file could not be written')}\n`);
|
|
322
354
|
return EXIT_CODES.LOOKUP_FAILED;
|
|
323
355
|
}
|
|
356
|
+
finally {
|
|
357
|
+
if (checkpoint && await checkpoint.release() > 0) {
|
|
358
|
+
write(stderr, `${args.action === 'case' ? 'Case' : args.action === 'indicator-set' ? 'Indicator revision' : 'Workflow'} cleanup warning: File ownership changed or a lease could not be removed. Inspect the selected directory before resuming.\n`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
324
361
|
}
|
|
325
362
|
async function runCli(argv, dependencies = {}) {
|
|
326
363
|
const stderr = dependencies.stderr || process.stderr;
|
package/cli/saved-lookup.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { types as utilTypes } from 'node:util';
|
|
3
3
|
import { classifyQuery } from "../lib/classify.mjs";
|
|
4
|
-
import { isSafeJsonObjectKey, scanBoundedJson, } from "../
|
|
4
|
+
import { isSafeJsonObjectKey, scanBoundedJson, } from "../packages/analysis/bounded-json.mjs";
|
|
5
5
|
import { decodeBoundedUtf8 } from "../lib/bounded-file.mjs";
|
|
6
|
-
import { CliUsageError } from "./
|
|
6
|
+
import { CliUsageError } from "./errors.mjs";
|
|
7
7
|
import { validHttpDeliveryMetadata, validPagePublicationMetadata, } from "../lib/homepage-metadata-contract.mjs";
|
|
8
8
|
import { registrarStandingObservedBy, resolveRegistrarIanaId, validRegistrarStanding, } from "../lib/registrar-standing-contract.mjs";
|
|
9
9
|
import { normalizeExplicitIsoTimestamp } from "../packages/evidence/observation.mjs";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
|
|
2
|
+
import { reviewSelectedInput } from "../packages/investigation/selected-input-review.mjs";
|
|
3
|
+
import { assertMessageBytes } from "../packages/investigation/intake-report.mjs";
|
|
4
|
+
import { DOCUMENT_REVIEW_DEADLINE_MS } from "../packages/contracts/document-intake.mjs";
|
|
5
|
+
import { MESSAGE_INTAKE_SCHEMA, MESSAGE_INTAKE_VERSION } from "../packages/contracts/message-intake.mjs";
|
|
6
|
+
if (!isMainThread && parentPort) {
|
|
7
|
+
const port = parentPort, request = workerData;
|
|
8
|
+
try {
|
|
9
|
+
port.postMessage({ result: await reviewSelectedInput(request.bytes, request.kind, request.reviewedAt) });
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
port.postMessage({ error: true });
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
if (request.bytes instanceof Uint8Array)
|
|
16
|
+
request.bytes.fill(0);
|
|
17
|
+
port.close();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** A disposable worker makes synchronous parser work cancellable, not a sandbox. */
|
|
21
|
+
export async function reviewSelectedInputInWorker(bytes, kind, reviewedAt, signal) {
|
|
22
|
+
assertMessageBytes(bytes);
|
|
23
|
+
signal?.throwIfAborted();
|
|
24
|
+
// Buffer.slice() can retain a pooled, non-transferable backing store.
|
|
25
|
+
const copy = Uint8Array.from(bytes);
|
|
26
|
+
const worker = new Worker(new URL(import.meta.url), { workerData: { bytes: copy, kind, reviewedAt },
|
|
27
|
+
transferList: [copy.buffer], env: {}, execArgv: [], stdout: true, stderr: true,
|
|
28
|
+
resourceLimits: { maxOldGenerationSizeMb: 512, maxYoungGenerationSizeMb: 32, stackSizeMb: 4 } });
|
|
29
|
+
worker.stdout.resume();
|
|
30
|
+
worker.stderr.resume();
|
|
31
|
+
try {
|
|
32
|
+
return await new Promise((resolve, reject) => {
|
|
33
|
+
let settled = false;
|
|
34
|
+
const finish = (value, cause) => {
|
|
35
|
+
if (settled)
|
|
36
|
+
return;
|
|
37
|
+
settled = true;
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
signal?.removeEventListener('abort', abort);
|
|
40
|
+
if (value)
|
|
41
|
+
resolve(value);
|
|
42
|
+
else
|
|
43
|
+
reject(cause ?? new TypeError('The selected input could not be reviewed within its format and processing bounds.'));
|
|
44
|
+
};
|
|
45
|
+
const abort = () => finish(undefined, new Error('Selected input review was cancelled. Nothing was saved.'));
|
|
46
|
+
const timer = setTimeout(() => finish(undefined, new Error('Selected input review exceeded its processing deadline. Nothing was saved.')), DOCUMENT_REVIEW_DEADLINE_MS);
|
|
47
|
+
worker.once('message', (reply) => {
|
|
48
|
+
if (reply && typeof reply === 'object' && 'result' in reply && reply.result?.report.schema === MESSAGE_INTAKE_SCHEMA && reply.result.report.schemaVersion === MESSAGE_INTAKE_VERSION)
|
|
49
|
+
finish(reply.result);
|
|
50
|
+
else
|
|
51
|
+
finish();
|
|
52
|
+
});
|
|
53
|
+
worker.once('error', () => finish());
|
|
54
|
+
worker.once('exit', () => finish());
|
|
55
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
56
|
+
if (signal?.aborted)
|
|
57
|
+
abort();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
await worker.terminate();
|
|
62
|
+
}
|
|
63
|
+
}
|