@slicedearth/whoisleuth-cli 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DISCLOSURE +1 -1
- package/NOTICE +2 -2
- package/cli/argument-values.mjs +55 -0
- package/cli/arguments.mjs +24 -470
- package/cli/artifact-validation/case-response.mjs +67 -9
- package/cli/artifact-validation/lookup-evidence.mjs +18 -4
- package/cli/artifact-validation/signed-review.mjs +61 -64
- package/cli/artifact-verify.mjs +11 -2
- package/cli/assurance-arguments.mjs +56 -0
- package/cli/assurance-command-definitions.mjs +179 -0
- package/cli/assurance-command-runner.mjs +1 -1
- package/cli/bounded-json.mjs +1 -1
- package/cli/bulk-checkpoint.mjs +1 -1
- package/cli/bulk-command-runner.mjs +6 -8
- package/cli/bulk.mjs +2 -3
- package/cli/case-command.mjs +99 -6
- package/cli/case-pack.mjs +19 -4
- 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 -210
- package/cli/command-definition.mjs +330 -0
- package/cli/command-reference.mjs +55 -1388
- package/cli/command-seeds.mjs +18 -0
- package/cli/compare.mjs +1 -1
- package/cli/config-profile.mjs +1 -1
- package/cli/ct-event-intake.mjs +4 -4
- package/cli/discover.mjs +2 -2
- package/cli/discovery-observation-snapshot.mjs +2 -2
- package/cli/discovery-scan-command-runner.mjs +7 -9
- package/cli/discovery-scan.mjs +1 -1
- package/cli/discovery-snapshot.mjs +2 -2
- package/cli/discovery-workflow.mjs +4 -4
- package/cli/domain-control-monitor.mjs +6 -6
- package/cli/domain-control-observations.mjs +2 -1
- package/cli/domain-feed.mjs +61 -0
- package/cli/errors.mjs +5 -6
- package/cli/evidence-arguments.mjs +46 -0
- package/cli/evidence-command-definitions.mjs +69 -0
- package/cli/evidence-command-runner.mjs +2 -3
- package/cli/evidence-signing.mjs +3 -2
- package/cli/external-observation-mapping.mjs +2 -2
- package/cli/formatters/json.mjs +1 -13
- package/cli/formatters/terminal-command-formats.mjs +6 -0
- package/cli/formatters/terminal-lookup.mjs +6 -0
- package/cli/history-arguments.mjs +39 -0
- package/cli/history-command-definitions.mjs +88 -0
- package/cli/indicator-set-command.mjs +32 -0
- package/cli/inline-command-families.mjs +6 -47
- package/cli/inline-command-runner.mjs +4 -31
- package/cli/input.mjs +2 -2
- package/cli/intake-command.mjs +147 -0
- package/cli/interchange-report.mjs +4 -2
- package/cli/investigation-artifacts.mjs +3 -4
- package/cli/investigation-package-review.mjs +2 -0
- package/cli/investigation-plan.mjs +32 -154
- package/cli/investigation-recipes.mjs +177 -0
- package/cli/investigation-run.mjs +78 -12
- package/cli/local-mmdb-review.mjs +220 -17
- package/cli/lookalike-calibration.mjs +1 -1
- package/cli/lookup-brief.mjs +2 -2
- package/cli/lookup-command-runner.mjs +19 -35
- package/cli/lookup-diff.mjs +10 -9
- package/cli/lookup-plan.mjs +10 -14
- package/cli/lookup-reconcile.mjs +6 -5
- package/cli/lookup-timeline.mjs +7 -6
- package/cli/mail-header-review.mjs +24 -247
- package/cli/mail-review.mjs +9 -7
- package/cli/network-arguments.mjs +51 -0
- package/cli/network-command-definitions.mjs +130 -0
- package/cli/network-command-runner.mjs +43 -31
- package/cli/offline-evidence-review.mjs +85 -6
- package/cli/posture.mjs +1 -1
- package/cli/registry-fixture-scaffold.mjs +1 -1
- package/cli/retained-artifact-diff.mjs +8 -7
- package/cli/review-arguments.mjs +132 -0
- package/cli/review-command-definitions.mjs +279 -0
- package/cli/review-command-runner.mjs +72 -159
- package/cli/risk-calibration.mjs +14 -12
- package/cli/runner.mjs +20 -32
- package/cli/saved-lookup.mjs +2 -2
- package/cli/selected-input-worker.mjs +73 -0
- package/cli/sharing-review.mjs +5 -64
- package/cli/source-reliability.mjs +2 -2
- package/cli/support-arguments.mjs +49 -0
- package/cli/support-command-definitions.mjs +209 -0
- package/cli/support-command-runner.mjs +4 -4
- package/cli/watchlist-review.mjs +43 -0
- package/cli/workflow-arguments.mjs +100 -0
- package/cli/workflow-command-definitions.mjs +103 -0
- package/cli/workflow-command-runner.mjs +40 -1
- package/docs/cli-reference.md +27 -0
- package/docs/cli.md +322 -4
- package/docs/contextual-reviews.md +178 -0
- package/docs/infrastructure-observations.md +126 -0
- package/docs/offline-intake-context.md +213 -0
- package/lib/analyst-taxonomy.mjs +2 -40
- package/lib/application-version.mjs +1 -1
- package/lib/availability.mjs +13 -789
- package/lib/bounded-contract-normalizers.mjs +4 -0
- package/lib/bounded-json.mjs +2 -330
- package/lib/browser-library-profile.mjs +62 -84
- package/lib/candidate-provenance-bounds.mjs +2 -0
- package/lib/classify.mjs +1 -1
- package/lib/credential-form-attribution.mjs +136 -0
- package/lib/credential-surface-profile.mjs +6 -2
- package/lib/ct-query.mjs +2 -27
- package/lib/ct-response-bounds.mjs +2 -9
- package/lib/ct-search.mjs +83 -47
- package/lib/dmarc-discovery.mjs +62 -0
- package/lib/dns-convergence-review.mjs +21 -8
- package/lib/dns-delegation-health.mjs +35 -15
- package/lib/dns-inheritance-review.mjs +53 -63
- package/lib/dns-intelligence.mjs +27 -11
- package/lib/dns-query-coverage.mjs +40 -0
- package/lib/domain-assurance.mjs +1 -1
- package/lib/domain-change-packet.mjs +12 -13
- package/lib/domain-change-review.mjs +32 -14
- package/lib/domain-control-flight-recorder.mjs +103 -27
- package/lib/domain-control-manifest.mjs +25 -17
- package/lib/domain-posture-analysis.mjs +75 -7
- package/lib/domain-posture-parsers.mjs +9 -0
- package/lib/domain-posture.mjs +45 -25
- package/lib/domain-registration.mjs +392 -0
- package/lib/evidence-export-privacy.mjs +27 -17
- package/lib/evidence-export.mjs +3 -3
- package/lib/evidence-report.mjs +5 -1
- package/lib/external-intelligence-risk.mjs +2 -124
- package/lib/favicon.mjs +13 -4
- package/lib/generated/cisa-kev-catalog.mjs +41 -3
- package/lib/generated/registrar-standing-catalogue.mjs +18 -10
- package/lib/generated/retire-browser-catalog.mjs +332 -19
- package/lib/generated/{unicode-confusables-17.mjs → unicode-confusables.mjs} +33 -33
- package/lib/html-signals.mjs +13 -2
- package/lib/http-evidence-bounds.mjs +2 -8
- package/lib/http-intelligence.mjs +13 -1
- 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-child-profile-contract.mjs +28 -11
- package/lib/lookup-contract-primitives.mjs +1 -1
- package/lib/lookup-diagnostics.mjs +1 -23
- package/lib/lookup-network-evidence-bounds.mjs +1 -10
- package/lib/lookup-progress.mjs +50 -1
- package/lib/lookup-response-contract.mjs +70 -40
- package/lib/lookup-response.mjs +5 -15
- package/lib/lookup-source-progress.mjs +8 -22
- package/lib/lookup.mjs +1 -1
- package/lib/operation-budget.mjs +4 -0
- package/lib/opportunity-scoring.mjs +2 -107
- package/lib/outbound-identity.mjs +1 -1
- package/lib/page-fingerprints.mjs +18 -1
- package/lib/perceptual-hash-comparison.mjs +2 -34
- package/lib/perceptual-hash.mjs +36 -10
- package/lib/portable-generator.mjs +2 -29
- package/lib/project-metadata.mjs +2 -8
- package/lib/rdap-client.mjs +11 -7
- package/lib/registrable-domain.mjs +2 -56
- package/lib/registrar-standing-contract.mjs +1 -1
- package/lib/registrar-standing.mjs +9 -13
- package/lib/registry-capabilities.mjs +2 -2
- package/lib/registry-capability-catalogue.mjs +27 -19
- package/lib/risk-calibration-summary.mjs +2 -352
- package/lib/risk-scoring.mjs +2 -345
- package/lib/scoring-evidence-quality.mjs +2 -107
- package/lib/semantic-version.mjs +2 -54
- package/lib/service-binding-dns.mjs +46 -2
- package/lib/smtp-transport-review.mjs +48 -19
- package/lib/sslbl-certificates.generated.mjs +142 -139
- package/lib/static-html-analysis.mjs +32 -4
- package/lib/structured-data-identity.mjs +1 -1
- package/lib/technology-evidence-role.mjs +1 -51
- package/lib/threat-intelligence-manifests.mjs +1 -1
- package/lib/threat-intelligence-runtime.mjs +1 -1
- package/lib/threat-intelligence-targets.mjs +1 -1
- package/lib/threat-intelligence-types.mjs +2 -75
- package/lib/tls-intelligence.mjs +30 -5
- package/lib/typosquat-generator.mjs +11 -2
- package/lib/website-enrichment.mjs +404 -0
- package/lib/whois-authority.mjs +43 -5
- package/lib/whois-chain.mjs +12 -3
- package/lib/whois-common-formats.mjs +3 -1
- package/lib/whois-contacts.mjs +13 -2
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-parser.mjs +5 -3
- package/package.json +15 -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 +348 -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-containment-handoff.mjs +195 -0
- package/packages/cases/case-evidence-links.mjs +151 -0
- package/packages/cases/case-evidence-model.mjs +89 -12
- package/packages/cases/case-evidence-quality.mjs +5 -0
- package/packages/cases/case-export-input.mjs +7 -5
- package/packages/cases/case-incident-context.mjs +52 -0
- package/packages/cases/case-markdown.mjs +11 -0
- package/packages/cases/case-migration-model.mjs +40 -24
- package/packages/cases/case-object-coverage.mjs +33 -0
- package/packages/cases/case-packet-correction.mjs +106 -0
- package/packages/cases/case-recheck-model.mjs +124 -36
- package/packages/cases/case-record-contracts.mjs +1 -1
- package/packages/cases/case-record-core.mjs +1 -1
- package/packages/cases/case-record-operations.mjs +111 -118
- package/packages/cases/case-record-projection.mjs +23 -4
- package/packages/cases/case-record-version-input.mjs +41 -0
- package/packages/cases/case-report.mjs +93 -27
- package/packages/cases/case-requested-evidence.mjs +146 -0
- package/packages/cases/case-response-actions.mjs +159 -1
- package/packages/cases/case-response-model.mjs +16 -1
- package/packages/cases/case-response-object.mjs +119 -0
- package/packages/cases/case-response-outcomes.mjs +108 -20
- package/packages/cases/case-response-packet-render.mjs +35 -12
- package/packages/cases/case-response-packet.mjs +55 -13
- package/packages/cases/case-response-records.mjs +0 -1
- package/packages/cases/case-response-review-inputs.mjs +60 -8
- package/packages/cases/case-storage-model.mjs +1 -1
- package/packages/cases/case-workflow-metadata.mjs +217 -0
- package/packages/cases/case-workflow-migration.mjs +43 -0
- package/packages/cases/http-summary.mjs +6 -1
- package/packages/comparison/favicon-similarity.mjs +1 -1
- package/packages/comparison/page-similarity.mjs +1 -1
- package/packages/comparison/relationship-evidence.mjs +21 -9
- package/packages/comparison/relationship-provenance.mjs +7 -1
- package/packages/contracts/analyst-interchange.mjs +35 -15
- package/packages/contracts/candidate-watch-review.mjs +54 -0
- package/packages/contracts/case-portability.mjs +360 -49
- package/packages/contracts/cli-command-semantics.mjs +5 -0
- package/packages/contracts/cli-grammar.mjs +6 -0
- package/packages/contracts/cli-lookup-brief.mjs +2 -0
- package/packages/contracts/context-review.mjs +25 -0
- package/packages/contracts/document-intake.mjs +11 -0
- package/packages/contracts/domain-control-flight-recorder.mjs +30 -14
- package/packages/contracts/domain-control-manifest.mjs +1 -1
- package/packages/contracts/domain-control-monitor.mjs +13 -2
- package/packages/contracts/domain-control-review.mjs +36 -15
- package/packages/contracts/external-observation-interchange.mjs +96 -28
- package/packages/contracts/har-review.mjs +2 -0
- package/packages/contracts/identity-events.mjs +4 -0
- package/packages/contracts/investigation-run.mjs +2 -0
- package/packages/contracts/ip-address.mjs +10 -0
- package/packages/contracts/lookup-evidence.mjs +3 -2
- package/packages/contracts/mail-authentication.mjs +5 -0
- package/packages/contracts/message-intake.mjs +37 -0
- package/packages/contracts/offline-comparison.mjs +4 -1
- package/packages/contracts/passive-mail.mjs +10 -0
- package/packages/contracts/risk-calibration.mjs +44 -29
- package/packages/contracts/schema-lifecycle.mjs +3 -1
- package/packages/contracts/web-capture.mjs +2 -2
- package/packages/contracts/workspace-portability.mjs +144 -28
- package/packages/evidence/collection-quality.mjs +67 -0
- package/packages/evidence/credential-form-attribution.mjs +60 -0
- package/packages/evidence/dns-query-outcome.mjs +72 -0
- package/packages/evidence/email-recipient.mjs +18 -0
- package/packages/evidence/image-regions.mjs +15 -0
- package/packages/evidence/lookup-target.mjs +5 -1
- package/packages/evidence/observation.mjs +2 -0
- package/packages/evidence/png-pixels.mjs +117 -0
- package/packages/evidence/public-address-policy.mjs +83 -0
- package/packages/evidence/record-identity.mjs +60 -16
- package/packages/evidence/technology-evidence-role.mjs +51 -0
- package/packages/interchange/defensive-indicator-export.mjs +264 -0
- package/packages/interchange/external-findings-import.mjs +36 -29
- package/packages/interchange/json-output.mjs +14 -0
- package/packages/interchange/managed-indicator-export.mjs +36 -0
- package/packages/interchange/managed-indicator-set.mjs +223 -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 +22 -15
- package/packages/investigation/acquisition-decision-packet.mjs +79 -1
- package/packages/investigation/bulk-mail-exposure.mjs +7 -13
- 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 +87 -0
- package/packages/investigation/docx-intake.mjs +133 -0
- package/packages/investigation/domain-history-review.mjs +132 -0
- package/packages/investigation/domain-transition-review.mjs +49 -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/infrastructure-observation.mjs +234 -0
- package/packages/investigation/intake-context.mjs +284 -0
- package/packages/investigation/intake-indicators.mjs +116 -0
- package/packages/investigation/intake-phones.mjs +155 -0
- package/packages/investigation/intake-report.mjs +20 -0
- package/packages/investigation/investigation-manifest.mjs +35 -9
- package/packages/investigation/link-intake.mjs +209 -0
- package/packages/investigation/lookup-asset-graph.mjs +1 -1
- package/packages/investigation/mail-authentication-review.mjs +222 -0
- package/packages/investigation/mail-header-identity.mjs +209 -0
- package/packages/investigation/message-intake.mjs +235 -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/domain-feed.mjs +321 -0
- package/packages/workspace/brand-candidate-workflow.mjs +351 -0
- package/packages/workspace/brand-keyword-campaign.mjs +231 -0
- package/packages/workspace/brand-profile-model.mjs +48 -4
- package/packages/workspace/bulk-review-model.mjs +28 -26
- package/packages/workspace/bulk-session-model.mjs +27 -13
- package/packages/workspace/campaign-model.mjs +1 -1
- package/packages/workspace/candidate-watch-handoff.mjs +195 -0
- package/packages/workspace/detection-rule-model.mjs +11 -3
- package/packages/workspace/hostile-input.mjs +1 -1
- package/packages/workspace/investigation-guide.mjs +2 -2
- package/packages/workspace/investigation-template-model.mjs +26 -2
- package/packages/workspace/page-baseline.mjs +2 -2
- package/packages/workspace/relationship-observation-model.mjs +17 -13
- package/packages/workspace/shortlist-model.mjs +24 -7
- package/packages/workspace/watchlist-history.mjs +59 -16
- package/packages/workspace/watchlist-store.mjs +152 -11
- package/packages/workspace/website-snapshot-model.mjs +28 -3
- package/packages/workspace/workspace-archive-crypto.mjs +1 -1
- package/packages/workspace/workspace-archive.mjs +5 -4
- package/third-party-notices.txt +549 -16
- package/lib/hostname.mjs +0 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { runDiscriminatedCommandHandler, } from "./discriminated-command-handlers.mjs";
|
|
1
2
|
import { fetchHomepage } from "../lib/availability.mjs";
|
|
2
3
|
import { searchCertificateTransparency } from "../lib/ct-search.mjs";
|
|
3
|
-
import { checkDomainPosture, normalizeAuditDomain, normalizeDkimSelectors } from "../lib/domain-posture.mjs";
|
|
4
|
+
import { checkDomainPosture, normalizeAuditDomain, normalizeDkimSelectors, } from "../lib/domain-posture.mjs";
|
|
4
5
|
import { collectTlsIntelligence, normalizeTlsHostname } from "../lib/tls-intelligence.mjs";
|
|
5
6
|
import { MAX_DNSSEC_TRUST_ANCHOR_BYTES, formatDnssecChainReport, validateDnssecChain, } from "../lib/dnssec-chain-validation.mjs";
|
|
6
7
|
import { MAX_MAIL_TRANSPORT_INPUT_BYTES, collectMailTransportReview, formatMailTransportReview, } from "../lib/smtp-transport-review.mjs";
|
|
@@ -11,9 +12,10 @@ import { buildHttpProbeResult } from "./http.mjs";
|
|
|
11
12
|
import { normalizePostureSelectors } from "./posture.mjs";
|
|
12
13
|
import EXIT_CODES from "./exit-codes.mjs";
|
|
13
14
|
import { buildPostureSarif } from "./ci-report.mjs";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const networkCommandHandlers = {
|
|
16
|
+
'ct-search': async (args, dependencies, context) => {
|
|
17
|
+
context.setFailureLabel('Certificate Transparency search');
|
|
18
|
+
const keyword = args.keyword || (await context.readSingleInput());
|
|
17
19
|
if (!keyword)
|
|
18
20
|
throw new CliUsageError('ct-search requires one keyword as an argument or on stdin.');
|
|
19
21
|
const search = dependencies.searchCertificateTransparency || searchCertificateTransparency;
|
|
@@ -25,9 +27,10 @@ async function runNetworkCommand(args, dependencies, context) {
|
|
|
25
27
|
: context.terminal(formatTerminalCtSearch(document), args.color));
|
|
26
28
|
}
|
|
27
29
|
return EXIT_CODES.SUCCESS;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
},
|
|
31
|
+
posture: async (args, dependencies, context) => {
|
|
32
|
+
context.setFailureLabel('Domain posture audit');
|
|
33
|
+
const requestedDomain = args.domain || (await context.readSingleInput());
|
|
31
34
|
if (!requestedDomain)
|
|
32
35
|
throw new CliUsageError('posture requires one domain as an argument or on stdin.');
|
|
33
36
|
const normalizeDomain = dependencies.normalizeAuditDomain || normalizeAuditDomain;
|
|
@@ -56,9 +59,10 @@ async function runNetworkCommand(args, dependencies, context) {
|
|
|
56
59
|
: context.terminal(formatTerminalPosture(document), args.color));
|
|
57
60
|
}
|
|
58
61
|
return EXIT_CODES.SUCCESS;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
},
|
|
63
|
+
http: async (args, dependencies, context) => {
|
|
64
|
+
context.setFailureLabel('HTTP probe');
|
|
65
|
+
const requestedDomain = args.domain || (await context.readSingleInput());
|
|
62
66
|
if (!requestedDomain)
|
|
63
67
|
throw new CliUsageError('http requires one domain as an argument or on stdin.');
|
|
64
68
|
const normalizeDomain = dependencies.normalizeAuditDomain || normalizeAuditDomain;
|
|
@@ -74,8 +78,9 @@ async function runNetworkCommand(args, dependencies, context) {
|
|
|
74
78
|
: context.terminal(formatTerminalHttp(document), args.color));
|
|
75
79
|
}
|
|
76
80
|
return EXIT_CODES.SUCCESS;
|
|
77
|
-
}
|
|
78
|
-
|
|
81
|
+
},
|
|
82
|
+
'dnssec-validate': async (args, dependencies, context) => {
|
|
83
|
+
context.setFailureLabel('DNSSEC chain validation');
|
|
79
84
|
let anchorInput;
|
|
80
85
|
try {
|
|
81
86
|
anchorInput = dependencies.readTrustAnchorInput
|
|
@@ -103,8 +108,9 @@ async function runNetworkCommand(args, dependencies, context) {
|
|
|
103
108
|
return report.state === 'secure' || report.state === 'insecure'
|
|
104
109
|
? EXIT_CODES.SUCCESS
|
|
105
110
|
: EXIT_CODES.PARTIAL_FAILURE;
|
|
106
|
-
}
|
|
107
|
-
|
|
111
|
+
},
|
|
112
|
+
'mail-transport': async (args, dependencies, context) => {
|
|
113
|
+
context.setFailureLabel('Mail transport review');
|
|
108
114
|
let input;
|
|
109
115
|
let anchorInput;
|
|
110
116
|
try {
|
|
@@ -150,22 +156,28 @@ async function runNetworkCommand(args, dependencies, context) {
|
|
|
150
156
|
: context.terminal(formatMailTransportReview(review), args.color));
|
|
151
157
|
}
|
|
152
158
|
return review.runState === 'complete' ? EXIT_CODES.SUCCESS : EXIT_CODES.PARTIAL_FAILURE;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
context.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
159
|
+
},
|
|
160
|
+
tls: async (args, dependencies, context) => {
|
|
161
|
+
context.setFailureLabel('TLS evidence collection');
|
|
162
|
+
const requestedHostname = args.hostname || (await context.readSingleInput());
|
|
163
|
+
if (!requestedHostname)
|
|
164
|
+
throw new CliUsageError('tls requires one hostname as an argument or on stdin.');
|
|
165
|
+
const normalizeHostname = dependencies.normalizeTlsHostname || normalizeTlsHostname;
|
|
166
|
+
const hostname = normalizeHostname(requestedHostname);
|
|
167
|
+
if (!hostname)
|
|
168
|
+
throw new CliUsageError('tls requires a valid DNS hostname, not an IP address.');
|
|
169
|
+
const collect = dependencies.collectTlsIntelligence || collectTlsIntelligence;
|
|
170
|
+
const result = await context.withProgress('Inspecting the current TLS connection', () => collect(hostname));
|
|
171
|
+
const document = buildCliTlsDocument(requestedHostname, result, context.now());
|
|
172
|
+
if (!args.quiet) {
|
|
173
|
+
context.writeStdout(args.output === 'json'
|
|
174
|
+
? formatJsonDocument(document)
|
|
175
|
+
: context.terminal(formatTerminalTls(document), args.color));
|
|
176
|
+
}
|
|
177
|
+
return EXIT_CODES.SUCCESS;
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
async function runNetworkCommand(args, dependencies, context) {
|
|
181
|
+
return runDiscriminatedCommandHandler(networkCommandHandlers, args, dependencies, context);
|
|
170
182
|
}
|
|
171
183
|
export { runNetworkCommand };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
|
-
import { scanBoundedJson } from "../
|
|
2
|
+
import { scanBoundedJson } from "../packages/analysis/bounded-json.mjs";
|
|
3
|
+
import { CONTEXT_INPUT_SCHEMAS, reviewContextInput } from "../packages/investigation/context-review.mjs";
|
|
4
|
+
import { CONTEXT_REVIEW_KINDS, DOMAIN_HISTORY_INPUT_SCHEMA, DOMAIN_HISTORY_INPUT_VERSION } from "../packages/contracts/context-review.mjs";
|
|
3
5
|
import { normalizeEncryptedDnsAdapter, planEncryptedDnsQuery, } from "../lib/encrypted-dns-contract.mjs";
|
|
4
6
|
import { validateDnssecEvidence } from "../lib/dnssec-evidence-validation.mjs";
|
|
5
7
|
import { reviewDomainChange } from "../lib/domain-change-review.mjs";
|
|
@@ -15,7 +17,11 @@ import { reviewDnsConvergence } from "../lib/dns-convergence-review.mjs";
|
|
|
15
17
|
import { compareTrustStoreEvidence } from "../lib/trust-store-comparison.mjs";
|
|
16
18
|
import { CliUsageError } from "./errors.mjs";
|
|
17
19
|
import { safeTerminalValue } from "./formatters/terminal.mjs";
|
|
18
|
-
import { LOCAL_MMDB_QUERY_SCHEMA, reviewLocalMmdb } from "./local-mmdb-review.mjs";
|
|
20
|
+
import { LOCAL_MMDB_QUERY_SCHEMA, LOCAL_MMDB_QUERY_VERSION, LOCAL_MMDB_REVIEW_SCHEMA, LOCAL_MMDB_REVIEW_VERSION, reviewLocalMmdb } from "./local-mmdb-review.mjs";
|
|
21
|
+
import { INFRASTRUCTURE_OBSERVATION_SCHEMA, MAX_INFRASTRUCTURE_OBSERVATION_BYTES, infrastructureObservationFacts, compareInfrastructureObservations } from "../packages/investigation/infrastructure-observation.mjs";
|
|
22
|
+
import { exact } from "../packages/evidence/artifact-structure.mjs";
|
|
23
|
+
import { INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA, MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES } from "../packages/contracts/external-observation-interchange.mjs";
|
|
24
|
+
import { CASE_CONTAINMENT_INPUT_SCHEMA, readCaseContainmentInput, formatCaseContainmentHandoff } from "../packages/cases/case-containment-handoff.mjs";
|
|
19
25
|
const OFFLINE_EVIDENCE_REVIEW_SCHEMA = 'whoisleuth.cli.offline-evidence-review';
|
|
20
26
|
const OFFLINE_EVIDENCE_REVIEW_VERSION = 1;
|
|
21
27
|
const MAX_OFFLINE_EVIDENCE_INPUT_BYTES = 16 * 1024 * 1024;
|
|
@@ -43,16 +49,45 @@ function parseInput(value) {
|
|
|
43
49
|
throw new CliUsageError('Offline evidence review requires one valid bounded JSON document without duplicate keys.');
|
|
44
50
|
}
|
|
45
51
|
const document = record(parsed);
|
|
46
|
-
if (document.
|
|
52
|
+
if (typeof document.schema !== 'string' || document.version !== 1
|
|
53
|
+
&& !(document.schema === LOCAL_MMDB_QUERY_SCHEMA && document.version === LOCAL_MMDB_QUERY_VERSION)
|
|
54
|
+
&& !(document.schema === DOMAIN_HISTORY_INPUT_SCHEMA && document.version === DOMAIN_HISTORY_INPUT_VERSION)) {
|
|
47
55
|
throw new CliUsageError('Offline evidence review requires a supported versioned input schema.');
|
|
48
56
|
}
|
|
49
57
|
return document;
|
|
50
58
|
}
|
|
51
59
|
function buildOfflineEvidenceReview(value, generatedAt = new Date().toISOString()) {
|
|
52
60
|
const input = parseInput(value);
|
|
61
|
+
if (input.schema === LOCAL_MMDB_QUERY_SCHEMA)
|
|
62
|
+
throw new CliUsageError('Local MMDB review requires --mmdb <database-file>.');
|
|
53
63
|
let kind;
|
|
54
64
|
let result;
|
|
55
|
-
if (input.schema ===
|
|
65
|
+
if (input.schema === CASE_CONTAINMENT_INPUT_SCHEMA) {
|
|
66
|
+
kind = 'internal_containment';
|
|
67
|
+
result = readCaseContainmentInput(input, generatedAt);
|
|
68
|
+
}
|
|
69
|
+
else if (input.schema === INFRASTRUCTURE_OBSERVATION_SCHEMA) {
|
|
70
|
+
if (typeof value === 'string' && new TextEncoder().encode(value).byteLength > MAX_INFRASTRUCTURE_OBSERVATION_BYTES)
|
|
71
|
+
throw new TypeError('Infrastructure snapshot input exceeds its byte bound.');
|
|
72
|
+
kind = 'infrastructure';
|
|
73
|
+
const retained = infrastructureObservationFacts(input);
|
|
74
|
+
result = { ...retained, state: retained.snapshot.coverage.state === 'complete' && !retained.snapshot.coverage.truncated ? 'complete' : 'partial' };
|
|
75
|
+
}
|
|
76
|
+
else if (input.schema === INFRASTRUCTURE_COMPARISON_INPUT_SCHEMA) {
|
|
77
|
+
if (typeof value === 'string' && new TextEncoder().encode(value).byteLength > MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES)
|
|
78
|
+
throw new TypeError('Infrastructure comparison input exceeds its byte bound.');
|
|
79
|
+
const pair = exact(input, ['schema', 'version', 'earlier', 'later'], 'Infrastructure comparison input');
|
|
80
|
+
if (new TextEncoder().encode(JSON.stringify(pair)).byteLength > MAX_INFRASTRUCTURE_COMPARISON_INPUT_BYTES)
|
|
81
|
+
throw new TypeError('Infrastructure comparison input exceeds its byte bound.');
|
|
82
|
+
kind = 'infrastructure_comparison';
|
|
83
|
+
result = compareInfrastructureObservations(pair.earlier, pair.later);
|
|
84
|
+
}
|
|
85
|
+
else if (CONTEXT_INPUT_SCHEMAS.includes(input.schema)) {
|
|
86
|
+
const review = reviewContextInput(input, generatedAt);
|
|
87
|
+
kind = review.kind;
|
|
88
|
+
result = review;
|
|
89
|
+
}
|
|
90
|
+
else if (input.schema === RDAP_SEARCH_INPUT_SCHEMA) {
|
|
56
91
|
kind = 'rdap_search';
|
|
57
92
|
const request = record(input.request);
|
|
58
93
|
const help = normalizeRdapSearchHelp(input.help);
|
|
@@ -163,14 +198,19 @@ async function buildOfflineEvidenceReviewWithLocalResources(value, generatedAt =
|
|
|
163
198
|
version: OFFLINE_EVIDENCE_REVIEW_VERSION,
|
|
164
199
|
generatedAt,
|
|
165
200
|
kind: 'geoip',
|
|
166
|
-
result: await reviewLocalMmdb(input, options.mmdbPath),
|
|
201
|
+
result: await reviewLocalMmdb(input, options.mmdbPath, generatedAt, options.signal ? { signal: options.signal } : {}),
|
|
167
202
|
limitations: Object.freeze([
|
|
168
203
|
'The review is local and uses only the supplied query metadata and analyst-supplied database. It does not transmit the address or refresh the database.',
|
|
169
204
|
]),
|
|
170
205
|
});
|
|
171
206
|
}
|
|
172
207
|
function formatOfflineEvidenceReview(document) {
|
|
208
|
+
if (document.kind === 'internal_containment')
|
|
209
|
+
return formatCaseContainmentHandoff(document.result);
|
|
173
210
|
const result = record(document.result);
|
|
211
|
+
if (document.kind === 'geoip' && result.schema === LOCAL_MMDB_REVIEW_SCHEMA && result.version !== LOCAL_MMDB_REVIEW_VERSION) {
|
|
212
|
+
throw new CliUsageError('Offline evidence review does not support this MMDB result version.');
|
|
213
|
+
}
|
|
174
214
|
const gate = record(result.gate);
|
|
175
215
|
const counts = record(result.counts);
|
|
176
216
|
const count = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
@@ -191,7 +231,35 @@ function formatOfflineEvidenceReview(document) {
|
|
|
191
231
|
`Kind ${document.kind.replaceAll('_', ' ')}`,
|
|
192
232
|
`State ${state.replaceAll('_', ' ')}`,
|
|
193
233
|
];
|
|
194
|
-
if (document.kind === '
|
|
234
|
+
if (document.kind === 'infrastructure') {
|
|
235
|
+
const snapshot = record(result.snapshot), scope = record(snapshot.scope), coverage = record(snapshot.coverage);
|
|
236
|
+
lines.push(`Snapshot ${safeTerminalValue(snapshot.id, '')} · ${safeTerminalValue(snapshot.target, '')} · ${safeTerminalValue(snapshot.observedAt, '')}`, `Coverage ${safeTerminalValue(coverage.state, 'unknown')} · ${safeTerminalValue(coverage.detail, '')}`, `Selected hosts ${listLength(scope.hostnames)}`);
|
|
237
|
+
for (const item of Array.isArray(result.facts) ? result.facts : []) {
|
|
238
|
+
const row = record(item), source = record(row.source);
|
|
239
|
+
lines.push(`${safeTerminalValue(row.hostname, '')} · ${safeTerminalValue(row.family, '')}: ${safeTerminalValue(row.value, '')} · ${safeTerminalValue(source.name, '')} (${safeTerminalValue(source.evidenceClass, '')}) · ${safeTerminalValue(row.observedAt, '')}`);
|
|
240
|
+
}
|
|
241
|
+
lines.push('Wildcard patterns are not enumerated hosts. DNS failure, missing values and history do not establish current absence. No requests or watchlist changes were made.');
|
|
242
|
+
}
|
|
243
|
+
else if (document.kind === 'infrastructure_comparison') {
|
|
244
|
+
for (const item of Array.isArray(result.rows) ? result.rows : []) {
|
|
245
|
+
const row = record(item), source = record(row.source);
|
|
246
|
+
// Bound individual identities, not the combined label: a valid DNS query
|
|
247
|
+
// and owner may each occupy all 253 hostname characters.
|
|
248
|
+
const cohort = Object.entries(record(row.cohort)).map(([key, value]) => `${safeTerminalValue(key)}=${safeTerminalValue(value, '', 253)}`).join(' · ');
|
|
249
|
+
const times = (value) => Array.isArray(value) ? value.map(time => safeTerminalValue(time, '')).join(', ') : '';
|
|
250
|
+
lines.push(`${safeTerminalValue(row.hostname, '')} · ${safeTerminalValue(row.family, '')} · ${safeTerminalValue(source.name, '')} (${safeTerminalValue(source.evidenceClass, '')}) · ${safeTerminalValue(row.state, '')}`, ` Cohort: ${cohort}`, ` Earlier observations: ${times(row.beforeTimes)}; later observations: ${times(row.afterTimes)}`, ` ${safeTerminalValue(JSON.stringify(row.before), '')} → ${safeTerminalValue(JSON.stringify(row.after), '')}`, ` ${safeTerminalValue(row.detail, '')}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else if (CONTEXT_REVIEW_KINDS.includes(document.kind)) {
|
|
254
|
+
lines.push(safeTerminalValue(result.summary, ''));
|
|
255
|
+
for (const item of Array.isArray(result.observations) ? result.observations : []) {
|
|
256
|
+
const row = record(item);
|
|
257
|
+
lines.push('', `${safeTerminalValue(row.label, 'Observation')} [${safeTerminalValue(row.state, 'unknown')}]`, ` ${safeTerminalValue(row.detail, '')}`, ` Source: ${safeTerminalValue(row.source, '')} · ${safeTerminalValue(row.observedAt, 'time unknown')}`);
|
|
258
|
+
}
|
|
259
|
+
for (const step of Array.isArray(result.nextSteps) ? result.nextSteps : [])
|
|
260
|
+
lines.push(`Next: ${safeTerminalValue(step, '')}`);
|
|
261
|
+
}
|
|
262
|
+
else if (document.kind === 'rdap_search') {
|
|
195
263
|
const help = record(result.help);
|
|
196
264
|
const plan = record(result.plan);
|
|
197
265
|
const responseInspection = record(result.responseInspection);
|
|
@@ -200,6 +268,17 @@ function formatOfflineEvidenceReview(document) {
|
|
|
200
268
|
if (result.responseInspection !== null)
|
|
201
269
|
lines.push(`Result ${String(responseInspection.state ?? 'invalid').replaceAll('_', ' ')}`);
|
|
202
270
|
}
|
|
271
|
+
else if (document.kind === 'geoip' && result.schema === LOCAL_MMDB_REVIEW_SCHEMA && result.version === LOCAL_MMDB_REVIEW_VERSION) {
|
|
272
|
+
const database = record(result.database), metadata = record(database.metadata), binaryFormat = record(metadata.binaryFormat);
|
|
273
|
+
const freshness = record(result.freshness), policy = record(freshness.policy), source = record(result.source), match = record(result.match);
|
|
274
|
+
lines.push(`Outcome ${safeTerminalValue(result.reason, 'unknown').replaceAll('_', ' ')}`, `Coverage ${safeTerminalValue(result.completeness, 'unavailable')}`, `Database SHA-256 ${safeTerminalValue(database.sha256, 'unavailable')}`, `Database bytes ${safeTerminalValue(database.byteLength, 'unavailable')}`, `Intrinsic type ${safeTerminalValue(metadata.databaseType, 'unavailable')}`, `Built ${safeTerminalValue(metadata.builtAt, 'unavailable')}`, `Binary format ${safeTerminalValue(binaryFormat.major, '?')}.${safeTerminalValue(binaryFormat.minor, '?')} · IP version ${safeTerminalValue(metadata.ipVersion, '?')}`, `Freshness ${safeTerminalValue(freshness.state, 'unknown')} · checked ${safeTerminalValue(freshness.checkedAt, 'unknown')}`, `Age policy ${safeTerminalValue(policy.maxAgeDays, '?')} days · ${safeTerminalValue(policy.rationale, '')}`, `Declared source ${safeTerminalValue(source.label, 'unknown')} · version ${safeTerminalValue(source.version, 'unknown')} · licence ${safeTerminalValue(source.license, 'unknown')}`);
|
|
275
|
+
if (result.match !== null) {
|
|
276
|
+
lines.push(`Matched network ${safeTerminalValue(match.network, 'unknown')}`, `Country ${safeTerminalValue(match.countryCode, 'unknown')} · region ${safeTerminalValue(match.region, 'unknown')} · city ${safeTerminalValue(match.city, 'unknown')}`, `ASN ${safeTerminalValue(match.asn, 'unknown')} · network label ${safeTerminalValue(match.asName, 'unknown')}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else if (document.kind === 'geoip') {
|
|
280
|
+
lines.push('Intrinsic MMDB freshness is not assessed by this result. A version-2 MMDB query can assess a selected local database against an explicit age policy.');
|
|
281
|
+
}
|
|
203
282
|
else if (document.kind === 'zone_intent') {
|
|
204
283
|
const desired = record(result.desired);
|
|
205
284
|
lines.push(`Desired ${listLength(desired.records)}`);
|
package/cli/posture.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export function buildRegistryFixtureScaffold(profileIdInput, suffixInput, scenar
|
|
|
14
14
|
throw new TypeError('Scenario must be registered, not_found, or inconclusive.');
|
|
15
15
|
const matchingProfiles = registryCompatibilityMatrix().filter((item) => item.id === profileId);
|
|
16
16
|
if (!matchingProfiles.length)
|
|
17
|
-
throw new TypeError('The capability profile is not present in the registry catalogue.');
|
|
17
|
+
throw new TypeError('The capability profile is not present in the registry catalogue. Run registry-support <suffix> --json to find its profile ID.');
|
|
18
18
|
const profile = matchingProfiles.find((item) => item.suffixes.includes(suffix));
|
|
19
19
|
if (!profile)
|
|
20
20
|
throw new TypeError(`The capability profile does not cover .${suffix}.`);
|
|
@@ -9,6 +9,7 @@ import { parseBoundedJsonObject } from "./bounded-json.mjs";
|
|
|
9
9
|
import { CliUsageError } from "./errors.mjs";
|
|
10
10
|
import { buildCliLookupDiff, formatCliLookupDiff } from "./lookup-diff.mjs";
|
|
11
11
|
import { SAVED_LOOKUP_SCHEMA } from "./saved-lookup.mjs";
|
|
12
|
+
import { safeTerminalValue } from "./formatters/terminal-shared.mjs";
|
|
12
13
|
import { CLI_COMPARISON_LEDGER_SCHEMA, CLI_COMPARISON_LEDGER_VERSION, MAX_RETAINED_ARTIFACT_DIFF_BYTES, } from "../packages/contracts/offline-comparison.mjs";
|
|
13
14
|
export { CLI_COMPARISON_LEDGER_SCHEMA, CLI_COMPARISON_LEDGER_VERSION, MAX_RETAINED_ARTIFACT_DIFF_BYTES };
|
|
14
15
|
const BULK_ROOT_KEYS = new Set(['schema', 'version', 'generatedAt', 'sessions', 'limitations']);
|
|
@@ -354,22 +355,22 @@ export function formatCliRetainedArtifactDiff(document) {
|
|
|
354
355
|
return formatCliLookupDiff(document);
|
|
355
356
|
const lines = [
|
|
356
357
|
'Retained artifact comparison ledger',
|
|
357
|
-
`Family ${document.artifactFamily.replaceAll('_', ' ')}`,
|
|
358
|
-
`Left ${document.left.label} · ${document.left.id}`,
|
|
359
|
-
`Right ${document.right.label} · ${document.right.id}`,
|
|
358
|
+
`Family ${safeTerminalValue(document.artifactFamily).replaceAll('_', ' ')}`,
|
|
359
|
+
`Left ${safeTerminalValue(document.left.label)} · ${safeTerminalValue(document.left.id)}`,
|
|
360
|
+
`Right ${safeTerminalValue(document.right.label)} · ${safeTerminalValue(document.right.id)}`,
|
|
360
361
|
`Candidates ${document.index.counts.retained}`,
|
|
361
362
|
`Exact rows ${document.details.rows.length} of ${document.details.totalRows}`,
|
|
362
363
|
'',
|
|
363
364
|
];
|
|
364
365
|
for (const row of document.details.rows) {
|
|
365
|
-
lines.push(`${row.entityId} · ${row.field} [${row.state.replaceAll('_', ' ')}]`);
|
|
366
|
-
lines.push(` Earlier: ${row.earlier.value
|
|
367
|
-
lines.push(` Later: ${row.later.value
|
|
366
|
+
lines.push(`${safeTerminalValue(row.entityId)} · ${safeTerminalValue(row.field)} [${safeTerminalValue(row.state).replaceAll('_', ' ')}]`);
|
|
367
|
+
lines.push(` Earlier: ${safeTerminalValue(row.earlier.value, 'not retained')} · ${safeTerminalValue(row.earlier.sourceState)}`);
|
|
368
|
+
lines.push(` Later: ${safeTerminalValue(row.later.value, 'not retained')} · ${safeTerminalValue(row.later.sourceState)}`);
|
|
368
369
|
}
|
|
369
370
|
if (document.details.omissions.detailRows)
|
|
370
371
|
lines.push(`Omitted exact rows: ${document.details.omissions.detailRows}`);
|
|
371
372
|
lines.push('', 'Limitations:');
|
|
372
373
|
for (const limitation of document.limitations)
|
|
373
|
-
lines.push(` - ${limitation}`);
|
|
374
|
+
lines.push(` - ${safeTerminalValue(limitation)}`);
|
|
374
375
|
return `${lines.join('\n')}\n`;
|
|
375
376
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {} from "./command-argument-grammar.mjs";
|
|
2
|
+
import { CliUsageError } from "./errors.mjs";
|
|
3
|
+
import { CLI_CASE_OPERATIONS, CLI_INDICATOR_OPERATIONS } from "./command-reference.mjs";
|
|
4
|
+
import { terminalOptions, jsonOutput, parseTwoFileComparisonArguments, singleInputAction, } from "./argument-values.mjs";
|
|
5
|
+
function parseVerifyArtifactArguments(parsed) {
|
|
6
|
+
if (parsed.hasOption('--folder') && parsed.positionalValue('source'))
|
|
7
|
+
throw new CliUsageError('--folder selects its own input; do not also supply a file or stdin marker.');
|
|
8
|
+
return {
|
|
9
|
+
action: 'verify-artifact',
|
|
10
|
+
source: parsed.positionalValue('source'),
|
|
11
|
+
passphraseSource: parsed.optionValue('--passphrase-file'),
|
|
12
|
+
manifestSource: parsed.optionValue('--manifest'),
|
|
13
|
+
manifestEntryId: parsed.optionValue('--manifest-entry'),
|
|
14
|
+
...(parsed.hasOption('--package') ? { package: true } : {}),
|
|
15
|
+
...(parsed.hasOption('--bagit') ? { bagit: true } : {}),
|
|
16
|
+
...(parsed.optionValue('--folder') ? { folder: parsed.optionValue('--folder') } : {}),
|
|
17
|
+
output: jsonOutput(parsed),
|
|
18
|
+
strictExit: parsed.hasOption('--strict-exit'),
|
|
19
|
+
...terminalOptions(parsed),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function parseCaseArguments(parsed) {
|
|
23
|
+
const operation = parsed.positionalValue('operation');
|
|
24
|
+
const source = parsed.positionalValue('source');
|
|
25
|
+
const domain = parsed.optionValue('--domain');
|
|
26
|
+
const caseId = parsed.optionValue('--case-id');
|
|
27
|
+
const title = parsed.optionValue('--title');
|
|
28
|
+
const newIncident = parsed.hasOption('--new-incident');
|
|
29
|
+
const text = parsed.optionValue('--text');
|
|
30
|
+
const noteSource = parsed.optionValue('--note-file');
|
|
31
|
+
const inputSource = parsed.optionValue('--input');
|
|
32
|
+
const expectedFileDigest = parsed.optionValue('--expect-file-digest');
|
|
33
|
+
if (source === '-' || inputSource === '-' || noteSource === '-')
|
|
34
|
+
throw new CliUsageError('Case operations require selected files, not stdin.');
|
|
35
|
+
if (operation !== 'open' && !source)
|
|
36
|
+
throw new CliUsageError(`${operation} requires a Case file.`);
|
|
37
|
+
if (operation !== 'show' && !parsed.hasOption('--output'))
|
|
38
|
+
throw new CliUsageError('Case mutations require --output; nothing was changed.');
|
|
39
|
+
if (operation === 'open' && !domain)
|
|
40
|
+
throw new CliUsageError('case open requires --domain.');
|
|
41
|
+
if (operation !== 'open' && (title || newIncident))
|
|
42
|
+
throw new CliUsageError('--title and --new-incident belong to case open.');
|
|
43
|
+
if (newIncident && (!title || caseId))
|
|
44
|
+
throw new CliUsageError('--new-incident requires a distinguishing --title and cannot select an existing --case-id.');
|
|
45
|
+
if (operation === 'note' ? text === null && !noteSource : text !== null || noteSource !== null) {
|
|
46
|
+
throw new CliUsageError('Only case note accepts and requires --text or --note-file.');
|
|
47
|
+
}
|
|
48
|
+
if (['pin', 'link', 'withdraw-link', 'assess', 'recheck', 'incident-link', 'action', 'action-event', 'recheck-question', 'close-object'].includes(operation)
|
|
49
|
+
? !inputSource
|
|
50
|
+
: inputSource !== null) {
|
|
51
|
+
throw new CliUsageError('This Case mutation requires --input; show, open and note do not accept it.');
|
|
52
|
+
}
|
|
53
|
+
if (expectedFileDigest !== null && (!source || !/^sha256:[a-f0-9]{64}$/u.test(expectedFileDigest))) {
|
|
54
|
+
throw new CliUsageError('--expect-file-digest requires a source file and sha256:<64 lowercase hexadecimal characters>.');
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
action: 'case',
|
|
58
|
+
operation,
|
|
59
|
+
source,
|
|
60
|
+
caseId,
|
|
61
|
+
domain,
|
|
62
|
+
title,
|
|
63
|
+
newIncident,
|
|
64
|
+
text,
|
|
65
|
+
noteSource,
|
|
66
|
+
inputSource,
|
|
67
|
+
expectedFileDigest,
|
|
68
|
+
output: jsonOutput(parsed),
|
|
69
|
+
...terminalOptions(parsed),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export const REVIEW_ARGUMENT_PARSERS = Object.freeze({
|
|
73
|
+
'verify-artifact': parseVerifyArtifactArguments,
|
|
74
|
+
'interchange-report': (parsed) => ({
|
|
75
|
+
action: 'interchange-report',
|
|
76
|
+
source: parsed.positionalValue('source'),
|
|
77
|
+
passphraseSource: parsed.optionValue('--passphrase-file'),
|
|
78
|
+
output: jsonOutput(parsed),
|
|
79
|
+
...terminalOptions(parsed),
|
|
80
|
+
}),
|
|
81
|
+
'source-report': (parsed) => singleInputAction('source-report', parsed),
|
|
82
|
+
compare: (parsed) => singleInputAction('compare', parsed),
|
|
83
|
+
'page-compare': (parsed) => ({
|
|
84
|
+
action: 'page-compare',
|
|
85
|
+
...parseTwoFileComparisonArguments(parsed, 'page-compare'),
|
|
86
|
+
}),
|
|
87
|
+
'mail-review': (parsed) => singleInputAction('mail-review', parsed),
|
|
88
|
+
'mail-headers': (parsed) => ({
|
|
89
|
+
...singleInputAction('mail-headers', parsed),
|
|
90
|
+
...(parsed.hasOption('--trusted-auth-header')
|
|
91
|
+
? { trustedAuthHeaders: parsed.optionValues('--trusted-auth-header') }
|
|
92
|
+
: {}),
|
|
93
|
+
}),
|
|
94
|
+
intake: (parsed) => ({
|
|
95
|
+
action: 'intake',
|
|
96
|
+
kind: parsed.positionalValue('kind'),
|
|
97
|
+
reportedActions: parsed.optionValues('--reported-action'),
|
|
98
|
+
...(parsed.hasOption('--intake-context') ? { intakeContextSource: parsed.optionValue('--intake-context') } : {}),
|
|
99
|
+
...(parsed.hasOption('--trusted-auth-header')
|
|
100
|
+
? { trustedAuthHeaders: parsed.optionValues('--trusted-auth-header') }
|
|
101
|
+
: {}),
|
|
102
|
+
source: parsed.positionalValue('source'),
|
|
103
|
+
output: jsonOutput(parsed),
|
|
104
|
+
strictExit: parsed.hasOption('--strict-exit'),
|
|
105
|
+
...terminalOptions(parsed),
|
|
106
|
+
}),
|
|
107
|
+
'review-evidence': (parsed) => ({
|
|
108
|
+
action: 'review-evidence',
|
|
109
|
+
source: parsed.positionalValue('source'),
|
|
110
|
+
mmdbSource: parsed.optionValue('--mmdb'),
|
|
111
|
+
output: jsonOutput(parsed),
|
|
112
|
+
strictExit: parsed.hasOption('--strict-exit'),
|
|
113
|
+
...terminalOptions(parsed),
|
|
114
|
+
}),
|
|
115
|
+
brief: (parsed) => singleInputAction('brief', parsed),
|
|
116
|
+
case: parseCaseArguments,
|
|
117
|
+
'indicator-set': (parsed) => ({
|
|
118
|
+
action: 'indicator-set',
|
|
119
|
+
operation: parsed.positionalValue('operation'),
|
|
120
|
+
source: parsed.positionalValue('source'),
|
|
121
|
+
output: jsonOutput(parsed),
|
|
122
|
+
...terminalOptions(parsed),
|
|
123
|
+
}),
|
|
124
|
+
'case-pack': (parsed) => ({
|
|
125
|
+
action: 'case-pack',
|
|
126
|
+
source: parsed.positionalValue('source'),
|
|
127
|
+
output: jsonOutput(parsed),
|
|
128
|
+
audience: parsed.optionValue('--audience'),
|
|
129
|
+
reviewed: true,
|
|
130
|
+
...terminalOptions(parsed),
|
|
131
|
+
}),
|
|
132
|
+
});
|