@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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {} from "./command-argument-grammar.mjs";
|
|
2
|
+
import { CliUsageError } from "./errors.mjs";
|
|
3
|
+
import {} from "./fail-policy.mjs";
|
|
4
|
+
import { commandDefaultNumber } from "./command-reference.mjs";
|
|
5
|
+
import { WORKFLOW_COMMAND_DEFINITIONS } from "./workflow-command-definitions.mjs";
|
|
6
|
+
import { terminalOptions, jsonOutput, failPolicies, parseOutput, } from "./argument-values.mjs";
|
|
7
|
+
function parseMonitorOnceArguments(parsed) {
|
|
8
|
+
const selectedFailPolicies = failPolicies(parsed, 'monitor-once');
|
|
9
|
+
return {
|
|
10
|
+
action: 'monitor-once',
|
|
11
|
+
source: parsed.positionalValue('source'),
|
|
12
|
+
previousSource: parsed.optionValue('--previous'),
|
|
13
|
+
output: parseOutput(parsed, WORKFLOW_COMMAND_DEFINITIONS['monitor-once']),
|
|
14
|
+
limit: parsed.integerOption('--limit') ?? commandDefaultNumber('monitor-once', '--limit'),
|
|
15
|
+
concurrency: parsed.integerOption('--concurrency') ??
|
|
16
|
+
commandDefaultNumber('monitor-once', '--concurrency'),
|
|
17
|
+
...terminalOptions(parsed),
|
|
18
|
+
...(selectedFailPolicies ? { failOn: selectedFailPolicies } : {}),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function parseWorkflowPlanArguments(parsed) {
|
|
22
|
+
const options = { output: jsonOutput(parsed), ...terminalOptions(parsed) };
|
|
23
|
+
if (parsed.hasOption('--list')) {
|
|
24
|
+
if (parsed.allPositionals.length > 0)
|
|
25
|
+
throw new CliUsageError('workflow-plan discovery options do not accept a subject.');
|
|
26
|
+
return { action: 'workflow-plan', discovery: 'list', ...options };
|
|
27
|
+
}
|
|
28
|
+
if (parsed.hasOption('--explain')) {
|
|
29
|
+
if (parsed.allPositionals.length > 0)
|
|
30
|
+
throw new CliUsageError('workflow-plan discovery options do not accept a subject.');
|
|
31
|
+
return {
|
|
32
|
+
action: 'workflow-plan',
|
|
33
|
+
discovery: 'explain',
|
|
34
|
+
recipe: parsed.optionValue('--explain'),
|
|
35
|
+
...options,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (parsed.allPositionals.length !== 2) {
|
|
39
|
+
throw new CliUsageError('workflow-plan requires one fixed recipe and one subject, --list, or --explain <recipe>.');
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
action: 'workflow-plan',
|
|
43
|
+
recipe: parsed.positionalValue('recipe'),
|
|
44
|
+
subject: parsed.positionalValue('subject'),
|
|
45
|
+
...options,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function parseWorkflowRunArguments(parsed) {
|
|
49
|
+
const artifactBindings = parsed.optionValues('--use-artifact').map((value) => {
|
|
50
|
+
const match = /^([a-z0-9]+(?:-[a-z0-9]+)*):([1-9][0-9]?)=([a-z0-9]+(?:-[a-z0-9]+)*)$/u.exec(value);
|
|
51
|
+
if (!match)
|
|
52
|
+
throw new CliUsageError('--use-artifact requires <step-id>:<input-number>=<earlier-step-id>.');
|
|
53
|
+
return Object.freeze({ stepId: match[1], input: Number(match[2]), sourceStepId: match[3] });
|
|
54
|
+
});
|
|
55
|
+
const selections = parsed.optionValues('--select').map((value) => {
|
|
56
|
+
const separator = value.indexOf('=');
|
|
57
|
+
const stepId = separator === -1 ? '' : value.slice(0, separator);
|
|
58
|
+
const selection = separator === -1 ? '' : value.slice(separator + 1);
|
|
59
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(stepId) || !selection.trim()) {
|
|
60
|
+
throw new CliUsageError('--select requires <step-id>=<path-or-value>.');
|
|
61
|
+
}
|
|
62
|
+
if (selection.startsWith('-')) {
|
|
63
|
+
throw new CliUsageError('--select values cannot start with a hyphen; prefix a file path with ./ when needed.');
|
|
64
|
+
}
|
|
65
|
+
return Object.freeze({ stepId, value: selection });
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
action: 'workflow-run',
|
|
69
|
+
recipe: parsed.positionalValue('recipe'),
|
|
70
|
+
subject: parsed.positionalValue('subject'),
|
|
71
|
+
resumeSource: parsed.optionValue('--resume'),
|
|
72
|
+
selections: Object.freeze(selections),
|
|
73
|
+
artifactBindings: Object.freeze(artifactBindings),
|
|
74
|
+
confirmedReviews: Object.freeze(parsed.optionValues('--confirm-review')),
|
|
75
|
+
approveNetwork: parsed.hasOption('--approve-network'),
|
|
76
|
+
interactive: parsed.hasOption('--interactive'),
|
|
77
|
+
...(parsed.hasOption('--preview') ? { preview: true } : {}),
|
|
78
|
+
output: jsonOutput(parsed),
|
|
79
|
+
...terminalOptions(parsed),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export const WORKFLOW_ARGUMENT_PARSERS = Object.freeze({
|
|
83
|
+
'monitor-once': parseMonitorOnceArguments,
|
|
84
|
+
'workflow-plan': parseWorkflowPlanArguments,
|
|
85
|
+
'workflow-run': parseWorkflowRunArguments,
|
|
86
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { INVESTIGATION_PLAN_RECIPES, RUNNABLE_INVESTIGATION_PLAN_RECIPES } from "./investigation-recipes.mjs";
|
|
2
|
+
import { positional, OPTIONAL_FILE_POSITIONAL, constraint, commandSeed, integer, BASE_INTEGER_RANGE, file } from "./command-definition.mjs";
|
|
3
|
+
export const WORKFLOW_COMMAND_DEFINITIONS = Object.freeze({
|
|
4
|
+
"monitor-once": commandSeed({
|
|
5
|
+
reference: {
|
|
6
|
+
description: 'Collect one bounded owned-domain review and compare it with an optional prior checkpoint.',
|
|
7
|
+
example: 'whoisleuth monitor-once manifest.json --previous previous.json --json --output next.json',
|
|
8
|
+
boundary: 'This is an operator-scheduled one-shot collection, not a daemon. It caps targets and concurrency, retains normalised observations, and never changes domain configuration.',
|
|
9
|
+
},
|
|
10
|
+
collection: { scope: 'Runs deep collection for at most 20 manifest domains with concurrency capped at 3.' },
|
|
11
|
+
summary: 'Run one bounded domain control review',
|
|
12
|
+
options: ['--previous', '--limit', '--concurrency', '--fail-on', '--json', '--junit', '--quiet', '--no-color'],
|
|
13
|
+
optionOverrides: {
|
|
14
|
+
'--concurrency': integer('Set the maximum number of concurrent collection tasks.', () => Object.freeze([BASE_INTEGER_RANGE(1, 3)]), () => 2),
|
|
15
|
+
},
|
|
16
|
+
positionals: OPTIONAL_FILE_POSITIONAL,
|
|
17
|
+
constraints: Object.freeze([
|
|
18
|
+
constraint({ kind: 'mutually_exclusive', options: ['--json', '--junit'] }),
|
|
19
|
+
]),
|
|
20
|
+
handlerOwner: 'workflow',
|
|
21
|
+
networkEffect: 'always_network',
|
|
22
|
+
common: false,
|
|
23
|
+
schemaIdentifiers: Object.freeze(['whoisleuth.cli.domain-control-monitor', 'whoisleuth.domain-control-flight-recorder.input']),
|
|
24
|
+
primaryArtefacts: Object.freeze([]),
|
|
25
|
+
planSupport: false,
|
|
26
|
+
additionalOutputFormats: Object.freeze([]),
|
|
27
|
+
bootstrapProfile: 'allowed',
|
|
28
|
+
}),
|
|
29
|
+
"workflow-plan": commandSeed({
|
|
30
|
+
reference: {
|
|
31
|
+
description: 'Build a fixed domain-investigation plan from existing bounded CLI commands.',
|
|
32
|
+
example: 'whoisleuth workflow-plan domain-triage example.test --json',
|
|
33
|
+
boundary: 'Planning is offline and plan-only. It does not execute commands, expand placeholders, read files, make requests, or submit evidence.',
|
|
34
|
+
},
|
|
35
|
+
collection: { scope: 'Builds a fixed typed recipe and executes none of its network or file steps.' },
|
|
36
|
+
summary: 'Plan a fixed investigation recipe',
|
|
37
|
+
options: ['--list', '--explain', '--json', '--quiet', '--no-color'],
|
|
38
|
+
positionals: Object.freeze([
|
|
39
|
+
positional('recipe', 'enum', 0, 1, INVESTIGATION_PLAN_RECIPES),
|
|
40
|
+
positional('subject', 'text', 0, 1),
|
|
41
|
+
]),
|
|
42
|
+
constraints: Object.freeze([
|
|
43
|
+
constraint({ kind: 'mutually_exclusive', options: ['--list', '--explain'] }),
|
|
44
|
+
]),
|
|
45
|
+
handlerOwner: 'workflow',
|
|
46
|
+
networkEffect: 'offline',
|
|
47
|
+
common: true,
|
|
48
|
+
schemaIdentifiers: Object.freeze(['whoisleuth.cli.investigation-plan', 'whoisleuth.cli.workflow-recipe-catalogue']),
|
|
49
|
+
primaryArtefacts: Object.freeze(['Plan-only workflow document']),
|
|
50
|
+
planSupport: true,
|
|
51
|
+
additionalOutputFormats: Object.freeze([]),
|
|
52
|
+
bootstrapProfile: 'allowed',
|
|
53
|
+
}),
|
|
54
|
+
"workflow-run": commandSeed({
|
|
55
|
+
reference: {
|
|
56
|
+
description: 'Execute approved steps from a fixed investigation recipe and emit a resumable checkpoint.',
|
|
57
|
+
example: 'whoisleuth workflow-run domain-triage example.test --approve-network --json --output run.json',
|
|
58
|
+
boundary: '--preview validates the checkpoint and shows retained outputs, unresolved inputs and remaining approvals without executing or writing. It cannot be combined with approval, interactive, quiet or output-file flags. Only installed recipe commands can run. Network steps require explicit approval for each invocation. New runs connect compatible earlier outputs using the recipe defaults. Use --use-artifact <step-id>:<input-number>=<earlier-step-id> to override a connection; input numbers start at 1. Repeat --select for remaining placeholders in order, or supply every input for a step to replace its connections with files. Values stay literal and cannot start with a hyphen or invoke a shell. Optional --interactive prompts on terminal stderr for missing inputs; a blank answer pauses. It grants neither network approval nor human-review confirmation. A step declaring human review still requires --confirm-review <step-id> for that invocation. Checkpoints do not grant later approvals. Resumes preserve recorded connections. Content digests identify retained output, not authenticity or freshness. Partial collections pause for review and are not recollected on resume; failed validation or export steps remain retryable. Diagnostics go to stderr. File output holds exclusive adjacent locks and refuses concurrently changed state files.',
|
|
59
|
+
},
|
|
60
|
+
collection: { scope: 'Runs only fixed-recipe steps; network collection requires --approve-network and unresolved analyst selections pause.' },
|
|
61
|
+
summary: 'Execute approved fixed-recipe steps',
|
|
62
|
+
options: ['--select', '--use-artifact', '--confirm-review', '--approve-network', '--resume', '--interactive', '--preview', '--json', '--quiet', '--no-color'],
|
|
63
|
+
optionOverrides: { '--resume': file('Resume the selected workflow checkpoint; approvals must be supplied again.') },
|
|
64
|
+
positionals: Object.freeze([
|
|
65
|
+
positional('recipe', 'enum', 1, 1, RUNNABLE_INVESTIGATION_PLAN_RECIPES),
|
|
66
|
+
positional('subject', 'text', 1, 1),
|
|
67
|
+
]),
|
|
68
|
+
constraints: Object.freeze([constraint({ kind: 'excludes_all', option: '--preview', excludedOptions: ['--approve-network', '--confirm-review', '--interactive', '--output', '--force', '--quiet'] })]),
|
|
69
|
+
handlerOwner: 'workflow',
|
|
70
|
+
networkEffect: 'conditional_network',
|
|
71
|
+
common: false,
|
|
72
|
+
schemaIdentifiers: Object.freeze(['whoisleuth.cli.investigation-run', 'whoisleuth.cli.investigation-preview']),
|
|
73
|
+
primaryArtefacts: Object.freeze(['Resumable workflow state', 'Offline resume preview']),
|
|
74
|
+
planSupport: false,
|
|
75
|
+
additionalOutputFormats: Object.freeze([]),
|
|
76
|
+
bootstrapProfile: 'allowed',
|
|
77
|
+
}),
|
|
78
|
+
});
|
|
@@ -5,7 +5,7 @@ import EXIT_CODES from "./exit-codes.mjs";
|
|
|
5
5
|
import { evaluateCliFailPolicies, formatFailPolicyNotice } from "./fail-policy.mjs";
|
|
6
6
|
import { formatJsonDocument } from "./formatters/json.mjs";
|
|
7
7
|
import { buildInvestigationPlan, buildWorkflowRecipeCatalogue, formatInvestigationPlan, formatWorkflowRecipeCatalogue, } from "./investigation-plan.mjs";
|
|
8
|
-
import { MAX_INVESTIGATION_RUN_BYTES, formatInvestigationRun, investigationRunExitCode, runInvestigationRecipe, } from "./investigation-run.mjs";
|
|
8
|
+
import { MAX_INVESTIGATION_RUN_BYTES, formatInvestigationRun, investigationRunExitCode, runInvestigationRecipe, previewInvestigationRecipe, formatInvestigationPreview, } from "./investigation-run.mjs";
|
|
9
9
|
import { MAX_OFFLINE_EVIDENCE_INPUT_BYTES } from "./offline-evidence-review.mjs";
|
|
10
10
|
import { MAX_OFFLINE_ARTIFACT_BYTES } from "./artifact-verify.mjs";
|
|
11
11
|
import { MAX_RETAINED_ARTIFACT_DIFF_BYTES } from "./retained-artifact-diff.mjs";
|
|
@@ -125,6 +125,13 @@ async function runWorkflowRecipeCommand(args, dependencies, context) {
|
|
|
125
125
|
throw new CliUsageError(`Could not read investigation resume state: ${boundedCliErrorMessage(error, 'Input could not be read')}`);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
+
if (args.preview) {
|
|
129
|
+
const preview = previewInvestigationRecipe(args.recipe, args.subject, {
|
|
130
|
+
resumeInput, selections: args.selections, artifactBindings: args.artifactBindings, generatedAt: context.now(),
|
|
131
|
+
});
|
|
132
|
+
context.writeStdout(args.output === 'json' ? formatJsonDocument(preview) : context.terminal(formatInvestigationPreview(preview), args.color));
|
|
133
|
+
return EXIT_CODES.SUCCESS;
|
|
134
|
+
}
|
|
128
135
|
const document = await runInvestigationRecipe(args.recipe, args.subject, {
|
|
129
136
|
approveNetwork: args.approveNetwork,
|
|
130
137
|
selections: args.selections,
|
package/docs/cli-reference.md
CHANGED
|
@@ -10,6 +10,11 @@ This page records the common interfaces and the boundaries that matter across
|
|
|
10
10
|
commands. The generated [privacy/data-flow catalogue](https://github.com/slicedearth/whoisleuth/blob/main/docs/privacy-data-flow-catalogue.md)
|
|
11
11
|
contains the exhaustive recipient, retention and export metadata.
|
|
12
12
|
|
|
13
|
+
For an explicitly selected local IP-location database, see the
|
|
14
|
+
[MMDB query and compatibility guide](cli.md#local-ip-location-database-review).
|
|
15
|
+
Current reviews retain exact-byte identity and intrinsic metadata, and require
|
|
16
|
+
an analyst-justified freshness policy. No database is downloaded or updated.
|
|
17
|
+
|
|
13
18
|
## Installation
|
|
14
19
|
|
|
15
20
|
Use the [installation guide](cli.md#installation) for runtime requirements,
|
|
@@ -61,6 +66,26 @@ Optional browser providers are not implicit CLI actions.
|
|
|
61
66
|
The [message-header review](cli.md#message-header-review) is offline and reports
|
|
62
67
|
publisher claims, not independent DNS or cryptographic validation.
|
|
63
68
|
|
|
69
|
+
For copied-content or credential-abuse review, keep the supplied reference,
|
|
70
|
+
candidate, form destination and deliberately observed navigation separately
|
|
71
|
+
attributed. A similar page without a logo or brand name can still merit manual
|
|
72
|
+
evidence review; correspondence does not establish authorship, rights authority
|
|
73
|
+
or maliciousness. Missing optional capture or comparison evidence does not
|
|
74
|
+
prevent ordinary Case pins and conclusions. Installed command help remains the
|
|
75
|
+
authority for offline file inputs and explicit collection plans.
|
|
76
|
+
|
|
77
|
+
The [reporting guide](https://www.whoisleuth.com/resources/reporting-and-takedown-guidance)
|
|
78
|
+
uses the same reviewed official-route catalogue as the Console for social
|
|
79
|
+
content, storefronts, app listings, hosted files/forms and advertising. Verify
|
|
80
|
+
the exact object, current route, reporter authority and disclosure before any
|
|
81
|
+
manual submission. Fraud, order complaints and rights notices are different
|
|
82
|
+
processes; search or app delisting is not source-content removal. Formal domain
|
|
83
|
+
disputes and copyright procedures have their own criteria, remedies and
|
|
84
|
+
jurisdiction-specific rules, not a universal waiting period. CLI preparation
|
|
85
|
+
does not submit a report or make a legal declaration. Preserve original delivery
|
|
86
|
+
references when preparing additional evidence, and recheck each response scope
|
|
87
|
+
independently rather than treating a closed ticket as remediation.
|
|
88
|
+
|
|
64
89
|
`dnssec-validate` and `mail-transport` are isolated authorised actions. Both
|
|
65
90
|
require a selected literal public resolver, a local trust-anchor document and
|
|
66
91
|
`--owned-or-authorized`; mail transport also requires `--active-probe`. Mail
|
|
@@ -194,6 +219,8 @@ own retention policy. No automatic key discovery, key storage or trust refresh o
|
|
|
194
219
|
evidence. They do not tune the running model, change infrastructure or turn an
|
|
195
220
|
analyst label into observed truth.
|
|
196
221
|
- `workflow-plan` lists fixed installed recipes without executing them.
|
|
222
|
+
`workflow-run --preview` validates a selected checkpoint and explains remaining
|
|
223
|
+
inputs and approvals offline, without running steps or rewriting the checkpoint.
|
|
197
224
|
`workflow-run` executes only installed steps, requires approval for network
|
|
198
225
|
work and pauses at unresolved analyst selections. Repeat
|
|
199
226
|
`--select <step-id>=<path-or-value>` for remaining placeholders in order, or bind
|
package/docs/cli.md
CHANGED
|
@@ -7,6 +7,11 @@ collection boundaries and output. Installed `whoisleuth --help`, focused
|
|
|
7
7
|
version. The [CLI reference](cli-reference.md) covers durable command and
|
|
8
8
|
artefact contracts.
|
|
9
9
|
|
|
10
|
+
The [website command reference](https://www.whoisleuth.com/cli) starts with common
|
|
11
|
+
tasks, then offers searchable commands with examples, option explanations and
|
|
12
|
+
defaults. Command-section links can be bookmarked. Its option guidance and
|
|
13
|
+
installed help derive from the same command definitions.
|
|
14
|
+
|
|
10
15
|
The generated [privacy and data-flow catalogue](https://github.com/slicedearth/whoisleuth/blob/main/docs/privacy-data-flow-catalogue.md)
|
|
11
16
|
lists the network, recipient, retention and export boundary for every command.
|
|
12
17
|
|
|
@@ -80,6 +85,11 @@ offline CLI workflow below.
|
|
|
80
85
|
When several incident Cases share a domain, select the intended Case before
|
|
81
86
|
retaining replay evidence. Case packs preserve each current Case ID separately;
|
|
82
87
|
trusted and public packs exclude analyst-entered incident titles.
|
|
88
|
+
Every audience retains tags, decision summaries and rationale as well as domain
|
|
89
|
+
evidence and Case, pin and decision IDs. Public does not mean anonymous.
|
|
90
|
+
Browser Lookup evidence can be verified or packaged in the CLI. `brief`,
|
|
91
|
+
`export`, `compare`, `registry-doctor` and `source-report` instead require a
|
|
92
|
+
saved CLI Lookup; they do not reconstruct it from a browser export.
|
|
83
93
|
|
|
84
94
|
Process selected local input:
|
|
85
95
|
|
|
@@ -136,6 +146,93 @@ their focused help. They do not use the hosted login or hosted usage controls.
|
|
|
136
146
|
`lookup --plan` lists planned source families and disclosure targets before
|
|
137
147
|
collection. `doctor` is offline unless `--network` is selected.
|
|
138
148
|
|
|
149
|
+
Deep Lookup reports IPv4 and IPv6 results separately, including empty answers,
|
|
150
|
+
NODATA, resolver-reported missing names and query failures. These outcomes remain
|
|
151
|
+
distinct in evidence exports; none alone establishes domain removal.
|
|
152
|
+
|
|
153
|
+
Plans are target-specific: Fast IP and ASN plans contain RDAP only; Deep IP
|
|
154
|
+
adds WHOIS and eligible reverse DNS, while Deep ASN adds WHOIS. Domain-only web,
|
|
155
|
+
TLS and selected intelligence do not apply to address or ASN targets.
|
|
156
|
+
Plans do not probe source health or measure remaining quota, cost, result fan-out
|
|
157
|
+
or exact request counts. CLI configuration profiles repeat supported settings;
|
|
158
|
+
optional selections made on the website do not transfer to a CLI command.
|
|
159
|
+
For completed website results, see [Source quality](https://github.com/slicedearth/whoisleuth/blob/main/docs/application-guide.md#lookup).
|
|
160
|
+
|
|
161
|
+
### Message and link intake
|
|
162
|
+
|
|
163
|
+
Use `intake email message.eml --json` for MIME email and nested messages,
|
|
164
|
+
`intake calendar invitation.ics`, `intake qr selected.png`, or `intake text`
|
|
165
|
+
with text on standard input. These commands never open a destination or execute
|
|
166
|
+
supplied content. Output compares displayed and actual hosts, retains reported
|
|
167
|
+
header authentication and interprets supplied authorisation parameters without
|
|
168
|
+
retaining URL tokens. The source hash identifies the original bytes.
|
|
169
|
+
Defanged schemes and hosts are accepted, including `hxxps[:]//host[.]example`.
|
|
170
|
+
Path, query and fragment characters keep their supplied meaning; text scanning
|
|
171
|
+
removes unmatched surrounding prose brackets, while explicit links remain exact.
|
|
172
|
+
|
|
173
|
+
Add a repeatable `--reported-action`, such as `entered_device_code` or
|
|
174
|
+
`granted_consent`, to include account-recovery guidance based on an explicitly
|
|
175
|
+
reported action. The report does not infer a stolen session from domain evidence.
|
|
176
|
+
`--strict-exit` returns 4 for a partial review. QR review supports still PNGs;
|
|
177
|
+
no decoded result is not proof that a symbol is absent.
|
|
178
|
+
Excluded links and unsupported QR payloads make the review partial, with
|
|
179
|
+
category counts rather than private payload text. Bare QR hostnames are not
|
|
180
|
+
automatically treated as URLs.
|
|
181
|
+
|
|
182
|
+
### Local IP-location database review
|
|
183
|
+
|
|
184
|
+
`review-evidence query.json --mmdb selected.mmdb --json --strict-exit` reads
|
|
185
|
+
one analyst-selected database locally. It never downloads, updates or transmits
|
|
186
|
+
the database. A current query uses this input:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"schema": "whoisleuth.local-mmdb-query",
|
|
191
|
+
"version": 2,
|
|
192
|
+
"address": "192.0.2.1",
|
|
193
|
+
"sourceLabel": "Selected local database",
|
|
194
|
+
"databaseVersion": "Analyst-declared edition",
|
|
195
|
+
"license": "Analyst-declared licence",
|
|
196
|
+
"freshnessPolicy": {
|
|
197
|
+
"maxAgeDays": 90,
|
|
198
|
+
"rationale": "Quarterly research review; recheck before operational use"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Replace the reserved example address with the public IP under review. Reserved
|
|
204
|
+
and private addresses are unavailable in current reviews. Select and justify an
|
|
205
|
+
age limit appropriate to the task; the example is not a default or an accuracy
|
|
206
|
+
guarantee. The policy requires positive whole days and a plain-text rationale
|
|
207
|
+
of at most 240 characters. The command's review time is compared with the
|
|
208
|
+
intrinsic build time; a future build, stale database, unsupported format/type,
|
|
209
|
+
miss or unusable record cannot supply current location context. Malformed
|
|
210
|
+
fields remain unknown, with partial coverage when other useful fields survive.
|
|
211
|
+
|
|
212
|
+
Current output retains SHA-256 of the exact read bytes, byte length, intrinsic
|
|
213
|
+
database type, build time, binary format and IP version. Analyst source, version
|
|
214
|
+
and licence labels remain separate claims. Only coarse network, country, region,
|
|
215
|
+
city and ASN attribution can be returned; no raw record or local path is included.
|
|
216
|
+
The existing version-1 review envelope contains the self-described
|
|
217
|
+
`whoisleuth.local-mmdb-review` version-1 result. Reject future result versions
|
|
218
|
+
rather than treating them as current evidence.
|
|
219
|
+
|
|
220
|
+
The file must remain a regular, stable file of at most 512 MiB. After the bounded
|
|
221
|
+
read and digest, one disposable worker limits synchronous parsing and lookup to
|
|
222
|
+
ten seconds, with bounded heap/stack and an 8 KiB result. This is not a ten-second
|
|
223
|
+
whole-command or filesystem deadline. Default exit 0 means a report was produced;
|
|
224
|
+
`--strict-exit` returns 4 for unavailable or partial current reviews. Version-1
|
|
225
|
+
queries retain their historical JSON shape and exit behaviour, without intrinsic
|
|
226
|
+
metadata or freshness admission; terminal output identifies that limitation.
|
|
227
|
+
|
|
228
|
+
### Contextual reviews
|
|
229
|
+
|
|
230
|
+
`review-evidence` also accepts [contextual review inputs](contextual-reviews.md)
|
|
231
|
+
for source-qualified incident sequences, retained domain history, platform-object continuity, authorised storefront
|
|
232
|
+
comparison and connector provenance. These workflows are offline; partial
|
|
233
|
+
context reviews return 4 when `--strict-exit` is selected. Browser-generated
|
|
234
|
+
reusable inputs use the same validators.
|
|
235
|
+
|
|
139
236
|
### Message-header review
|
|
140
237
|
|
|
141
238
|
`mail-headers` parses only the bounded header block from a selected message file
|
|
@@ -145,6 +242,43 @@ reported order. It makes no request and does not retain address local parts,
|
|
|
145
242
|
display names, subject, body, attachments, or raw header values in its output.
|
|
146
243
|
Authentication states are header claims, not an independent DNS or
|
|
147
244
|
cryptographic validation, and alignment differences can be legitimate.
|
|
245
|
+
Both `mail-headers` and `intake email` preserve individual authentication header
|
|
246
|
+
positions and service identifiers. Evaluate the receiver boundary yourself; to
|
|
247
|
+
record trust in one exact header, add `--trusted-auth-header 1:3` (message part 1,
|
|
248
|
+
header 3). Repeat for separate headers. This annotates the supplied claim without
|
|
249
|
+
validating it or extending trust to matching service names or nested messages.
|
|
250
|
+
|
|
251
|
+
`intake pdf selected.pdf`, `intake docx selected.docx` and `intake har selected.har`
|
|
252
|
+
use the same offline review as the browser. Document results include part hashes,
|
|
253
|
+
page references and extraction coverage. Encrypted documents require a separately
|
|
254
|
+
decrypted copy; no password is requested. HAR results preserve file order and
|
|
255
|
+
reported timings, with unavailable values distinct from zero. Neither document
|
|
256
|
+
resources nor recorded requests are fetched. Add `--strict-exit` for exit 4 when
|
|
257
|
+
coverage is partial, and `--json` for the minimised report.
|
|
258
|
+
|
|
259
|
+
`intake identity events.json --json` reads an Entra sign-in `value` array or an
|
|
260
|
+
Okta System Log array. To record a scoped comparison, wrap the selected provider
|
|
261
|
+
records in this input (timestamps require an explicit timezone):
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"schema": "whoisleuth.identity-events.input",
|
|
266
|
+
"version": 1,
|
|
267
|
+
"provider": "entra",
|
|
268
|
+
"events": [],
|
|
269
|
+
"match": {
|
|
270
|
+
"applicationId": "11111111-1111-4111-8111-111111111111",
|
|
271
|
+
"tenantId": null,
|
|
272
|
+
"actorLabel": null,
|
|
273
|
+
"startedAt": "2026-01-01T00:00:00Z",
|
|
274
|
+
"endedAt": "2026-01-01T01:00:00Z"
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Replace `events` with the selected records. Actor labels apply only within that
|
|
280
|
+
file; missing fields stay unavailable. The source hash identifies the complete
|
|
281
|
+
selected file, including an envelope when supplied. No provider API is contacted.
|
|
148
282
|
|
|
149
283
|
## Output and automation
|
|
150
284
|
|
|
@@ -162,14 +296,20 @@ Use `--case-id` when the file contains several Cases. `open` reuses the selected
|
|
|
162
296
|
Case; `--new-incident --title "Another incident"` creates a distinct ID for the
|
|
163
297
|
same domain. It does not open a browser or collect anything.
|
|
164
298
|
|
|
165
|
-
`pin`, `assess` and `recheck` read a selected JSON file with `--input`:
|
|
299
|
+
`pin`, `link`, `withdraw-link`, `assess` and `recheck` read a selected JSON file with `--input`:
|
|
166
300
|
|
|
167
301
|
```sh
|
|
168
302
|
whoisleuth case pin cases.json --input pin.json --output cases.json --force
|
|
303
|
+
whoisleuth case link cases.json --input relationship.json --output cases.json --force
|
|
169
304
|
whoisleuth case assess cases.json --input assessment.json --output cases.json --force
|
|
170
305
|
whoisleuth case recheck cases.json --input recheck.json --output cases.json --force
|
|
171
306
|
```
|
|
172
307
|
|
|
308
|
+
For `link`, use `fromPinId`, `toPinId`, `kind` (`derived_from` or
|
|
309
|
+
`shared_source`) and `basis`. `withdraw-link` takes the relationship `id` and
|
|
310
|
+
`reason`; its original declaration remains in the file. `case show` lists the
|
|
311
|
+
identities and `--json` includes the full history.
|
|
312
|
+
|
|
173
313
|
A pin describes the supplied observation, not a new collection. For example:
|
|
174
314
|
|
|
175
315
|
```json
|
|
@@ -316,6 +456,17 @@ selected evidence exports, not complete workspace backups.
|
|
|
316
456
|
|
|
317
457
|
### Resuming a fixed workflow
|
|
318
458
|
|
|
459
|
+
Inspect a paused run without executing any step or rewriting its checkpoint:
|
|
460
|
+
|
|
461
|
+
```sh
|
|
462
|
+
whoisleuth workflow-run domain-triage example.test --resume run.json --preview --json
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
The preview separates validated complete and partial outputs, failed steps that
|
|
466
|
+
can be retried, unresolved inputs, pending upstream outputs and fresh approval
|
|
467
|
+
requirements. It does not open selected input files. Preview output goes to
|
|
468
|
+
stdout; approval, interactive and output-file flags cannot be combined with it.
|
|
469
|
+
|
|
319
470
|
All recipes listed by `workflow-plan --list` can run through `workflow-run`.
|
|
320
471
|
Planning remains offline. Execution emits a checkpoint; terminal output shows
|
|
321
472
|
retained steps, output identities, missing inputs and the next required action.
|
|
@@ -348,6 +499,12 @@ remain analyst selections. No external file is inferred or extracted automatical
|
|
|
348
499
|
disagreement and unavailable evidence separate. Distinct labels do not verify
|
|
349
500
|
independent collection; timing and shared caches can affect the comparison.
|
|
350
501
|
|
|
502
|
+
`domain-control-flight-recorder` retains the last comparable complete observation
|
|
503
|
+
across failed or partial collections. A changed recovery records the interval
|
|
504
|
+
between complete source observations; it does not assign the change to the
|
|
505
|
+
recovery time. An approved window must contain that entire interval. Historical
|
|
506
|
+
report versions remain readable without acquiring this newer qualification.
|
|
507
|
+
|
|
351
508
|
For example, registry review can reuse its collection without extracting files:
|
|
352
509
|
|
|
353
510
|
```sh
|
|
@@ -385,6 +542,52 @@ an interrupted process, confirm that its owner has stopped before removing its
|
|
|
385
542
|
abandoned lock. Locks have no automatic expiry. Shell redirection and external
|
|
386
543
|
editors do not participate in this cooperative file-ownership protocol.
|
|
387
544
|
|
|
545
|
+
### Managed indicator revisions
|
|
546
|
+
|
|
547
|
+
`indicator-set` uses the same file-backed revision model as Bulk. It performs
|
|
548
|
+
no collection or submission. A plan names the set, gives a review `basis` and
|
|
549
|
+
UTC `expiresAt`, and supplies `rows` plus an explicit `selectedDomains` list.
|
|
550
|
+
Rows use the normal Bulk eligibility rules, including available profile
|
|
551
|
+
context, a Suspicious or Confirmed abuse disposition and retained risk evidence.
|
|
552
|
+
Supply `officialDomains`, `allowlistedDomains` and `commonInfrastructureDomains`
|
|
553
|
+
where applicable; excluded candidates never become additions.
|
|
554
|
+
|
|
555
|
+
Example plan shape (fictional input; replace the evidence and expiry):
|
|
556
|
+
|
|
557
|
+
```json
|
|
558
|
+
{
|
|
559
|
+
"name": "Reviewed candidates",
|
|
560
|
+
"basis": "Reviewed the selected page observation",
|
|
561
|
+
"expiresAt": "2026-10-23T00:00:00.000Z",
|
|
562
|
+
"selectedDomains": ["candidate.example.test"],
|
|
563
|
+
"rows": [{
|
|
564
|
+
"domain": "candidate.example.test", "availability": "registered",
|
|
565
|
+
"analystDisposition": "suspicious", "risk": 80,
|
|
566
|
+
"profileContext": { "sourceState": "ready" }
|
|
567
|
+
}]
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
```sh
|
|
572
|
+
whoisleuth indicator-set revise indicator-plan.json --json --output indicators-r1.json
|
|
573
|
+
whoisleuth indicator-set inspect indicators-r1.json
|
|
574
|
+
whoisleuth verify-artifact indicators-r1.json --strict-exit
|
|
575
|
+
whoisleuth indicator-set stix indicators-r1.json --output indicators-r1.stix.json
|
|
576
|
+
whoisleuth indicator-set misp indicators-r1.json --output indicators-r1.misp.json
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
Later plans embed the last manifest as `previous` and explicitly list `renewIds`
|
|
580
|
+
or `withdrawIds`. New domains still require reviewed rows and selection.
|
|
581
|
+
`reintroduceDomains` permits a previously withdrawn domain to receive a new
|
|
582
|
+
identity; it never revives the old one. Missing domains stay unchanged.
|
|
583
|
+
Renewal updates the review window and note, not the original observation.
|
|
584
|
+
|
|
585
|
+
Keep previous manifests and use separate output paths. File ownership checks
|
|
586
|
+
reject concurrent source/destination changes and refuse replacing the input,
|
|
587
|
+
even with `--force`. STIX and MISP operations always emit JSON. MISP expiry is
|
|
588
|
+
a review deadline, not automatic deletion; exports require separate recipient
|
|
589
|
+
review. Digests verify content, not authorship or an unavailable earlier chain.
|
|
590
|
+
|
|
388
591
|
### Formats and exit behaviour
|
|
389
592
|
|
|
390
593
|
Terminal text is the default. Commands expose JSON, JSONL, CSV, Markdown, HTML
|
|
@@ -414,7 +617,20 @@ diagnostics, and missing health metadata remains unmeasured.
|
|
|
414
617
|
collection times visible. Source-output and review-generation times do not
|
|
415
618
|
stand in for missing observation times. `domain-change` and
|
|
416
619
|
`domain-change-packet` retain authority, resolver and certificate observation
|
|
417
|
-
times; previously published version-2 packets remain verifiable.
|
|
620
|
+
times; previously published version-2 and version-3 packets remain verifiable.
|
|
621
|
+
Current DNS convergence and domain-change inputs use version 2. Each snapshot
|
|
622
|
+
declares `queries`, for example
|
|
623
|
+
`[{"owner":"example.test","type":"MX","state":"observed"}]`.
|
|
624
|
+
An `observed` query with no matching records means an empty answer; `partial`
|
|
625
|
+
and `unavailable` queries do not. Omitted queries are not queried. Version-1
|
|
626
|
+
inputs remain readable but establish query scope only for supplied records.
|
|
627
|
+
An expected empty set alone cannot establish an observed empty answer.
|
|
628
|
+
|
|
629
|
+
Domain-control review version 3 keeps renewal reminders separate from observed
|
|
630
|
+
configuration: `due` and `not_due` describe the chosen review date, not DNS or
|
|
631
|
+
registration changes. Incomplete evidence remains `partial` even when a reminder
|
|
632
|
+
is due. Terminal and JUnit summaries report due reminders separately. Previous
|
|
633
|
+
review and monitor checkpoints retain their original versioned interpretation.
|
|
418
634
|
|
|
419
635
|
## Exit codes
|
|
420
636
|
|
|
@@ -426,6 +642,11 @@ For scripts, use `whoisleuth verify-artifact evidence.json --json --strict-exit`
|
|
|
426
642
|
An incomplete verification returns **4** with that option. Without it, **0**
|
|
427
643
|
means a report was produced, not that the artefact passed verification; inspect
|
|
428
644
|
the report's state and checks.
|
|
645
|
+
Unencrypted workspace archives retain mutable root metadata outside their
|
|
646
|
+
section digests, so they return 4 under `--strict-exit` even when all supported
|
|
647
|
+
section checks pass. Structure-only exports can pass their narrower contract;
|
|
648
|
+
strict exit is not a ranking of cryptographic assurance. `interchange-report`
|
|
649
|
+
separately describes import/export support and is not the full verifier inventory.
|
|
429
650
|
|
|
430
651
|
## Command details
|
|
431
652
|
|