@slicedearth/whoisleuth-cli 2.4.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 +2 -2
- package/cli/argument-values.mjs +55 -0
- package/cli/arguments.mjs +24 -470
- package/cli/artifact-validation/case-response.mjs +21 -6
- package/cli/artifact-validation/lookup-evidence.mjs +3 -3
- package/cli/artifact-validation/signed-review.mjs +59 -13
- package/cli/artifact-verify.mjs +2 -0
- 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 +2 -2
- package/cli/bulk.mjs +2 -3
- package/cli/case-command.mjs +20 -3
- package/cli/case-pack.mjs +12 -2
- 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 +329 -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 +3 -3
- 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/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/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 +33 -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 +107 -0
- package/cli/interchange-report.mjs +4 -2
- package/cli/investigation-artifacts.mjs +1 -1
- 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 +197 -17
- package/cli/lookalike-calibration.mjs +1 -1
- package/cli/lookup-brief.mjs +2 -2
- package/cli/lookup-command-runner.mjs +18 -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 +39 -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 +131 -0
- package/cli/review-command-definitions.mjs +273 -0
- package/cli/review-command-runner.mjs +68 -158
- package/cli/risk-calibration.mjs +8 -8
- package/cli/runner.mjs +20 -32
- package/cli/saved-lookup.mjs +2 -2
- package/cli/selected-input-worker.mjs +63 -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 +2 -2
- package/cli/workflow-arguments.mjs +86 -0
- package/cli/workflow-command-definitions.mjs +78 -0
- package/cli/workflow-command-runner.mjs +8 -1
- package/docs/cli-reference.md +27 -0
- package/docs/cli.md +223 -2
- package/docs/contextual-reviews.md +155 -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/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 +12 -8
- package/lib/dns-inheritance-review.mjs +53 -63
- package/lib/dns-intelligence.mjs +9 -7
- 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 +22 -5
- package/lib/domain-posture.mjs +16 -15
- package/lib/domain-registration.mjs +392 -0
- package/lib/evidence-export-privacy.mjs +22 -15
- package/lib/evidence-export.mjs +3 -3
- package/lib/evidence-report.mjs +4 -1
- package/lib/external-intelligence-risk.mjs +2 -124
- 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 +7 -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-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 +69 -40
- package/lib/lookup-response.mjs +5 -15
- package/lib/lookup-source-progress.mjs +8 -22
- package/lib/lookup.mjs +1 -1
- 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 +27 -0
- package/lib/smtp-transport-review.mjs +48 -19
- package/lib/sslbl-certificates.generated.mjs +141 -139
- package/lib/static-html-analysis.mjs +1 -1
- package/lib/structured-data-identity.mjs +1 -1
- 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/typosquat-generator.mjs +11 -2
- package/lib/website-enrichment.mjs +388 -0
- package/lib/whois-authority.mjs +43 -5
- package/lib/whois-normalization.mjs +3 -1
- package/lib/whois-parser.mjs +5 -3
- package/package.json +13 -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-evidence-links.mjs +135 -0
- package/packages/cases/case-evidence-model.mjs +78 -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-recheck-model.mjs +90 -32
- 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 +40 -112
- package/packages/cases/case-record-projection.mjs +8 -1
- package/packages/cases/case-record-version-input.mjs +19 -0
- package/packages/cases/case-report.mjs +63 -27
- package/packages/cases/case-requested-evidence.mjs +146 -0
- package/packages/cases/case-response-actions.mjs +103 -1
- package/packages/cases/case-response-model.mjs +9 -1
- package/packages/cases/case-response-outcomes.mjs +29 -8
- package/packages/cases/case-response-packet-render.mjs +13 -9
- package/packages/cases/case-response-packet.mjs +25 -11
- package/packages/cases/case-response-records.mjs +0 -1
- package/packages/cases/case-response-review-inputs.mjs +14 -3
- package/packages/cases/case-storage-model.mjs +1 -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/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 +28 -10
- package/packages/contracts/case-portability.mjs +118 -25
- package/packages/contracts/cli-command-semantics.mjs +2 -0
- package/packages/contracts/cli-grammar.mjs +6 -0
- package/packages/contracts/cli-lookup-brief.mjs +2 -0
- package/packages/contracts/context-review.mjs +24 -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/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/mail-authentication.mjs +5 -0
- package/packages/contracts/message-intake.mjs +31 -0
- package/packages/contracts/offline-comparison.mjs +4 -1
- package/packages/contracts/passive-mail.mjs +10 -0
- package/packages/contracts/web-capture.mjs +2 -2
- package/packages/contracts/workspace-portability.mjs +63 -17
- package/packages/evidence/collection-quality.mjs +67 -0
- package/packages/evidence/dns-query-outcome.mjs +72 -0
- package/packages/evidence/email-recipient.mjs +18 -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/interchange/defensive-indicator-export.mjs +264 -0
- package/packages/interchange/external-findings-import.mjs +4 -12
- 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 +22 -15
- package/packages/investigation/acquisition-decision-packet.mjs +1 -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 +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-manifest.mjs +2 -2
- package/packages/investigation/link-intake.mjs +179 -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 +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/workspace/brand-profile-model.mjs +2 -2
- package/packages/workspace/bulk-review-model.mjs +28 -26
- package/packages/workspace/bulk-session-model.mjs +23 -13
- package/packages/workspace/campaign-model.mjs +1 -1
- package/packages/workspace/detection-rule-model.mjs +1 -1
- 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 +14 -5
- package/packages/workspace/watchlist-history.mjs +28 -12
- package/packages/workspace/watchlist-store.mjs +17 -5
- 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
package/NOTICE
CHANGED
|
@@ -44,8 +44,8 @@ Shalini Chandra (2024), licensed under CC BY 4.0 without warranties. The source
|
|
|
44
44
|
and licence links, modifications and reproduction instructions are in that
|
|
45
45
|
directory's README.md. This dataset is not included in the website or CLI.
|
|
46
46
|
|
|
47
|
-
The generated Unicode confusable projection is derived from Unicode UTS #39
|
|
48
|
-
data file confusables.txt
|
|
47
|
+
The generated Unicode confusable projection is derived from the Unicode UTS #39
|
|
48
|
+
data file confusables.txt. Its source version and digest accompany the projection.
|
|
49
49
|
|
|
50
50
|
UNICODE LICENSE V3 COPYRIGHT AND PERMISSION NOTICE
|
|
51
51
|
Copyright © 1991-2026 Unicode, Inc.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {} from "./command-argument-grammar.mjs";
|
|
2
|
+
import { CliUsageError } from "./errors.mjs";
|
|
3
|
+
import { parseCliFailPolicies, } from "./fail-policy.mjs";
|
|
4
|
+
import { PRESENTATION_OPTIONS } from "./command-definition.mjs";
|
|
5
|
+
function terminalOptions(parsed) {
|
|
6
|
+
return { quiet: parsed.hasOption('--quiet'), color: !parsed.hasOption('--no-color') };
|
|
7
|
+
}
|
|
8
|
+
function jsonOutput(parsed) {
|
|
9
|
+
return parsed.hasOption('--json') ? 'json' : 'terminal';
|
|
10
|
+
}
|
|
11
|
+
function normalizedLabel(parsed, option, maximum) {
|
|
12
|
+
const value = parsed.optionValue(option);
|
|
13
|
+
if (value === null)
|
|
14
|
+
return null;
|
|
15
|
+
const normalized = value.replace(/\s+/gu, ' ').trim();
|
|
16
|
+
if (!normalized || normalized.length > maximum) {
|
|
17
|
+
throw new CliUsageError(`${option} requires a label of at most ${maximum} characters.`);
|
|
18
|
+
}
|
|
19
|
+
return normalized;
|
|
20
|
+
}
|
|
21
|
+
function failPolicies(parsed, command) {
|
|
22
|
+
const value = parsed.optionValue('--fail-on');
|
|
23
|
+
return value === null ? null : parseCliFailPolicies(value, command);
|
|
24
|
+
}
|
|
25
|
+
function parseOutput(parsed, definition) {
|
|
26
|
+
// Membership proves the returned literal belongs to this command's declared
|
|
27
|
+
// options. Grammar validation independently rejects conflicting selections.
|
|
28
|
+
return (PRESENTATION_OPTIONS.find(([option]) => definition.options.includes(option) && parsed.hasOption(option))?.[2] ?? 'terminal');
|
|
29
|
+
}
|
|
30
|
+
function parseTwoFileComparisonArguments(parsed, command) {
|
|
31
|
+
const sources = parsed.positionalValues('sources');
|
|
32
|
+
if (sources[0] === sources[1])
|
|
33
|
+
throw new CliUsageError(`${command} requires two different input files.`);
|
|
34
|
+
return {
|
|
35
|
+
leftSource: sources[0],
|
|
36
|
+
rightSource: sources[1],
|
|
37
|
+
output: jsonOutput(parsed),
|
|
38
|
+
...terminalOptions(parsed),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function uniqueSources(parsed, command) {
|
|
42
|
+
const sources = parsed.positionalValues('sources');
|
|
43
|
+
if (new Set(sources).size !== sources.length)
|
|
44
|
+
throw new CliUsageError(`${command} input files must be different.`);
|
|
45
|
+
return sources;
|
|
46
|
+
}
|
|
47
|
+
function singleInputAction(action, parsed, positionalName = 'source') {
|
|
48
|
+
return {
|
|
49
|
+
action,
|
|
50
|
+
[positionalName]: parsed.positionalValue(positionalName),
|
|
51
|
+
output: jsonOutput(parsed),
|
|
52
|
+
...terminalOptions(parsed),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export { terminalOptions, jsonOutput, normalizedLabel, failPolicies, parseOutput, parseTwoFileComparisonArguments, uniqueSources, singleInputAction, };
|
package/cli/arguments.mjs
CHANGED
|
@@ -1,482 +1,36 @@
|
|
|
1
1
|
import { parseCommandArguments } from "./command-argument-grammar.mjs";
|
|
2
2
|
import { CliUsageError, hasUnsafeCliText } from "./errors.mjs";
|
|
3
|
-
import { parseArchiveContentDigest } from "./archive-content-digest.mjs";
|
|
4
|
-
import { parseCliFailPolicies } from "./fail-policy.mjs";
|
|
5
3
|
import { isDirectLookupTarget } from "../lib/classify.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import { CLI_COMMANDS,
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import { MAX_CLI_ARGUMENTS, MAX_CLI_ARGUMENT_LENGTH } from "../packages/contracts/cli-grammar.mjs";
|
|
5
|
+
import { CLI_COMMANDS, cliMetaActionForInvocation, isCliCommand } from "./command-reference.mjs";
|
|
6
|
+
import { commandHasOwner } from "./command-reference.mjs";
|
|
7
|
+
import { SUPPORT_ARGUMENT_PARSERS } from "./support-arguments.mjs";
|
|
8
|
+
import { ASSURANCE_ARGUMENT_PARSERS } from "./assurance-arguments.mjs";
|
|
9
|
+
import { COLLECTION_ARGUMENT_PARSERS } from "./collection-arguments.mjs";
|
|
10
|
+
import { NETWORK_ARGUMENT_PARSERS } from "./network-arguments.mjs";
|
|
11
|
+
import { REVIEW_ARGUMENT_PARSERS } from "./review-arguments.mjs";
|
|
12
|
+
import { EVIDENCE_ARGUMENT_PARSERS } from "./evidence-arguments.mjs";
|
|
13
|
+
import { WORKFLOW_ARGUMENT_PARSERS } from "./workflow-arguments.mjs";
|
|
14
|
+
import { HISTORY_ARGUMENT_PARSERS } from "./history-arguments.mjs";
|
|
15
|
+
const CLI_PARSERS = Object.freeze({
|
|
16
|
+
...SUPPORT_ARGUMENT_PARSERS,
|
|
17
|
+
...ASSURANCE_ARGUMENT_PARSERS,
|
|
18
|
+
...COLLECTION_ARGUMENT_PARSERS,
|
|
19
|
+
...NETWORK_ARGUMENT_PARSERS,
|
|
20
|
+
...REVIEW_ARGUMENT_PARSERS,
|
|
21
|
+
...EVIDENCE_ARGUMENT_PARSERS,
|
|
22
|
+
...WORKFLOW_ARGUMENT_PARSERS,
|
|
23
|
+
...HISTORY_ARGUMENT_PARSERS,
|
|
24
|
+
});
|
|
25
|
+
export function isCliCommandForOwner(args, owner) {
|
|
26
|
+
return commandHasOwner(args.action, owner);
|
|
27
|
+
}
|
|
10
28
|
function boundedArgument(value) {
|
|
11
29
|
if (typeof value !== 'string' || value.length > MAX_CLI_ARGUMENT_LENGTH || hasUnsafeCliText(value)) {
|
|
12
30
|
throw new CliUsageError('Arguments must be bounded text without control characters.');
|
|
13
31
|
}
|
|
14
32
|
return value;
|
|
15
33
|
}
|
|
16
|
-
function terminalOptions(parsed) {
|
|
17
|
-
return { quiet: parsed.hasOption('--quiet'), color: !parsed.hasOption('--no-color') };
|
|
18
|
-
}
|
|
19
|
-
function parseInspectArchiveArguments(parsed) {
|
|
20
|
-
const expectedContentDigest = parsed.optionValue('--expect-content-digest');
|
|
21
|
-
try {
|
|
22
|
-
parseArchiveContentDigest(expectedContentDigest);
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
throw new CliUsageError('--expect-content-digest requires sha256:<64 lowercase hex> or sorted-json-v2:sha256:<64 lowercase hex>.');
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
action: 'inspect-archive',
|
|
29
|
-
source: parsed.positionalValue('source'),
|
|
30
|
-
passphraseSource: parsed.optionValue('--passphrase-file'),
|
|
31
|
-
search: parsed.optionValue('--search'),
|
|
32
|
-
reveal: parsed.hasOption('--reveal'),
|
|
33
|
-
requireMatch: parsed.hasOption('--require-match'),
|
|
34
|
-
expectedContentDigest,
|
|
35
|
-
output: parsed.hasOption('--json') ? 'json' : 'terminal',
|
|
36
|
-
...terminalOptions(parsed),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function parseSignArtifactArguments(parsed) {
|
|
40
|
-
return {
|
|
41
|
-
action: 'sign-artifact',
|
|
42
|
-
source: parsed.positionalValue('source'),
|
|
43
|
-
privateKeySource: parsed.optionValue('--private-key-file'),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
function parseVerifySignatureArguments(parsed) {
|
|
47
|
-
return {
|
|
48
|
-
action: 'verify-signature',
|
|
49
|
-
source: parsed.positionalValue('source'),
|
|
50
|
-
publicKeySource: parsed.optionValue('--public-key-file'),
|
|
51
|
-
trustStoreSource: parsed.optionValue('--trust-store-file'),
|
|
52
|
-
output: parsed.hasOption('--json') ? 'json' : 'terminal',
|
|
53
|
-
...terminalOptions(parsed),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function jsonOutput(parsed) {
|
|
57
|
-
return parsed.hasOption('--json') ? 'json' : 'terminal';
|
|
58
|
-
}
|
|
59
|
-
function normalizedLabel(parsed, option, maximum) {
|
|
60
|
-
const value = parsed.optionValue(option);
|
|
61
|
-
if (value === null)
|
|
62
|
-
return null;
|
|
63
|
-
const normalized = value.replace(/\s+/gu, ' ').trim();
|
|
64
|
-
if (!normalized || normalized.length > maximum) {
|
|
65
|
-
throw new CliUsageError(`${option} requires a label of at most ${maximum} characters.`);
|
|
66
|
-
}
|
|
67
|
-
return normalized;
|
|
68
|
-
}
|
|
69
|
-
function failPolicies(parsed, command) {
|
|
70
|
-
const value = parsed.optionValue('--fail-on');
|
|
71
|
-
return value === null ? null : parseCliFailPolicies(value, command);
|
|
72
|
-
}
|
|
73
|
-
function parseOutput(parsed, formats) {
|
|
74
|
-
return formats.find(([option]) => parsed.hasOption(option))?.[1] ?? 'terminal';
|
|
75
|
-
}
|
|
76
|
-
function singleInputAction(action, parsed, positionalName = 'source') {
|
|
77
|
-
return {
|
|
78
|
-
action,
|
|
79
|
-
[positionalName === 'source' ? 'source' : positionalName]: parsed.positionalValue(positionalName),
|
|
80
|
-
output: jsonOutput(parsed),
|
|
81
|
-
...terminalOptions(parsed),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
function parseLookupArguments(parsed) {
|
|
85
|
-
const output = parseOutput(parsed, [
|
|
86
|
-
['--json', 'json'], ['--junit', 'junit'], ['--markdown', 'markdown'], ['--html', 'html'],
|
|
87
|
-
]);
|
|
88
|
-
const selectedFailPolicies = failPolicies(parsed, 'lookup');
|
|
89
|
-
return {
|
|
90
|
-
action: 'lookup',
|
|
91
|
-
query: parsed.positionalValue('target'),
|
|
92
|
-
output,
|
|
93
|
-
deep: parsed.hasOption('--deep'),
|
|
94
|
-
detail: parsed.hasOption('--summary') ? 'summary' : parsed.hasOption('--verbose') ? 'verbose' : 'standard',
|
|
95
|
-
strictExit: parsed.hasOption('--strict-exit'),
|
|
96
|
-
events: parsed.hasOption('--events'),
|
|
97
|
-
plan: parsed.hasOption('--plan'),
|
|
98
|
-
includeAttribution: !parsed.hasOption('--no-attribution'),
|
|
99
|
-
observerLabel: normalizedLabel(parsed, '--observer', 80),
|
|
100
|
-
vantageLabel: normalizedLabel(parsed, '--vantage', 80),
|
|
101
|
-
...terminalOptions(parsed),
|
|
102
|
-
...(parsed.hasOption('--browse') ? { browse: true } : {}),
|
|
103
|
-
...(parsed.hasOption('--exact-url') ? { exactUrl: true } : {}),
|
|
104
|
-
...(parsed.optionValue('--save-lookup') ? { saveLookup: parsed.optionValue('--save-lookup') } : {}),
|
|
105
|
-
...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
function parseManifestArguments(parsed) {
|
|
109
|
-
const sources = parsed.positionalValues('artefacts');
|
|
110
|
-
if (sources.some((source) => !source))
|
|
111
|
-
throw new CliUsageError(`manifest requires from 1 to ${MAX_INVESTIGATION_MANIFEST_ARTIFACTS} artefact files.`);
|
|
112
|
-
if (new Set(sources).size !== sources.length)
|
|
113
|
-
throw new CliUsageError('manifest artefact files must be different.');
|
|
114
|
-
const configurationDigestSha256 = parsed.optionValue('--configuration-digest');
|
|
115
|
-
if (configurationDigestSha256 !== null && !/^sha256:[a-f0-9]{64}$/u.test(configurationDigestSha256)) {
|
|
116
|
-
throw new CliUsageError('--configuration-digest requires a sha256: hexadecimal digest.');
|
|
117
|
-
}
|
|
118
|
-
return {
|
|
119
|
-
action: 'manifest',
|
|
120
|
-
sources,
|
|
121
|
-
workflow: parsed.optionValue('--workflow'),
|
|
122
|
-
configurationDigestSha256,
|
|
123
|
-
...(parsed.optionValue('--passphrase-file') ? { passphraseSource: parsed.optionValue('--passphrase-file') } : {}),
|
|
124
|
-
...(parsed.hasOption('--package') ? { package: true } : {}),
|
|
125
|
-
...(parsed.hasOption('--bagit') ? { bagit: true } : {}),
|
|
126
|
-
...(parsed.optionValue('--folder') ? { folder: parsed.optionValue('--folder') } : {}),
|
|
127
|
-
output: jsonOutput(parsed),
|
|
128
|
-
...terminalOptions(parsed),
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function parseBulkArguments(parsed) {
|
|
132
|
-
const deep = parsed.hasOption('--deep');
|
|
133
|
-
const selectedFailPolicies = failPolicies(parsed, 'bulk');
|
|
134
|
-
const output = parseOutput(parsed, [
|
|
135
|
-
['--json', 'json'], ['--jsonl', 'jsonl'], ['--csv', 'csv'], ['--domains', 'domains'],
|
|
136
|
-
['--csv-with-metadata', 'csv_metadata'],
|
|
137
|
-
['--queries', 'queries'], ['--junit', 'junit'],
|
|
138
|
-
]);
|
|
139
|
-
const filter = parsed.hasOption('--registered-only') ? 'registered'
|
|
140
|
-
: parsed.hasOption('--inconclusive-only') ? 'inconclusive'
|
|
141
|
-
: parsed.hasOption('--errors-only') ? 'errors' : 'all';
|
|
142
|
-
return {
|
|
143
|
-
action: 'bulk',
|
|
144
|
-
source: parsed.positionalValue('source'),
|
|
145
|
-
output,
|
|
146
|
-
deep,
|
|
147
|
-
concurrency: parsed.integerOption('--concurrency') ?? (deep ? 2 : 4),
|
|
148
|
-
checkpoint: parsed.optionValue('--checkpoint'),
|
|
149
|
-
resume: parsed.hasOption('--resume'),
|
|
150
|
-
events: parsed.hasOption('--events'),
|
|
151
|
-
plan: parsed.hasOption('--plan'),
|
|
152
|
-
filter,
|
|
153
|
-
...terminalOptions(parsed),
|
|
154
|
-
...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
function discoveryValues(parsed) {
|
|
158
|
-
const familyText = parsed.optionValue('--families');
|
|
159
|
-
return {
|
|
160
|
-
preset: (familyText ? 'custom' : parsed.optionValue('--preset') ?? 'all'),
|
|
161
|
-
keyboardLayout: (parsed.optionValue('--keyboard') ?? 'qwerty'),
|
|
162
|
-
tldText: parsed.optionValue('--tlds'),
|
|
163
|
-
dictionarySource: parsed.optionValue('--dictionary'),
|
|
164
|
-
familyText,
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function parseDiscoverArguments(parsed) {
|
|
168
|
-
return {
|
|
169
|
-
action: 'discover',
|
|
170
|
-
seed: parsed.positionalValue('subject'),
|
|
171
|
-
output: parseOutput(parsed, [['--json', 'json'], ['--jsonl', 'jsonl'], ['--domains', 'domains']]),
|
|
172
|
-
...discoveryValues(parsed),
|
|
173
|
-
snapshotSource: parsed.optionValue('--snapshot'),
|
|
174
|
-
...terminalOptions(parsed),
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
function parseDiscoverScanArguments(parsed) {
|
|
178
|
-
const deep = parsed.hasOption('--deep');
|
|
179
|
-
const selectedFailPolicies = failPolicies(parsed, 'discover-scan');
|
|
180
|
-
const filter = parsed.hasOption('--registered-only') ? 'registered'
|
|
181
|
-
: parsed.hasOption('--inconclusive-only') ? 'inconclusive'
|
|
182
|
-
: parsed.hasOption('--acquisition-only') ? 'acquisition'
|
|
183
|
-
: parsed.hasOption('--suppressed-only') ? 'suppressed' : 'all';
|
|
184
|
-
return {
|
|
185
|
-
action: 'discover-scan',
|
|
186
|
-
seed: parsed.positionalValue('subject'),
|
|
187
|
-
output: parseOutput(parsed, [['--json', 'json'], ['--jsonl', 'jsonl'], ['--csv', 'csv'], ['--csv-with-metadata', 'csv_metadata'], ['--domains', 'domains']]),
|
|
188
|
-
...discoveryValues(parsed),
|
|
189
|
-
deep,
|
|
190
|
-
scanLimit: parsed.integerOption('--scan-limit') ?? Math.min(100, deep ? 50 : 500),
|
|
191
|
-
chunkSize: parsed.integerOption('--chunk-size') ?? 25,
|
|
192
|
-
concurrency: parsed.integerOption('--concurrency') ?? (deep ? 2 : 4),
|
|
193
|
-
checkpoint: parsed.optionValue('--checkpoint'),
|
|
194
|
-
resume: parsed.hasOption('--resume'),
|
|
195
|
-
resolverText: parsed.optionValue('--resolver'),
|
|
196
|
-
observationSnapshot: parsed.optionValue('--observation-snapshot'),
|
|
197
|
-
allowlistSource: parsed.optionValue('--allowlist'),
|
|
198
|
-
filter,
|
|
199
|
-
events: parsed.hasOption('--events'),
|
|
200
|
-
plan: parsed.hasOption('--plan'),
|
|
201
|
-
...terminalOptions(parsed),
|
|
202
|
-
...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
function parsePostureArguments(parsed) {
|
|
206
|
-
const mailProfile = parsed.optionValue('--mail-profile') ?? 'standard';
|
|
207
|
-
return {
|
|
208
|
-
action: 'posture',
|
|
209
|
-
domain: parsed.positionalValue('domain'),
|
|
210
|
-
output: parseOutput(parsed, [['--json', 'json'], ['--sarif', 'sarif']]),
|
|
211
|
-
selectorText: parsed.optionValue('--selectors'),
|
|
212
|
-
retiredSelectorText: parsed.optionValue('--retired-selectors'),
|
|
213
|
-
mailProfile: mailProfile === 'defensive-no-mail' ? 'defensive_no_mail' : mailProfile,
|
|
214
|
-
ownedDomain: parsed.hasOption('--owned-domain'),
|
|
215
|
-
...(parsed.hasOption('--include-inherited-dns') ? { includeInheritedDns: true } : {}),
|
|
216
|
-
...terminalOptions(parsed),
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
function parseDnssecValidateArguments(parsed) {
|
|
220
|
-
return {
|
|
221
|
-
action: 'dnssec-validate',
|
|
222
|
-
target: parsed.positionalValue('domain'),
|
|
223
|
-
resolver: parsed.optionValue('--resolver'),
|
|
224
|
-
trustAnchorSource: parsed.optionValue('--trust-anchor'),
|
|
225
|
-
ownedOrAuthorized: true,
|
|
226
|
-
output: jsonOutput(parsed),
|
|
227
|
-
...terminalOptions(parsed),
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
function parseMailTransportArguments(parsed) {
|
|
231
|
-
return {
|
|
232
|
-
action: 'mail-transport',
|
|
233
|
-
source: parsed.positionalValue('source'),
|
|
234
|
-
resolver: parsed.optionValue('--resolver'),
|
|
235
|
-
trustAnchorSource: parsed.optionValue('--trust-anchor'),
|
|
236
|
-
ownedOrAuthorized: true,
|
|
237
|
-
activeProbeAcknowledged: true,
|
|
238
|
-
output: jsonOutput(parsed),
|
|
239
|
-
...terminalOptions(parsed),
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
function parseTwoFileComparisonArguments(parsed, command) {
|
|
243
|
-
const sources = parsed.positionalValues('sources');
|
|
244
|
-
if (sources[0] === sources[1])
|
|
245
|
-
throw new CliUsageError(`${command} requires two different input files.`);
|
|
246
|
-
return { leftSource: sources[0], rightSource: sources[1], output: jsonOutput(parsed), ...terminalOptions(parsed) };
|
|
247
|
-
}
|
|
248
|
-
function parseVerifyArtifactArguments(parsed) {
|
|
249
|
-
if (parsed.hasOption('--folder') && parsed.positionalValue('source'))
|
|
250
|
-
throw new CliUsageError('--folder selects its own input; do not also supply a file or stdin marker.');
|
|
251
|
-
return {
|
|
252
|
-
action: 'verify-artifact',
|
|
253
|
-
source: parsed.positionalValue('source'),
|
|
254
|
-
passphraseSource: parsed.optionValue('--passphrase-file'),
|
|
255
|
-
manifestSource: parsed.optionValue('--manifest'),
|
|
256
|
-
manifestEntryId: parsed.optionValue('--manifest-entry'),
|
|
257
|
-
...(parsed.hasOption('--package') ? { package: true } : {}),
|
|
258
|
-
...(parsed.hasOption('--bagit') ? { bagit: true } : {}),
|
|
259
|
-
...(parsed.optionValue('--folder') ? { folder: parsed.optionValue('--folder') } : {}),
|
|
260
|
-
output: jsonOutput(parsed),
|
|
261
|
-
strictExit: parsed.hasOption('--strict-exit'),
|
|
262
|
-
...terminalOptions(parsed),
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
function parseMonitorOnceArguments(parsed) {
|
|
266
|
-
const selectedFailPolicies = failPolicies(parsed, 'monitor-once');
|
|
267
|
-
return {
|
|
268
|
-
action: 'monitor-once',
|
|
269
|
-
source: parsed.positionalValue('source'),
|
|
270
|
-
previousSource: parsed.optionValue('--previous'),
|
|
271
|
-
output: parseOutput(parsed, [['--json', 'json'], ['--junit', 'junit']]),
|
|
272
|
-
limit: parsed.integerOption('--limit') ?? 20,
|
|
273
|
-
concurrency: parsed.integerOption('--concurrency') ?? 2,
|
|
274
|
-
...terminalOptions(parsed),
|
|
275
|
-
...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
function parseSharingReviewArguments(parsed) {
|
|
279
|
-
const purpose = parsed.optionValue('--purpose').replace(/\s+/gu, ' ').trim();
|
|
280
|
-
if (!purpose || purpose.length > 200)
|
|
281
|
-
throw new CliUsageError('--purpose is limited to 200 characters.');
|
|
282
|
-
return {
|
|
283
|
-
action: 'sharing-review',
|
|
284
|
-
source: parsed.positionalValue('source'),
|
|
285
|
-
output: jsonOutput(parsed),
|
|
286
|
-
marking: parsed.optionValue('--marking'),
|
|
287
|
-
recipientScope: parsed.optionValue('--recipient-scope'),
|
|
288
|
-
purpose,
|
|
289
|
-
humanReviewed: parsed.hasOption('--human-reviewed'),
|
|
290
|
-
personalDataReviewed: parsed.hasOption('--personal-data-reviewed'),
|
|
291
|
-
redactionsConfirmed: parsed.hasOption('--redactions-confirmed'),
|
|
292
|
-
...terminalOptions(parsed),
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
function parseWorkflowPlanArguments(parsed) {
|
|
296
|
-
const options = { output: jsonOutput(parsed), ...terminalOptions(parsed) };
|
|
297
|
-
if (parsed.hasOption('--list')) {
|
|
298
|
-
if (parsed.allPositionals.length > 0)
|
|
299
|
-
throw new CliUsageError('workflow-plan discovery options do not accept a subject.');
|
|
300
|
-
return { action: 'workflow-plan', discovery: 'list', ...options };
|
|
301
|
-
}
|
|
302
|
-
if (parsed.hasOption('--explain')) {
|
|
303
|
-
if (parsed.allPositionals.length > 0)
|
|
304
|
-
throw new CliUsageError('workflow-plan discovery options do not accept a subject.');
|
|
305
|
-
return { action: 'workflow-plan', discovery: 'explain', recipe: parsed.optionValue('--explain'), ...options };
|
|
306
|
-
}
|
|
307
|
-
if (parsed.allPositionals.length !== 2) {
|
|
308
|
-
throw new CliUsageError('workflow-plan requires one fixed recipe and one subject, --list, or --explain <recipe>.');
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
action: 'workflow-plan',
|
|
312
|
-
recipe: parsed.positionalValue('recipe'),
|
|
313
|
-
subject: parsed.positionalValue('subject'),
|
|
314
|
-
...options,
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
function parseWorkflowRunArguments(parsed) {
|
|
318
|
-
const artifactBindings = parsed.optionValues('--use-artifact').map((value) => {
|
|
319
|
-
const match = /^([a-z0-9]+(?:-[a-z0-9]+)*):([1-9][0-9]?)=([a-z0-9]+(?:-[a-z0-9]+)*)$/u.exec(value);
|
|
320
|
-
if (!match)
|
|
321
|
-
throw new CliUsageError('--use-artifact requires <step-id>:<input-number>=<earlier-step-id>.');
|
|
322
|
-
return Object.freeze({ stepId: match[1], input: Number(match[2]), sourceStepId: match[3] });
|
|
323
|
-
});
|
|
324
|
-
const selections = parsed.optionValues('--select').map((value) => {
|
|
325
|
-
const separator = value.indexOf('=');
|
|
326
|
-
const stepId = separator === -1 ? '' : value.slice(0, separator);
|
|
327
|
-
const selection = separator === -1 ? '' : value.slice(separator + 1);
|
|
328
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(stepId) || !selection.trim()) {
|
|
329
|
-
throw new CliUsageError('--select requires <step-id>=<path-or-value>.');
|
|
330
|
-
}
|
|
331
|
-
if (selection.startsWith('-')) {
|
|
332
|
-
throw new CliUsageError('--select values cannot start with a hyphen; prefix a file path with ./ when needed.');
|
|
333
|
-
}
|
|
334
|
-
return Object.freeze({ stepId, value: selection });
|
|
335
|
-
});
|
|
336
|
-
return {
|
|
337
|
-
action: 'workflow-run',
|
|
338
|
-
recipe: parsed.positionalValue('recipe'),
|
|
339
|
-
subject: parsed.positionalValue('subject'),
|
|
340
|
-
resumeSource: parsed.optionValue('--resume'),
|
|
341
|
-
selections: Object.freeze(selections),
|
|
342
|
-
artifactBindings: Object.freeze(artifactBindings),
|
|
343
|
-
confirmedReviews: Object.freeze(parsed.optionValues('--confirm-review')),
|
|
344
|
-
approveNetwork: parsed.hasOption('--approve-network'),
|
|
345
|
-
interactive: parsed.hasOption('--interactive'),
|
|
346
|
-
output: jsonOutput(parsed),
|
|
347
|
-
...terminalOptions(parsed),
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
function parseDiffArguments(parsed) {
|
|
351
|
-
const compared = parseTwoFileComparisonArguments(parsed, 'diff');
|
|
352
|
-
const leftSessionId = parsed.optionValue('--left-session');
|
|
353
|
-
const rightSessionId = parsed.optionValue('--right-session');
|
|
354
|
-
for (const [option, value] of [['--left-session', leftSessionId], ['--right-session', rightSessionId]]) {
|
|
355
|
-
if (value !== null && !/^[A-Za-z0-9_-]{1,128}$/u.test(value)) {
|
|
356
|
-
throw new CliUsageError(`${option} requires one bounded saved-session ID.`);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return { action: 'diff', ...compared, leftSessionId, rightSessionId };
|
|
360
|
-
}
|
|
361
|
-
function uniqueSources(parsed, command) {
|
|
362
|
-
const sources = parsed.positionalValues('sources');
|
|
363
|
-
if (new Set(sources).size !== sources.length)
|
|
364
|
-
throw new CliUsageError(`${command} input files must be different.`);
|
|
365
|
-
return sources;
|
|
366
|
-
}
|
|
367
|
-
function parseCaseArguments(parsed) {
|
|
368
|
-
const operation = parsed.positionalValue('operation');
|
|
369
|
-
const source = parsed.positionalValue('source');
|
|
370
|
-
const domain = parsed.optionValue('--domain');
|
|
371
|
-
const caseId = parsed.optionValue('--case-id');
|
|
372
|
-
const title = parsed.optionValue('--title');
|
|
373
|
-
const newIncident = parsed.hasOption('--new-incident');
|
|
374
|
-
const text = parsed.optionValue('--text');
|
|
375
|
-
const noteSource = parsed.optionValue('--note-file');
|
|
376
|
-
const inputSource = parsed.optionValue('--input');
|
|
377
|
-
const expectedFileDigest = parsed.optionValue('--expect-file-digest');
|
|
378
|
-
if (source === '-' || inputSource === '-' || noteSource === '-')
|
|
379
|
-
throw new CliUsageError('Case operations require selected files, not stdin.');
|
|
380
|
-
if (operation !== 'open' && !source)
|
|
381
|
-
throw new CliUsageError(`${operation} requires a Case file.`);
|
|
382
|
-
if (operation !== 'show' && !parsed.hasOption('--output'))
|
|
383
|
-
throw new CliUsageError('Case mutations require --output; nothing was changed.');
|
|
384
|
-
if (operation === 'open' && !domain)
|
|
385
|
-
throw new CliUsageError('case open requires --domain.');
|
|
386
|
-
if (operation !== 'open' && (title || newIncident))
|
|
387
|
-
throw new CliUsageError('--title and --new-incident belong to case open.');
|
|
388
|
-
if (newIncident && (!title || caseId))
|
|
389
|
-
throw new CliUsageError('--new-incident requires a distinguishing --title and cannot select an existing --case-id.');
|
|
390
|
-
if (operation === 'note' ? (text === null && !noteSource) : (text !== null || noteSource !== null)) {
|
|
391
|
-
throw new CliUsageError('Only case note accepts and requires --text or --note-file.');
|
|
392
|
-
}
|
|
393
|
-
if (['pin', 'assess', 'recheck'].includes(operation) ? !inputSource : inputSource !== null) {
|
|
394
|
-
throw new CliUsageError('Only case pin, assess and recheck accept and require --input.');
|
|
395
|
-
}
|
|
396
|
-
if (expectedFileDigest !== null && (!source || !/^sha256:[a-f0-9]{64}$/u.test(expectedFileDigest))) {
|
|
397
|
-
throw new CliUsageError('--expect-file-digest requires a source file and sha256:<64 lowercase hexadecimal characters>.');
|
|
398
|
-
}
|
|
399
|
-
return { action: 'case', operation, source, caseId, domain, title, newIncident, text, noteSource, inputSource, expectedFileDigest, output: jsonOutput(parsed), ...terminalOptions(parsed) };
|
|
400
|
-
}
|
|
401
|
-
const CLI_PARSERS = Object.freeze({
|
|
402
|
-
completion: (parsed) => ({ action: 'completion', shell: parsed.positionalValue('shell') }),
|
|
403
|
-
doctor: (parsed) => ({ action: 'doctor', network: parsed.hasOption('--network'), output: jsonOutput(parsed), ...terminalOptions(parsed) }),
|
|
404
|
-
commands: (parsed) => ({
|
|
405
|
-
action: 'commands', output: jsonOutput(parsed), common: parsed.hasOption('--common'),
|
|
406
|
-
group: parsed.optionValue('--group'),
|
|
407
|
-
mode: parsed.optionValue('--mode'),
|
|
408
|
-
...terminalOptions(parsed),
|
|
409
|
-
}),
|
|
410
|
-
manual: () => ({ action: 'manual' }),
|
|
411
|
-
manifest: parseManifestArguments,
|
|
412
|
-
'map-observations': (parsed) => singleInputAction('map-observations', parsed),
|
|
413
|
-
'oam-export': (parsed) => singleInputAction('oam-export', parsed),
|
|
414
|
-
lookup: parseLookupArguments,
|
|
415
|
-
bulk: parseBulkArguments,
|
|
416
|
-
'ct-search': (parsed) => singleInputAction('ct-search', parsed, 'keyword'),
|
|
417
|
-
'ct-intake': (parsed) => singleInputAction('ct-intake', parsed),
|
|
418
|
-
discover: parseDiscoverArguments,
|
|
419
|
-
'discover-scan': parseDiscoverScanArguments,
|
|
420
|
-
posture: parsePostureArguments,
|
|
421
|
-
http: (parsed) => singleInputAction('http', parsed, 'domain'),
|
|
422
|
-
tls: (parsed) => singleInputAction('tls', parsed, 'hostname'),
|
|
423
|
-
'dnssec-validate': parseDnssecValidateArguments,
|
|
424
|
-
'mail-transport': parseMailTransportArguments,
|
|
425
|
-
'registry-support': (parsed) => ({
|
|
426
|
-
action: 'registry-support', target: parsed.positionalValue('domain-or-suffix'),
|
|
427
|
-
output: jsonOutput(parsed), ...terminalOptions(parsed),
|
|
428
|
-
}),
|
|
429
|
-
'registry-doctor': (parsed) => singleInputAction('registry-doctor', parsed),
|
|
430
|
-
'registry-cohort': (parsed) => singleInputAction('registry-cohort', parsed),
|
|
431
|
-
'registry-scaffold': (parsed) => ({
|
|
432
|
-
action: 'registry-scaffold', profile: parsed.optionValue('--profile'), suffix: parsed.optionValue('--suffix'),
|
|
433
|
-
scenario: parsed.optionValue('--scenario'),
|
|
434
|
-
}),
|
|
435
|
-
'risk-calibrate': (parsed) => ({
|
|
436
|
-
action: 'risk-calibrate', source: parsed.positionalValue('source'),
|
|
437
|
-
output: parsed.hasOption('--json') ? 'json' : parsed.hasOption('--summary-json') ? 'summary_json' : 'terminal',
|
|
438
|
-
...terminalOptions(parsed),
|
|
439
|
-
}),
|
|
440
|
-
'lookalike-calibrate': (parsed) => singleInputAction('lookalike-calibrate', parsed),
|
|
441
|
-
'verify-artifact': parseVerifyArtifactArguments,
|
|
442
|
-
'interchange-report': (parsed) => ({
|
|
443
|
-
action: 'interchange-report', source: parsed.positionalValue('source'),
|
|
444
|
-
passphraseSource: parsed.optionValue('--passphrase-file'), output: jsonOutput(parsed), ...terminalOptions(parsed),
|
|
445
|
-
}),
|
|
446
|
-
'inspect-archive': parseInspectArchiveArguments,
|
|
447
|
-
'sign-artifact': parseSignArtifactArguments,
|
|
448
|
-
'verify-signature': parseVerifySignatureArguments,
|
|
449
|
-
'source-report': (parsed) => singleInputAction('source-report', parsed),
|
|
450
|
-
compare: (parsed) => singleInputAction('compare', parsed),
|
|
451
|
-
'page-compare': (parsed) => ({ action: 'page-compare', ...parseTwoFileComparisonArguments(parsed, 'page-compare') }),
|
|
452
|
-
'mail-review': (parsed) => singleInputAction('mail-review', parsed),
|
|
453
|
-
'mail-headers': (parsed) => singleInputAction('mail-headers', parsed),
|
|
454
|
-
'review-evidence': (parsed) => ({
|
|
455
|
-
action: 'review-evidence', source: parsed.positionalValue('source'), mmdbSource: parsed.optionValue('--mmdb'),
|
|
456
|
-
output: jsonOutput(parsed), strictExit: parsed.hasOption('--strict-exit'), ...terminalOptions(parsed),
|
|
457
|
-
}),
|
|
458
|
-
brief: (parsed) => singleInputAction('brief', parsed),
|
|
459
|
-
case: parseCaseArguments,
|
|
460
|
-
'case-pack': (parsed) => ({
|
|
461
|
-
action: 'case-pack', source: parsed.positionalValue('source'), output: jsonOutput(parsed),
|
|
462
|
-
audience: parsed.optionValue('--audience'), reviewed: true, ...terminalOptions(parsed),
|
|
463
|
-
}),
|
|
464
|
-
'domain-control': (parsed) => singleInputAction('domain-control', parsed),
|
|
465
|
-
'monitor-once': parseMonitorOnceArguments,
|
|
466
|
-
assurance: (parsed) => singleInputAction('assurance', parsed),
|
|
467
|
-
'change-packet': (parsed) => singleInputAction('change-packet', parsed),
|
|
468
|
-
'sharing-review': parseSharingReviewArguments,
|
|
469
|
-
'workflow-plan': parseWorkflowPlanArguments,
|
|
470
|
-
'workflow-run': parseWorkflowRunArguments,
|
|
471
|
-
diff: parseDiffArguments,
|
|
472
|
-
reconcile: (parsed) => ({ action: 'reconcile', sources: uniqueSources(parsed, 'reconcile'), output: jsonOutput(parsed), ...terminalOptions(parsed) }),
|
|
473
|
-
timeline: (parsed) => ({ action: 'timeline', sources: uniqueSources(parsed, 'timeline'), output: jsonOutput(parsed), ...terminalOptions(parsed) }),
|
|
474
|
-
export: (parsed) => ({
|
|
475
|
-
action: 'export', source: parsed.positionalValue('source'),
|
|
476
|
-
format: parsed.hasOption('--markdown') ? 'markdown' : parsed.hasOption('--html') ? 'html' : 'json',
|
|
477
|
-
compact: parsed.hasOption('--compact'), includeAttribution: !parsed.hasOption('--no-attribution'),
|
|
478
|
-
}),
|
|
479
|
-
});
|
|
480
34
|
function parseCliArguments(rawArgv) {
|
|
481
35
|
if (!Array.isArray(rawArgv) || rawArgv.length > MAX_CLI_ARGUMENTS) {
|
|
482
36
|
throw new CliUsageError(`At most ${MAX_CLI_ARGUMENTS} command arguments are supported.`);
|