@slicedearth/whoisleuth-cli 1.34.0 → 1.39.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/README.md +19 -2
- package/bin/whoisleuth.mjs +41 -24
- package/cli/arguments.mjs +326 -3
- package/cli/artifact-verify.mjs +60 -14
- package/cli/bulk.mjs +1 -2
- package/cli/compare.mjs +1 -1
- package/cli/completion.mjs +36 -2
- package/cli/doctor.mjs +11 -5
- package/cli/evidence-command-arguments.mjs +1 -1
- package/cli/evidence-command-runner.mjs +2 -2
- package/cli/evidence-signing.mjs +4 -4
- package/cli/export-evidence.mjs +9 -2
- package/cli/formatters/html.mjs +1 -1
- package/cli/formatters/json.mjs +13 -1
- package/cli/formatters/terminal.mjs +14 -4
- package/cli/investigation-plan.mjs +112 -0
- package/cli/local-mmdb-review.mjs +76 -0
- package/cli/lookalike-calibration.mjs +121 -0
- package/cli/lookup-command-runner.mjs +4 -1
- package/cli/lookup-plan.mjs +3 -3
- package/cli/lookup-reconcile.mjs +141 -0
- package/cli/lookup-timeline.mjs +1 -1
- package/cli/mail-review.mjs +48 -3
- package/cli/offline-evidence-review.mjs +159 -0
- package/cli/registry-doctor.mjs +125 -0
- package/cli/registry-support.mjs +1 -4
- package/cli/risk-calibration.mjs +117 -9
- package/cli/runner.mjs +342 -15
- package/cli/saved-lookup.mjs +2 -2
- package/cli/sharing-review.mjs +202 -0
- package/cli/source-reliability.mjs +1 -1
- package/docs/cli-reference.md +1110 -0
- package/docs/cli.md +224 -847
- package/frontend/src/lib/analysis/acquisition-decision-packet.js +1 -1
- package/frontend/src/lib/analysis/acquisition-due-diligence.js +6 -6
- package/frontend/src/lib/analysis/bulk-domain-comparison.js +12 -12
- package/frontend/src/lib/analysis/bulk-mail-exposure.js +1 -1
- package/frontend/src/lib/analysis/bulk-retry-plan.js +3 -10
- package/frontend/src/lib/analysis/bulk-session-model.js +7 -2
- package/frontend/src/lib/analysis/bulk-source-coverage.js +76 -0
- package/frontend/src/lib/analysis/case-evidence-model.js +26 -4
- package/frontend/src/lib/analysis/case-migration-model.js +6 -1
- package/frontend/src/lib/analysis/case-record-contracts.js +4 -2
- package/frontend/src/lib/analysis/case-record-core.js +5 -1
- package/frontend/src/lib/analysis/case-record-operations.js +6 -1
- package/frontend/src/lib/analysis/case-response-packet.js +7 -7
- package/frontend/src/lib/analysis/evidence-coverage-ledger.js +328 -0
- package/frontend/src/lib/analysis/http-summary.js +2 -1
- package/frontend/src/lib/analysis/investigation-capsule.js +105 -0
- package/frontend/src/lib/analysis/lookup-asset-graph.js +806 -0
- package/frontend/src/lib/analysis/lookup-decision-support.js +480 -0
- package/frontend/src/lib/analysis/lookup-investigation-brief.js +153 -0
- package/frontend/src/lib/analysis/lookup-presentation.js +74 -0
- package/frontend/src/lib/analysis/lookup-response.js +2 -0
- package/frontend/src/lib/analysis/lookup-source-refresh.js +304 -0
- package/frontend/src/lib/analysis/lookup-summary-model.js +398 -0
- package/frontend/src/lib/analysis/page-similarity.js +1 -1
- package/frontend/src/lib/analysis/relationship-evidence.js +3 -3
- package/frontend/src/lib/analysis/relationship-observation-model.js +4 -4
- package/frontend/src/lib/analysis/scoring.js +8 -88
- package/frontend/src/lib/analysis/shortlist-model.js +8 -4
- package/frontend/src/lib/analysis/utils.js +7 -40
- package/frontend/src/lib/analysis/visualization-bounds.js +26 -0
- package/frontend/src/lib/analysis/visualization-force-graph.js +352 -0
- package/frontend/src/lib/analysis/visualization-matrix.js +88 -0
- package/frontend/src/lib/analysis/visualization-models.js +370 -0
- package/frontend/src/lib/analysis/visualization-time-series.js +144 -0
- package/frontend/src/lib/analysis/website-snapshot-model.js +57 -3
- package/frontend/src/lib/analysis/workspace-archive-crypto.js +1 -1
- package/frontend/src/lib/analysis/workspace-archive.js +2 -2
- package/lib/analyst-taxonomy.mjs +40 -0
- package/lib/availability.mjs +30 -10
- package/lib/browser-library-profile.mjs +15 -2
- package/lib/bulk-limits.mjs +13 -0
- package/lib/certificate-extension-profile.mjs +71 -0
- package/lib/credential-surface-profile.mjs +1 -1
- package/lib/ct-response-bounds.mjs +9 -0
- package/lib/ct-search.mjs +66 -12
- package/lib/dns-delegation-health.mjs +56 -5
- package/lib/dnssec-evidence-validation.mjs +235 -0
- package/lib/domain-assurance.mjs +368 -0
- package/lib/domain-control-manifest.mjs +382 -0
- package/lib/domain-posture.mjs +11 -10
- package/lib/encrypted-dns-contract.mjs +81 -0
- package/lib/evidence-export.mjs +17 -3
- package/lib/evidence-report-markdown.mjs +1 -1
- package/lib/evidence-report.mjs +4 -1
- package/lib/favicon.mjs +3 -8
- package/lib/generated/cisa-kev-catalog.mjs +1668 -0
- package/lib/html-signals.mjs +4 -0
- package/lib/http-evidence-bounds.mjs +4 -0
- package/lib/http-intelligence.mjs +2 -1
- package/lib/ip-prefix.mjs +87 -0
- package/lib/local-geoip-evidence.mjs +89 -0
- package/lib/lookup-response-contract.mjs +346 -0
- package/lib/observed-network-context.mjs +1 -1
- package/lib/opportunity-scoring.mjs +107 -0
- package/lib/outbound-identity.mjs +9 -0
- package/lib/outbound-request-bounds.mjs +9 -0
- package/lib/page-fingerprints.mjs +1 -1
- package/lib/perceptual-hash-comparison.mjs +34 -0
- package/lib/perceptual-hash.mjs +3 -31
- package/lib/project-metadata.mjs +8 -0
- package/lib/provider-policy-admission.mjs +49 -0
- package/lib/rdap-capabilities.mjs +6 -6
- package/lib/rdap-search-workbench.mjs +247 -0
- package/lib/rdap.mjs +7 -2
- package/lib/registry-access.mjs +1 -1
- package/lib/registry-capabilities.mjs +31 -1
- package/lib/registry-capability-catalogue.mjs +15 -14
- package/lib/registry-insights.mjs +6 -6
- package/lib/response-policy.mjs +81 -6
- package/lib/risk-scoring.mjs +206 -83
- package/lib/rpki-evidence.mjs +105 -0
- package/lib/safe-fetch.mjs +2 -1
- package/lib/scoring-evidence-quality.mjs +107 -0
- package/lib/security-txt.mjs +3 -3
- package/lib/semantic-version.mjs +54 -0
- package/lib/static-html-analysis.mjs +27 -0
- package/lib/threat-intelligence-manifests.mjs +1 -1
- package/lib/threatfox-intelligence.mjs +11 -6
- package/lib/tls-intelligence.mjs +13 -3
- package/lib/tlsa-evidence.mjs +194 -0
- package/lib/urlhaus-intelligence.mjs +11 -6
- package/lib/urlscan-intelligence.mjs +10 -5
- package/lib/website-security-posture.mjs +16 -9
- package/lib/website-technology.mjs +1 -1
- package/lib/whois-chain.mjs +14 -152
- package/lib/whois.mjs +2 -2
- package/package.json +6 -2
- package/third-party-notices.txt +911 -19
|
@@ -0,0 +1,1110 @@
|
|
|
1
|
+
# WHOISleuth CLI reference
|
|
2
|
+
|
|
3
|
+
This complete reference documents every supported command contract. For a
|
|
4
|
+
short installation and first-use path, start with the [CLI guide](cli.md).
|
|
5
|
+
|
|
6
|
+
The first-party command-line interface runs the same local classification and
|
|
7
|
+
lookup modules as the Express and serverless adapters. It does not call the
|
|
8
|
+
hosted WHOISleuth deployment.
|
|
9
|
+
|
|
10
|
+
`whoisleuth --help` displays the copyright, AGPL-3.0-only licence, and official
|
|
11
|
+
source location. It groups commands by investigation, discovery, saved-evidence,
|
|
12
|
+
integrity, and calibration workflows. `whoisleuth <command> --help` displays the
|
|
13
|
+
command's purpose, focused invocation, example, and collection boundary without
|
|
14
|
+
printing the full command list. `whoisleuth commands --json` exposes those same
|
|
15
|
+
contracts as a stable local catalogue for wrappers and terminal tooling.
|
|
16
|
+
Packaged copies include `LICENSE`, `NOTICE`, and
|
|
17
|
+
`TRADEMARKS.md` alongside the CLI documentation.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Public releases require Node.js 24 or later and install the `whoisleuth`
|
|
22
|
+
command:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm exec --yes --ignore-scripts --package=@slicedearth/whoisleuth-cli -- whoisleuth --help
|
|
26
|
+
npm install --global --ignore-scripts @slicedearth/whoisleuth-cli
|
|
27
|
+
whoisleuth doctor
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Update and verify an existing global installation with:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install --global --ignore-scripts @slicedearth/whoisleuth-cli@latest
|
|
34
|
+
whoisleuth --version
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The scoped package and application share one semantic version. The package runs
|
|
38
|
+
locally and does not call the hosted WHOISleuth deployment. Networked commands
|
|
39
|
+
contact only the sources described by their collection profile; offline review,
|
|
40
|
+
comparison, verification, completion, and manual commands remain local.
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
node bin/whoisleuth.mts lookup example.com
|
|
46
|
+
node bin/whoisleuth.mts lookup AS13335 --json
|
|
47
|
+
printf 'example.com\n' | node bin/whoisleuth.mts lookup --json
|
|
48
|
+
node bin/whoisleuth.mts lookup example.com --deep
|
|
49
|
+
node bin/whoisleuth.mts lookup example.com --deep --plan --json
|
|
50
|
+
node bin/whoisleuth.mts lookup example.com --deep --summary
|
|
51
|
+
node bin/whoisleuth.mts lookup example.com --deep --verbose
|
|
52
|
+
node bin/whoisleuth.mts lookup example.com --deep --markdown --output lookup.md
|
|
53
|
+
node bin/whoisleuth.mts lookup example.com --deep --json --strict-exit --events
|
|
54
|
+
node bin/whoisleuth.mts lookup example.test --deep --observer workstation-a --vantage office-egress --json > office.json
|
|
55
|
+
cat domains.txt | node bin/whoisleuth.mts bulk --jsonl
|
|
56
|
+
node bin/whoisleuth.mts bulk domains.txt --csv --registered-only
|
|
57
|
+
node bin/whoisleuth.mts bulk domains.txt --domains --inconclusive-only
|
|
58
|
+
node bin/whoisleuth.mts bulk domains.txt --queries --errors-only
|
|
59
|
+
node bin/whoisleuth.mts bulk domains.txt --concurrency 4
|
|
60
|
+
node bin/whoisleuth.mts bulk domains.txt --deep --checkpoint bulk-checkpoint.json
|
|
61
|
+
node bin/whoisleuth.mts bulk domains.txt --deep --checkpoint bulk-checkpoint.json --resume
|
|
62
|
+
node bin/whoisleuth.mts ct-search 'example brand' --json
|
|
63
|
+
node bin/whoisleuth.mts discover example.com --preset common --jsonl
|
|
64
|
+
node bin/whoisleuth.mts discover example.test --dictionary private-terms.txt --snapshot discovery-state.json --json
|
|
65
|
+
node bin/whoisleuth.mts discover-scan example.test --scan-limit 50 --checkpoint candidate-scan.json --json
|
|
66
|
+
node bin/whoisleuth.mts discover-scan example.test --deep --scan-limit 20 --resolver 192.0.2.53 --observation-snapshot observed-candidates.json --csv
|
|
67
|
+
node bin/whoisleuth.mts posture example.com --selectors selector1 --retired-selectors selector0 --mail-profile defensive-no-mail --json
|
|
68
|
+
node bin/whoisleuth.mts http example.com --json
|
|
69
|
+
node bin/whoisleuth.mts tls example.com --json
|
|
70
|
+
node bin/whoisleuth.mts registry-support example.uk --json
|
|
71
|
+
node bin/whoisleuth.mts registry-doctor lookup.json --json
|
|
72
|
+
node bin/whoisleuth.mts risk-calibrate calibration.json --json
|
|
73
|
+
node bin/whoisleuth.mts lookalike-calibrate reviewed-candidates.json --json
|
|
74
|
+
node bin/whoisleuth.mts verify-artifact workspace.json --json
|
|
75
|
+
node bin/whoisleuth.mts verify-artifact lookup.json --json
|
|
76
|
+
node bin/whoisleuth.mts verify-artifact workspace-encrypted.json --passphrase-file passphrase.txt --json
|
|
77
|
+
node bin/whoisleuth.mts source-report lookup.json --json
|
|
78
|
+
node bin/whoisleuth.mts inspect-archive workspace.json --json
|
|
79
|
+
node bin/whoisleuth.mts inspect-archive workspace.json --search example.invalid --json
|
|
80
|
+
node bin/whoisleuth.mts sign-artifact response-packet.json --private-key-file analyst-private.pem > response-packet.signed.json
|
|
81
|
+
node bin/whoisleuth.mts verify-signature response-packet.signed.json --public-key-file analyst-public.pem
|
|
82
|
+
node bin/whoisleuth.mts lookup example.com --deep --json > lookup.json
|
|
83
|
+
node bin/whoisleuth.mts compare lookup.json --json
|
|
84
|
+
node bin/whoisleuth.mts page-compare official.json candidate.json --json
|
|
85
|
+
node bin/whoisleuth.mts mail-review bulk.json --json
|
|
86
|
+
node bin/whoisleuth.mts review-evidence dnssec-evidence.json --json
|
|
87
|
+
node bin/whoisleuth.mts domain-control domain-control-input.json --json
|
|
88
|
+
node bin/whoisleuth.mts assurance domain-assurance-input.json --json
|
|
89
|
+
node bin/whoisleuth.mts sharing-review response-packet.json --marking amber --recipient-scope organization --purpose 'Reviewed incident handoff' --human-reviewed --personal-data-reviewed --redactions-confirmed --json
|
|
90
|
+
node bin/whoisleuth.mts workflow-plan domain-triage example.test --json
|
|
91
|
+
node bin/whoisleuth.mts diff first-lookup.json second-lookup.json --json
|
|
92
|
+
node bin/whoisleuth.mts reconcile office.json mobile.json external.json --json
|
|
93
|
+
node bin/whoisleuth.mts timeline first-observation.json second-observation.json latest-observation.json --json
|
|
94
|
+
node bin/whoisleuth.mts export lookup.json > evidence.json
|
|
95
|
+
node bin/whoisleuth.mts export lookup.json --markdown > evidence.md
|
|
96
|
+
node bin/whoisleuth.mts export lookup.json --html > evidence.html
|
|
97
|
+
node bin/whoisleuth.mts doctor
|
|
98
|
+
node bin/whoisleuth.mts doctor --network --json
|
|
99
|
+
node bin/whoisleuth.mts commands --json
|
|
100
|
+
node bin/whoisleuth.mts completion zsh
|
|
101
|
+
node bin/whoisleuth.mts completion powershell
|
|
102
|
+
node bin/whoisleuth.mts manual | man -l -
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
These examples run from a checked-out repository; replace
|
|
106
|
+
`node bin/whoisleuth.mts` with `whoisleuth` after installation. The root
|
|
107
|
+
application package remains private. A separate scoped CLI archive is assembled
|
|
108
|
+
from the exact executable dependency closure, installed in a temporary
|
|
109
|
+
directory, and smoke tested with:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm run cli:package:check
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The check includes only the executable, reachable TypeScript modules, CLI
|
|
116
|
+
guide, licence, notices, and trademark terms. It excludes application routes,
|
|
117
|
+
deployment adapters, tests, and development tools. Direct runtime dependencies
|
|
118
|
+
are pinned to the exact versions exercised by the reviewed repository lockfile.
|
|
119
|
+
The package and its dependencies require no lifecycle scripts, so the install
|
|
120
|
+
examples disable them explicitly.
|
|
121
|
+
Its generated manifest stays private. Release-candidate assembly is a separate
|
|
122
|
+
explicit operation and never publishes from a developer checkout. Use the
|
|
123
|
+
scoped package name; an unqualified `npx whoisleuth` is not this project.
|
|
124
|
+
|
|
125
|
+
Lookup defaults to the conservative fast profile. `--deep` must be requested
|
|
126
|
+
explicitly and can add bounded WHOIS, DNS, website, TLS, registrar RDAP, and
|
|
127
|
+
observed-network IP RDAP work for a domain. The browser Console's optional
|
|
128
|
+
security.txt and external intelligence selections are not CLI flags; the CLI
|
|
129
|
+
does not run those actions implicitly.
|
|
130
|
+
|
|
131
|
+
For a directly entered public IP address, `lookup --deep` can also run one
|
|
132
|
+
bounded reverse-DNS query. Terminal output shows the explicit source state and
|
|
133
|
+
up to five normalised PTR names; JSON retains up to eight. PTR names are
|
|
134
|
+
operator-published routing context, not proof of hosting control or ownership.
|
|
135
|
+
Fast lookups do not run this query.
|
|
136
|
+
|
|
137
|
+
`lookup --plan` performs classification and prints a versioned preflight without
|
|
138
|
+
starting collection. It lists the normalised target, selected fast or deep
|
|
139
|
+
mode, planned source families, conditional operations, and the data each family
|
|
140
|
+
may receive. The plan cannot predict live feature configuration, cache state,
|
|
141
|
+
redirects, referrals, source availability, or an exact request count. It can be
|
|
142
|
+
combined with `--json`, but not with report, detail, strict-exit, progress-event,
|
|
143
|
+
or quiet options.
|
|
144
|
+
|
|
145
|
+
Only one query is accepted by `lookup`. Multiple-input processing belongs to
|
|
146
|
+
the explicit `bulk` command rather than being silently inferred by `lookup`.
|
|
147
|
+
Standard input is capped at 4 KiB and must contain one non-empty line.
|
|
148
|
+
|
|
149
|
+
`registry-support` is an offline catalogue view. It accepts one domain, bare
|
|
150
|
+
suffix, or leading-dot suffix as an argument or on stdin and makes no network
|
|
151
|
+
request. Its output is described below.
|
|
152
|
+
|
|
153
|
+
`doctor` is offline by default. It checks the Node runtime, platform, terminal
|
|
154
|
+
capabilities, and availability of offline command families. `doctor --network`
|
|
155
|
+
adds independent bounded public-DNS, HTTPS/RDAP bootstrap, and WHOIS port 43
|
|
156
|
+
checks against fixed IANA diagnostic infrastructure. The three probes run
|
|
157
|
+
within the same per-probe ceiling so one stalled transport does not block the
|
|
158
|
+
others. The report retains only state, HTTP status, and a bounded explanation,
|
|
159
|
+
not resolved addresses or response content. A failed optional network check
|
|
160
|
+
returns the partial-failure exit code rather than claiming that all CLI
|
|
161
|
+
collection is unavailable.
|
|
162
|
+
|
|
163
|
+
`completion bash`, `completion zsh`, `completion fish`, and `completion
|
|
164
|
+
powershell` print static shell completion scripts to stdout. The command does
|
|
165
|
+
not edit a shell profile or make a network request. Review the generated script
|
|
166
|
+
before sourcing it or placing it in the relevant shell completion directory.
|
|
167
|
+
`manual` prints a generated roff manual page and likewise changes no local
|
|
168
|
+
configuration.
|
|
169
|
+
|
|
170
|
+
`commands` prints the installed command catalogue without running collection or
|
|
171
|
+
reading evidence. `commands --json` emits version 1 of the
|
|
172
|
+
`whoisleuth.cli.command-catalogue` schema, including each command's usage,
|
|
173
|
+
example, declared collection mode, collection scope, and boundary. This is the
|
|
174
|
+
supported discovery contract for local wrappers; it is not a permission grant
|
|
175
|
+
or a substitute for focused help.
|
|
176
|
+
|
|
177
|
+
Focused command help and the generated manual classify every operation as
|
|
178
|
+
offline or networked and state the relevant target, input, and concurrency
|
|
179
|
+
ceiling. These labels describe observable behaviour rather than granting
|
|
180
|
+
authorisation. The packaged `DISCLOSURE` defines the intended defensive scope;
|
|
181
|
+
operators remain responsible for law, provider terms, and authorisation.
|
|
182
|
+
|
|
183
|
+
## Deployment boundary
|
|
184
|
+
|
|
185
|
+
The CLI runs locally. The serverless deployment publishes only `frontend/build`
|
|
186
|
+
and packages functions only from `netlify/functions`, so `bin/` and `cli/` are
|
|
187
|
+
not part of the hosted static site or function bundle. The application root and
|
|
188
|
+
ordinary package-check candidate remain private. Explicitly reviewed CLI
|
|
189
|
+
release candidates can be published under the scoped public package described
|
|
190
|
+
in the installation section.
|
|
191
|
+
|
|
192
|
+
Commands that query RDAP, WHOIS, DNS, HTTP, TLS, or Certificate Transparency do
|
|
193
|
+
so directly from the machine running the CLI. They do not use the hosted login,
|
|
194
|
+
hosted session, or deployment usage controls; upstream providers can see and
|
|
195
|
+
rate-limit the local machine's network address. Offline `discover`, `compare`,
|
|
196
|
+
`page-compare`, `mail-review`, `review-evidence`, `domain-control`, `assurance`,
|
|
197
|
+
`sharing-review`, `workflow-plan`, `diff`, `reconcile`, `timeline`,
|
|
198
|
+
`risk-calibrate`, `lookalike-calibrate`, `registry-doctor`, `verify-artifact`, `source-report`, `export`,
|
|
199
|
+
`commands`, `completion`, and `manual` operations make no network requests. Commands write
|
|
200
|
+
to stdout unless the analyst deliberately selects a local output file.
|
|
201
|
+
|
|
202
|
+
## Output
|
|
203
|
+
|
|
204
|
+
Human-readable terminal output is the default. On an interactive terminal it
|
|
205
|
+
uses restrained semantic colour, groups Lookup evidence by purpose, wraps
|
|
206
|
+
prose to the detected terminal width, and shows transient progress on stderr
|
|
207
|
+
for slower collection. State remains explicit in text, so colour is never the
|
|
208
|
+
only distinction. Redirected terminal output, JSON, and JSONL contain no ANSI
|
|
209
|
+
sequences or progress text. `--no-color` and a non-empty conventional
|
|
210
|
+
`NO_COLOR` environment variable disable colour; `WHOISLEUTH_NO_PROGRESS=1`
|
|
211
|
+
disables the transient indicator. Command examples remain one copyable line
|
|
212
|
+
even when prose is wrapped for a narrow terminal.
|
|
213
|
+
|
|
214
|
+
Every command accepts `--output <file>` as a safer alternative to shell
|
|
215
|
+
redirection. WHOISleuth buffers bounded output, creates a private temporary file
|
|
216
|
+
beside the destination, syncs it, and publishes it atomically with mode `0600`.
|
|
217
|
+
An existing destination is refused. `--force` is valid only with `--output` and
|
|
218
|
+
allows an intentional atomic replacement. A failed or cancelled command does
|
|
219
|
+
not publish a partial output file. Output is capped at 32 MiB.
|
|
220
|
+
|
|
221
|
+
`lookup --markdown` and `lookup --html` build the existing normalised evidence
|
|
222
|
+
report directly after one completed domain lookup. They do not start a second
|
|
223
|
+
collection. These report formats reject IP and ASN inputs before collection;
|
|
224
|
+
JSON and terminal output remain available for those query types.
|
|
225
|
+
|
|
226
|
+
Lookup terminal output supports three detail levels. The standard view keeps
|
|
227
|
+
the normal bounded evidence summary. `--summary` keeps the source states and
|
|
228
|
+
key findings while omitting endpoints and supporting detail. `--verbose` adds
|
|
229
|
+
the document generation time and the existing bounded per-source collection
|
|
230
|
+
timings. These switches are presentation-only and cannot be combined with
|
|
231
|
+
`--json`; they do not change collection or the result document.
|
|
232
|
+
|
|
233
|
+
`--json` writes one versioned document to standard output:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"schema": "whoisleuth.cli.lookup",
|
|
238
|
+
"version": 1,
|
|
239
|
+
"generatedAt": "2026-07-14T00:00:00.000Z",
|
|
240
|
+
"mode": "fast",
|
|
241
|
+
"query": "example.com",
|
|
242
|
+
"type": "domain"
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The complete document also carries the normalised `rdap`, `whois`,
|
|
247
|
+
`availability`, and `diagnostics` sections returned by the shared lookup
|
|
248
|
+
orchestrator. Machine output goes to stdout. Usage and lookup errors go to
|
|
249
|
+
stderr, so redirected JSON is not mixed with diagnostics.
|
|
250
|
+
|
|
251
|
+
Human-readable domain lookup output separately shows the registrar RDAP status
|
|
252
|
+
and endpoint whenever the shared lookup diagnostics represent that follow-up.
|
|
253
|
+
Deep mode may report success, unsupported, not found, or an explicit failure;
|
|
254
|
+
fast mode reports the existing skipped state. IP, ASN, and lookup responses
|
|
255
|
+
without registrar diagnostics remain unchanged. These source states are
|
|
256
|
+
provenance only and do not decide availability or imply safety.
|
|
257
|
+
|
|
258
|
+
When a deep domain response includes registry interpretation version 1, the
|
|
259
|
+
terminal summary also shows the lifecycle label, separately attributed RDAP and
|
|
260
|
+
WHOIS disclosure states, reconciliation state, and complete, partial, and
|
|
261
|
+
unavailable publication counts. It does not print published contact routes.
|
|
262
|
+
The JSON document retains the bounded interpretation and its limitations.
|
|
263
|
+
|
|
264
|
+
A deep domain lookup can also show the status, selected public address, and
|
|
265
|
+
registered network name from the bounded observed network context. It uses the
|
|
266
|
+
same address and IP RDAP source represented in the JSON document. This is
|
|
267
|
+
point-in-time edge or network-registration context, not proof of the origin
|
|
268
|
+
host, hosting control, ownership, intent, or maliciousness. Fast and compact
|
|
269
|
+
commands do not run the enrichment.
|
|
270
|
+
|
|
271
|
+
The deep terminal summary also reports the website activity state, page title,
|
|
272
|
+
DNS, HTTP, and TLS source states, bounded publisher-declared structured
|
|
273
|
+
identity entities, up to six bounded technology indicators, a browser-library
|
|
274
|
+
profile count, and the four passive security-posture counts.
|
|
275
|
+
These are concise projections of the same evidence already present in the
|
|
276
|
+
lookup response. The browser-library line counts apparent components and
|
|
277
|
+
catalogue advisory matches without retaining script references or raw
|
|
278
|
+
signatures. These summaries make no extra request, omit raw evidence
|
|
279
|
+
descriptions, and do not turn a technology or advisory match, or a missing
|
|
280
|
+
posture signal, into proof of exploitability, hosting control, ownership, or
|
|
281
|
+
maliciousness. Use `--json` when the full bounded evidence, limitations, and
|
|
282
|
+
source diagnostics are required.
|
|
283
|
+
|
|
284
|
+
`lookup --strict-exit` is an opt-in automation policy. It still emits the
|
|
285
|
+
ordinary complete output document, but returns exit code 4 when a requested
|
|
286
|
+
diagnostic source is partial, unavailable, rate-limited, timed out, or failed.
|
|
287
|
+
The default exit contract remains unchanged, so inconclusive source health does
|
|
288
|
+
not silently break existing scripts.
|
|
289
|
+
|
|
290
|
+
`lookup --events` and `bulk --events` write versioned
|
|
291
|
+
`whoisleuth.cli.progress` version 1 JSONL lifecycle events to stderr while the
|
|
292
|
+
ordinary final result stays on stdout. Events contain only the command,
|
|
293
|
+
sequence, time, source state or Bulk item index, categorical failure reasons,
|
|
294
|
+
and final exit status. They do not contain a target, query, endpoint, file
|
|
295
|
+
path, error detail, or evidence value. Human progress and error text are
|
|
296
|
+
suppressed while events are active. `--events` cannot be combined with
|
|
297
|
+
`--output`, because the final event must describe the same completed stdout
|
|
298
|
+
delivery observed by the caller.
|
|
299
|
+
|
|
300
|
+
Pressing Ctrl-C asks an in-flight Lookup or Bulk run to stop, suppresses any
|
|
301
|
+
partial final result, flushes an enabled Bulk checkpoint, and exits with code
|
|
302
|
+
130. Already started network operations remain subject to their existing hard
|
|
303
|
+
timeouts while the shared in-process API unwinds; the executable terminates
|
|
304
|
+
after CLI cleanup. A second Ctrl-C removes any known unpublished temporary
|
|
305
|
+
output file on a best-effort basis before exiting immediately.
|
|
306
|
+
|
|
307
|
+
Bulk can persist compact progress with `--checkpoint <file>`. A new checkpoint
|
|
308
|
+
is private, bounded to 16 MiB, versioned, and tied to the exact ordered input
|
|
309
|
+
and scan mode by SHA-256. It stores only compact per-item results, never full
|
|
310
|
+
Lookup responses. Reusing the same path is refused unless `--resume` is also
|
|
311
|
+
specified. Resume revalidates the complete untrusted document and skips only
|
|
312
|
+
the exact completed rows; changed input, mode, schema, digest, or malformed
|
|
313
|
+
results fail closed.
|
|
314
|
+
|
|
315
|
+
If checkpoint persistence fails after collection, WHOISleuth still emits the
|
|
316
|
+
completed final result, reports the checkpoint limitation, and exits with code
|
|
317
|
+
4. The last successfully published checkpoint remains available, but it may
|
|
318
|
+
not contain the final completed items.
|
|
319
|
+
|
|
320
|
+
`diff <left.json> <right.json>` compares two different saved domain Lookup
|
|
321
|
+
documents entirely offline. It reuses the bounded Bulk comparison model for
|
|
322
|
+
registration, DNS, page identity, mail, certificate, and relationship fields,
|
|
323
|
+
keeps missing and unavailable evidence distinct, and makes no ownership or
|
|
324
|
+
maliciousness inference. The output records both original observation times.
|
|
325
|
+
|
|
326
|
+
`lookup --observer <label> --vantage <label>` can add two bounded analyst labels
|
|
327
|
+
to a saved Lookup document. The labels identify the person or process that ran
|
|
328
|
+
the observation and the collection context chosen by that analyst; they are
|
|
329
|
+
not verified identities, network measurements, or proof of independent
|
|
330
|
+
collection. They do not change which sources are queried.
|
|
331
|
+
|
|
332
|
+
`reconcile <observation.json> <observation.json> [...]` accepts from 2 to 5
|
|
333
|
+
saved Lookup documents for the same domain and compares their bounded fields
|
|
334
|
+
offline. It keeps agreement, disagreement, and non-comparable evidence
|
|
335
|
+
separate, retains observation times and optional analyst labels, and copies no
|
|
336
|
+
filenames or raw registration payloads. Distinct complete label pairs are
|
|
337
|
+
reported as labelled collection contexts only; majority agreement is not
|
|
338
|
+
treated as truth or authority.
|
|
339
|
+
|
|
340
|
+
`timeline <observation.json> <observation.json> [...]` accepts from 2 to 20
|
|
341
|
+
saved Lookup documents for one domain, orders them by their validated
|
|
342
|
+
observation times, and compares each adjacent pair offline. The 32 MiB aggregate
|
|
343
|
+
input ceiling is enforced while files are read and again by the timeline
|
|
344
|
+
builder. Output retains normalised field states and observation times but no
|
|
345
|
+
input filenames or raw registry payloads. A difference can reflect a domain
|
|
346
|
+
change or changed collection conditions, so the command makes no current-state,
|
|
347
|
+
ownership, intent, safety, or maliciousness conclusion.
|
|
348
|
+
|
|
349
|
+
When diagnostics version 5, 6, 7, or 8 reports a documented registry collection
|
|
350
|
+
constraint, terminal output also shows the suffix, WHOIS and RDAP access
|
|
351
|
+
profiles, and the bounded limitation. This is static access-policy context: it
|
|
352
|
+
does not make another request, and restricted, unpublished, or unavailable
|
|
353
|
+
machine access is not evidence that a domain is unregistered or safe.
|
|
354
|
+
|
|
355
|
+
## Exit codes
|
|
356
|
+
|
|
357
|
+
| Code | Meaning |
|
|
358
|
+
| ---: | --- |
|
|
359
|
+
| 0 | Command completed. Individual sources may still be partial or inconclusive; inspect diagnostics. |
|
|
360
|
+
| 2 | Invalid command, option, input, or stdin shape. |
|
|
361
|
+
| 3 | The requested lookup, collection, or comparison operation could not run. |
|
|
362
|
+
| 4 | A bounded operation completed partially, such as Bulk item failures or failed explicit Doctor network checks. |
|
|
363
|
+
| 70 | Unexpected CLI bootstrap failure. |
|
|
364
|
+
| 130 | The analyst cancelled the command. No partial final result was emitted. |
|
|
365
|
+
|
|
366
|
+
This release supports `lookup`, `bulk`, `ct-search`, `discover`, `discover-scan`, `posture`,
|
|
367
|
+
`http`, `tls`, `registry-support`, `registry-doctor`, `risk-calibrate`,
|
|
368
|
+
`lookalike-calibrate`, `verify-artifact`,
|
|
369
|
+
`inspect-archive`, `sign-artifact`, `verify-signature`, `source-report`,
|
|
370
|
+
`compare`, `page-compare`, `mail-review`, `review-evidence`, `domain-control`,
|
|
371
|
+
`assurance`, `sharing-review`, `workflow-plan`, `diff`, `reconcile`, `timeline`,
|
|
372
|
+
`export`, `doctor`, `commands`, `completion`, and `manual`. Additional command families
|
|
373
|
+
are added as separate bounded increments rather than exposing incomplete
|
|
374
|
+
aliases.
|
|
375
|
+
|
|
376
|
+
## Registry capability coverage
|
|
377
|
+
|
|
378
|
+
`registry-support` projects the embedded, versioned registry compatibility
|
|
379
|
+
catalogue for one domain or suffix. It distinguishes an explicit
|
|
380
|
+
fixture-backed or access-documented suffix profile from the generic IANA
|
|
381
|
+
discovery profile and reports the RDAP and WHOIS discovery/access paths,
|
|
382
|
+
WHOIS query and parser profiles, fixture scenarios,
|
|
383
|
+
verification references, documentation references, and the catalogue's
|
|
384
|
+
limitation. The same document includes a dated, official-source aggregate of
|
|
385
|
+
generic, generic-restricted, sponsored, and infrastructure RDAP coverage. That
|
|
386
|
+
snapshot is separate from the suffix profile and from live reachability.
|
|
387
|
+
|
|
388
|
+
Terminal output is bounded and control-safe. `--json` emits version 3 of the
|
|
389
|
+
`whoisleuth.cli.registry-support` schema. Unknown but syntactically valid
|
|
390
|
+
suffixes retain the generic `discovery_only` profile; malformed input exits
|
|
391
|
+
with code 2. The command never probes a registry or tests current reachability.
|
|
392
|
+
Coverage is context only and cannot decide registration, availability,
|
|
393
|
+
ownership, safety, or maliciousness.
|
|
394
|
+
|
|
395
|
+
`registry-doctor [lookup.json]` compares one saved domain Lookup with the same
|
|
396
|
+
reviewed local capability profile. It reports whether RDAP and WHOIS collection
|
|
397
|
+
states align with allowed, permission-required, or unsupported access, counts
|
|
398
|
+
bounded normalised publication fields, and reports whether a registry object
|
|
399
|
+
identifier was observed. A missing identifier is a publication omission, not
|
|
400
|
+
a failed lookup. The command is offline, makes no retry, and cannot establish
|
|
401
|
+
current registry reachability.
|
|
402
|
+
|
|
403
|
+
The offline RDAP supplied-evidence review implements the request shape defined
|
|
404
|
+
by RFC 9536 for `resources`, `domains`, `nameservers`, and `entities`. It also
|
|
405
|
+
validates a supplied server's `reverse_search_properties_mapping` response
|
|
406
|
+
before preparing a request. Only registered IANA property paths and exact
|
|
407
|
+
supported resource/property pairs are accepted. Preparing the request neither
|
|
408
|
+
proves that a particular server supports it nor sends a reverse search; active
|
|
409
|
+
execution remains gated on a reviewed server policy, privacy boundary, and
|
|
410
|
+
deterministic fixtures.
|
|
411
|
+
|
|
412
|
+
The separate maintenance command `npm run rdap-extensions:drift` compares the
|
|
413
|
+
pinned official RDAP extension fixture with the reviewed local interpretation
|
|
414
|
+
catalogue entirely offline. `--live` performs one bounded manual fetch from the
|
|
415
|
+
fixed official CSV URL and reports added, removed, renamed, status-changed,
|
|
416
|
+
unrecognized, and local-only identifiers. `--json` emits
|
|
417
|
+
`whoisleuth.rdap-extension-drift-audit` version 1. Drift exits with status 1 and
|
|
418
|
+
requires specification and fixture review; the command never enables an
|
|
419
|
+
extension, changes authority or availability logic, or issues reverse search.
|
|
420
|
+
|
|
421
|
+
`npm run service-dependencies:audit` validates the passive service-dependency
|
|
422
|
+
catalogue entirely offline. It checks the catalogue digest, duplicate
|
|
423
|
+
identifiers and target suffixes, evidence classes, source and licence
|
|
424
|
+
treatment, source dates, and a fixed 180-day review age. `--json` emits
|
|
425
|
+
`whoisleuth.service-dependency-signature-audit` version 1. A stale or changed
|
|
426
|
+
signature requires manual provider and benign-fixture review; the command
|
|
427
|
+
never resolves a target, checks an account, or tests claimability.
|
|
428
|
+
|
|
429
|
+
## Offline Risk calibration
|
|
430
|
+
|
|
431
|
+
`risk-calibrate` replays a versioned analyst-labelled fixture dataset through
|
|
432
|
+
the exact Risk model shared with the web application. It does not perform a
|
|
433
|
+
lookup, contact the hosted deployment, persist the dataset, alter model weights,
|
|
434
|
+
choose a threshold, or tune the model automatically. The report compares fixed
|
|
435
|
+
thresholds from 40 through 90 and identifies 70 as the current review band. It
|
|
436
|
+
also reports F1, balanced accuracy, Wilson 95% intervals, deliberately small
|
|
437
|
+
scan-depth and review-reason strata, and a current-versus-previous model replay.
|
|
438
|
+
Strata with fewer than 20 included labels are marked insufficient rather than
|
|
439
|
+
presented as reliable performance estimates.
|
|
440
|
+
|
|
441
|
+
The JSON report derives a bounded `interoperabilityTags` list from the
|
|
442
|
+
analyst-owned disposition and review reason. The mapping uses reviewed MISP
|
|
443
|
+
taxonomy terms only where the meaning is unambiguous. Tags do not change a
|
|
444
|
+
score, disposition, or case, and no MISP service is contacted.
|
|
445
|
+
|
|
446
|
+
Input is capped at 2 MiB and 500 records. Each record requires a unique bounded
|
|
447
|
+
ID, an ASCII DNS hostname, one existing analyst disposition, and a strict
|
|
448
|
+
whitelist of current scoring inputs. Unknown fields are discarded; arrays,
|
|
449
|
+
strings, ages, booleans, provider records, and external findings are revalidated
|
|
450
|
+
and bounded before scoring. The accepted dataset envelope is:
|
|
451
|
+
|
|
452
|
+
```json
|
|
453
|
+
{
|
|
454
|
+
"schema": "whoisleuth.risk-calibration-dataset",
|
|
455
|
+
"version": 2,
|
|
456
|
+
"records": [
|
|
457
|
+
{
|
|
458
|
+
"id": "fixture-1",
|
|
459
|
+
"domain": "login.example.test",
|
|
460
|
+
"analystDisposition": "confirmed_abuse",
|
|
461
|
+
"reviewReasonCode": "confirmed_credential_abuse",
|
|
462
|
+
"evidence": {
|
|
463
|
+
"availability": "registered",
|
|
464
|
+
"mutationTypes": ["dictionary"],
|
|
465
|
+
"hasPasswordField": true
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
]
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
`confirmed_abuse` is treated as a positive calibration label; `false_positive`
|
|
473
|
+
and `expected` are negative labels. `unreviewed`, `suspicious`, and
|
|
474
|
+
`closed_no_action` remain contextual and are excluded from threshold quality
|
|
475
|
+
metrics, as are records for which Risk is not applicable. Terminal output caps
|
|
476
|
+
its record list at 100 while `--json` retains the complete bounded report,
|
|
477
|
+
factor breakdowns, score bands, confidence intervals, bounded strata, model
|
|
478
|
+
comparison counts, and per-threshold confusion metrics. Analyst
|
|
479
|
+
dispositions and heuristic scores are review context: neither proves
|
|
480
|
+
maliciousness or safety.
|
|
481
|
+
|
|
482
|
+
The authenticated Monitor workspace can deliberately create the accepted
|
|
483
|
+
dataset from explicitly selected cases. Selection is available only when a case
|
|
484
|
+
has a reviewed disposition and retained normalised evidence. That local export
|
|
485
|
+
contains the case ID, domain, disposition, optional reviewed reason code, and a
|
|
486
|
+
bounded whitelist of current scoring inputs. A matched review-language signal
|
|
487
|
+
is exported as a fixed indicator, not as captured page text. It excludes notes,
|
|
488
|
+
tags, assertions, actions, contacts, raw
|
|
489
|
+
source data, provider payloads, and stored Risk scores. Before download, a local
|
|
490
|
+
review step shows the selected, included, and excluded counts, the domains and
|
|
491
|
+
dispositions that will be written, and the excluded evidence classes. The CLI
|
|
492
|
+
still validates every exported record and never trains, tunes, or changes the
|
|
493
|
+
Risk model.
|
|
494
|
+
|
|
495
|
+
## Lookalike review-yield calibration
|
|
496
|
+
|
|
497
|
+
`lookalike-calibrate [dataset.json]` summarises reviewed candidate outcomes by
|
|
498
|
+
mutation family without retaining domains, candidate identifiers, analyst
|
|
499
|
+
notes, or evidence values in its output. Input uses
|
|
500
|
+
`whoisleuth.lookalike-calibration-input` version 1, is capped at 2 MiB and 5,000
|
|
501
|
+
unique opaque records, and requires one reviewed disposition plus from 1 to 12
|
|
502
|
+
bounded mutation-family identifiers per record.
|
|
503
|
+
|
|
504
|
+
The report keeps exact disposition counts, review-lead rate, false-positive
|
|
505
|
+
rate, and a per-family sample state. Fewer than 20 reviewed observations remains
|
|
506
|
+
`insufficient`. The command is diagnostic only: it does not tune candidate
|
|
507
|
+
generation, filtering, ranking, or Risk, and a useful historical yield does not
|
|
508
|
+
establish that a current candidate is malicious or safe.
|
|
509
|
+
|
|
510
|
+
## Offline artefact verification
|
|
511
|
+
|
|
512
|
+
`verify-artifact` validates a supported local artefact without printing its
|
|
513
|
+
evidence contents. Input is capped at 15 MiB. It currently recognises ordinary
|
|
514
|
+
and encrypted workspace archives, case-response packets, acquisition-decision
|
|
515
|
+
exports, domain-comparison exports, Bulk mail-exposure exports, and Bulk review
|
|
516
|
+
manifests. It also verifies the internal digests of a complete investigation
|
|
517
|
+
capsule, including its evidence, brief, graph, and optional analyst-record
|
|
518
|
+
projections, and that capsule can be passed to `sign-artifact`. It also validates the bounded versioned structure of saved CLI
|
|
519
|
+
Lookup JSON. Because saved Lookup documents do not embed a checksum or
|
|
520
|
+
signature, that result is reported as `structure_valid` with content integrity
|
|
521
|
+
explicitly unchecked. Saved Lookup validation retains its stricter 8 MiB
|
|
522
|
+
document ceiling.
|
|
523
|
+
|
|
524
|
+
For an ordinary workspace archive, the command validates the versioned
|
|
525
|
+
structure, section byte counts, record counts, and per-section checksums. For a
|
|
526
|
+
supported signed review artefact, it validates the declared SHA-256 digest. An
|
|
527
|
+
encrypted archive without `--passphrase-file` can establish only that the
|
|
528
|
+
envelope is structurally valid; it reports `envelope_valid` and explicitly
|
|
529
|
+
leaves authenticated encryption and inner checksums unchecked.
|
|
530
|
+
|
|
531
|
+
Supplying a separate passphrase file, capped at 1 KiB, allows authenticated
|
|
532
|
+
decryption followed by the ordinary workspace checks. The passphrase is never
|
|
533
|
+
accepted as a command-line value, printed, or retained. Verification detects
|
|
534
|
+
changes against the artefact's declared contract; it does not authenticate the
|
|
535
|
+
analyst or establish that an observation was accurate or remains current.
|
|
536
|
+
|
|
537
|
+
## Privacy-safe source reliability report
|
|
538
|
+
|
|
539
|
+
`source-report` summarises source states, durations, truncation, and rate-limit
|
|
540
|
+
counts from one version-1 CLI Lookup, Bulk, or Bulk-item document, a JSON array
|
|
541
|
+
of up to 100 such documents, or an array containing only previously generated
|
|
542
|
+
source-reliability reports. Lookup documents and reports cannot be mixed.
|
|
543
|
+
Merged reports preserve exact state and sample totals and add bounded ranges
|
|
544
|
+
for each input report's median and p95 duration, a bounded source timeline,
|
|
545
|
+
fast/deep/unknown cohorts, and the earliest and latest valid report generation
|
|
546
|
+
times. Exact duplicate reports are rejected so one saved report is not
|
|
547
|
+
accidentally counted twice. Per-source failure, partial, truncation, and
|
|
548
|
+
rate-limit rates remain separate. The command does not pretend that aggregate
|
|
549
|
+
timing distributions can reconstruct the original target-level samples. Input
|
|
550
|
+
is capped at 12 MiB and traversal, source, duration, and output counts are
|
|
551
|
+
bounded.
|
|
552
|
+
|
|
553
|
+
The report retains only fixed source identifiers and aggregate operational
|
|
554
|
+
counts. It does not output targets, queries, endpoints, source limitations, raw
|
|
555
|
+
evidence, or provider payloads. Observation-envelope duration and the
|
|
556
|
+
potentially overlapping Lookup timing are reported separately and never added
|
|
557
|
+
together. It is a local engineering diagnostic, not analytics, a source-quality
|
|
558
|
+
score, or evidence that a collector is currently healthy.
|
|
559
|
+
|
|
560
|
+
## Workspace archive inspection
|
|
561
|
+
|
|
562
|
+
`inspect-archive` verifies an ordinary or encrypted workspace archive and then
|
|
563
|
+
reports its versioned sections, counts, byte sizes, and checksum states without
|
|
564
|
+
printing stored records. An encrypted archive requires
|
|
565
|
+
`--passphrase-file`; passphrases are never accepted on the command line.
|
|
566
|
+
|
|
567
|
+
An optional `--search` is exact and case-insensitive across a fixed allowlist
|
|
568
|
+
of target fields. Default matches disclose only the section, field name, and a
|
|
569
|
+
SHA-256 digest. `--reveal` must be selected explicitly to print the matched
|
|
570
|
+
value. Notes, contacts, raw evidence, arbitrary object fields, and partial text
|
|
571
|
+
search are excluded. Unicode domain searches are canonicalized to their
|
|
572
|
+
DNS-safe ASCII form before exact comparison. `--require-match` makes a
|
|
573
|
+
no-match result fail for automation and is valid only with `--search`.
|
|
574
|
+
Traversal, input size, matches, depth, arrays, and strings are bounded.
|
|
575
|
+
|
|
576
|
+
## Optional evidence-package signing
|
|
577
|
+
|
|
578
|
+
`sign-artifact` signs a locally verified case-response packet or supported
|
|
579
|
+
review manifest with an externally managed Ed25519 private key:
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
node bin/whoisleuth.mts sign-artifact packet.json --private-key-file analyst-private.pem > packet.signed.json
|
|
583
|
+
node bin/whoisleuth.mts verify-signature packet.signed.json --public-key-file analyst-public.pem
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
The signed package embeds the public key, key identifier, signature time, and
|
|
587
|
+
original artefact. Verification first checks the exact package field set and
|
|
588
|
+
signature, then the artefact's own checksums or manifest. A result is reported
|
|
589
|
+
as `signature_valid`; signer trust is separately reported as `trusted_key` or
|
|
590
|
+
`embedded_key_only`. Without a separately supplied trusted public key,
|
|
591
|
+
verification establishes only that the package is internally self-consistent.
|
|
592
|
+
WHOISleuth does not generate, store, recover, rotate, publish, or establish
|
|
593
|
+
trust in signing keys.
|
|
594
|
+
|
|
595
|
+
## Bulk lookup
|
|
596
|
+
|
|
597
|
+
`bulk` accepts a newline-delimited file or stdin. It preserves input order,
|
|
598
|
+
removes case-insensitive duplicates, and returns a result for every retained
|
|
599
|
+
query. Input is capped at 1 MiB. Fast mode is capped at 500 queries with four
|
|
600
|
+
workers by default. Deep mode is capped at 50 queries with two workers by
|
|
601
|
+
default. Explicit concurrency cannot exceed eight in fast mode or three in
|
|
602
|
+
deep mode.
|
|
603
|
+
|
|
604
|
+
Bulk uses the shared compact lookup response, so it does not retain raw RDAP
|
|
605
|
+
objects or WHOIS response bodies. `--json` returns one bounded collection;
|
|
606
|
+
`--jsonl` emits one self-contained versioned item per line. `--domains` emits
|
|
607
|
+
only successfully normalised domain names, while `--queries` preserves the
|
|
608
|
+
selected bounded input queries and is suitable for an exact retry queue.
|
|
609
|
+
Version 2 adds a
|
|
610
|
+
bounded `dnsSummary` projection for observed A, AAAA, NS, and MX records plus
|
|
611
|
+
null MX, SPF, and DMARC state. `--csv` writes fixed columns for automation,
|
|
612
|
+
including the DNS summaries and explicit outcome.
|
|
613
|
+
|
|
614
|
+
`--registered-only`, `--inconclusive-only`, and `--errors-only` are mutually
|
|
615
|
+
exclusive output filters. The first retains registered, for-sale, and expiring
|
|
616
|
+
authority-aware states. The second retains unknown authority states and failed
|
|
617
|
+
rows. The third retains only rows whose lookup operation failed, which is useful
|
|
618
|
+
for producing an exact retry queue without treating an inconclusive successful
|
|
619
|
+
lookup as an operational error. Filtering does not change collection and never
|
|
620
|
+
converts an unavailable or failed source into an unregistered result. Machine
|
|
621
|
+
documents record collected and emitted counts. A mixture of successful and
|
|
622
|
+
failed queries exits with code 4 while preserving every collected result before
|
|
623
|
+
output filtering. For example, `bulk domains.txt --queries --errors-only`
|
|
624
|
+
produces only the exact failed inputs while retaining the partial-failure exit.
|
|
625
|
+
|
|
626
|
+
## Certificate Transparency search
|
|
627
|
+
|
|
628
|
+
`ct-search` accepts one keyword as an argument or on stdin and calls the same
|
|
629
|
+
bounded Certificate Transparency module as the web application. It contacts
|
|
630
|
+
the upstream public log search service directly from the local machine; it
|
|
631
|
+
does not call the hosted WHOISleuth deployment. Quote a multi-word keyword so
|
|
632
|
+
the shell passes it as one argument.
|
|
633
|
+
|
|
634
|
+
Terminal output summarises certificate rows, observed hostnames, canonical
|
|
635
|
+
registrable-domain matches, observation times, bounded certificate-issuance
|
|
636
|
+
groups, and completeness. An issuance group contains names observed together
|
|
637
|
+
in one public certificate record; a cross-domain group is a review lead, not
|
|
638
|
+
an attribution or ownership finding. Terminal output shows at most 100 matches
|
|
639
|
+
and five hostnames per match, with explicit omission notes. `--json` returns
|
|
640
|
+
the complete bounded structured result in the versioned
|
|
641
|
+
`whoisleuth.cli.ct-search` schema. The certificate-group cap is reported
|
|
642
|
+
separately from the registrable-domain result cap. CT observations do not prove
|
|
643
|
+
that a website is active or malicious.
|
|
644
|
+
|
|
645
|
+
## Lookalike discovery
|
|
646
|
+
|
|
647
|
+
`discover` runs the same pure, bounded lookalike generator as the Discover
|
|
648
|
+
tool without making network requests. It accepts a brand label or a
|
|
649
|
+
domain with one suffix label. The default TLD set is `com,net,org`; replace it
|
|
650
|
+
with `--tlds com,net` when narrower coverage is wanted.
|
|
651
|
+
|
|
652
|
+
Generation presets are `common`, `impersonation`, and `all` (the default).
|
|
653
|
+
Keyboard-aware mutations support `qwerty` (the default), `azerty`, and
|
|
654
|
+
`qwertz`; `--keyboard all` uses their bounded combined neighbour set, including
|
|
655
|
+
adjacent number-row keys. Supply a UTF-8 text file with
|
|
656
|
+
`--dictionary terms.txt` to add up to 100 local terms when
|
|
657
|
+
using the `impersonation` or `all` preset. Dictionary input is capped at 4,096
|
|
658
|
+
bytes and individual terms at 32 characters. Machine output records accepted
|
|
659
|
+
and rejected term counts but never copies the private terms into its metadata.
|
|
660
|
+
|
|
661
|
+
Use `--families <ids>` instead of `--preset` to run an exact comma-separated
|
|
662
|
+
selection. Supported IDs are `character_addition`, `character_omission`,
|
|
663
|
+
`character_duplication`, `character_transposition`, `pluralization`,
|
|
664
|
+
`tld_embedding`, `www_prefix`, `hyphenation`, `separator_omission`,
|
|
665
|
+
`word_reordering`, `keyboard_substitution`, `keyboard_insertion`, `vowel_swap`,
|
|
666
|
+
`bitsquatting`, `ascii_homoglyph`, `unicode_homoglyph`,
|
|
667
|
+
`unicode_whole_label`, `unicode_homoglyph_depth_2`, `dictionary`,
|
|
668
|
+
`dictionary_token_replacement`, `tld_typo`, and `tld_substitution`. Machine
|
|
669
|
+
output records the normalised custom selection. A custom dictionary file
|
|
670
|
+
requires `dictionary` or `dictionary_token_replacement` in that selection.
|
|
671
|
+
|
|
672
|
+
`--domains` writes the unique candidate names only. `--snapshot <file>` keeps
|
|
673
|
+
one private, versioned local observation of the normalised seed, generation
|
|
674
|
+
configuration, custom-dictionary digest, and candidate set. The first run
|
|
675
|
+
creates the snapshot; later runs report added and removed candidates before
|
|
676
|
+
atomically replacing it. The snapshot never stores private dictionary terms.
|
|
677
|
+
Use the same command from a local scheduler such as cron or a system timer to
|
|
678
|
+
perform repeatable discovery runs. WHOISleuth does not install a scheduler,
|
|
679
|
+
run a background daemon, or infer that an added candidate is registered,
|
|
680
|
+
active, available, or malicious.
|
|
681
|
+
|
|
682
|
+
Dotted subdomain permutations are intentionally excluded because the lookup
|
|
683
|
+
pipeline validates a hostname's registrable parent. The CLI does not present
|
|
684
|
+
authoritative parent registration evidence as proof that a generated hostname
|
|
685
|
+
exists.
|
|
686
|
+
|
|
687
|
+
Terminal output is capped at 200 candidates with an explicit notice. Versioned
|
|
688
|
+
JSON and JSONL use `whoisleuth.cli.discover` schema version 2 and retain the
|
|
689
|
+
complete bounded candidate set and mutation provenance. Internationalised
|
|
690
|
+
candidates show the DNS-safe ASCII form and readable Unicode form together in
|
|
691
|
+
terminal output. The Impersonation and All presets can add up to six
|
|
692
|
+
deterministic whole-label Unicode candidates when every letter has a reviewed
|
|
693
|
+
same-script replacement. The command generates candidates only. It does not
|
|
694
|
+
claim that a domain is registered, active, or malicious.
|
|
695
|
+
|
|
696
|
+
`unicode_homoglyph_depth_2` is an explicit `--families` option and is never
|
|
697
|
+
enabled by a preset. It replaces exactly two ASCII letters with characters
|
|
698
|
+
from the same reviewed script, ranks curated mappings first, and returns at
|
|
699
|
+
most 256 candidates. JSON and terminal output report generated candidates,
|
|
700
|
+
cross-script or invalid combinations omitted by policy, and lower-ranked
|
|
701
|
+
label variants omitted by the family or overall generation budget.
|
|
702
|
+
|
|
703
|
+
## Supervised candidate scan
|
|
704
|
+
|
|
705
|
+
`discover-scan` composes the same bounded generator with the compact Lookup
|
|
706
|
+
collector. Unlike `discover`, it is an explicitly networked command. It selects
|
|
707
|
+
the first deterministic candidates up to `--scan-limit`, processes fixed
|
|
708
|
+
chunks sequentially, and retains each registration and DNS source state. Fast
|
|
709
|
+
mode is the default and accepts at most 500 candidates. Explicit `--deep` mode
|
|
710
|
+
is capped at 50 candidates and concurrency 3 because it can add WHOIS, DNS,
|
|
711
|
+
HTTP, TLS, page, and technology work for every selected candidate.
|
|
712
|
+
|
|
713
|
+
Use `--chunk-size 1-100`, `--concurrency 1-8`, and the existing private
|
|
714
|
+
`--checkpoint <file> [--resume]` boundary to control a run. The checkpoint is
|
|
715
|
+
tied to the exact ordered candidate set and scan mode. A new generation order,
|
|
716
|
+
limit, or mode requires another checkpoint. `--resolver` accepts at most three
|
|
717
|
+
literal IPv4 or IPv6 recursive-resolver addresses and applies them only to DNS
|
|
718
|
+
observations in this local run. Resolver hostnames and automatic third-party
|
|
719
|
+
resolver selection are not supported. The selected resolvers can observe the
|
|
720
|
+
queried candidates and may apply their own logging and retention policies.
|
|
721
|
+
|
|
722
|
+
An optional newline-delimited `--allowlist` accepts at most 500 domains and 64
|
|
723
|
+
KiB. Matching candidates remain in evidence and are labelled `suppressed` in
|
|
724
|
+
the review queue; the full list and unmatched entries are not copied into
|
|
725
|
+
output, while each matching scanned candidate necessarily exposes that match.
|
|
726
|
+
Allowlisting never deletes a result or asserts that it is safe. Output filters
|
|
727
|
+
select registered, inconclusive, acquisition-review, or
|
|
728
|
+
suppressed rows without changing whole-run counts or source health. JSON,
|
|
729
|
+
JSONL, CSV, and domain-list forms retain mutation provenance and explicit
|
|
730
|
+
review lanes. The queue provides next manual actions, not automatic lookup,
|
|
731
|
+
acquisition, blocking, submission, or enforcement.
|
|
732
|
+
|
|
733
|
+
Deep compact results can group exact shared addresses, nameservers, and mail
|
|
734
|
+
servers observed across at least two candidates. These bounded relationships
|
|
735
|
+
are pivot leads only. Common hosting, delivery, registrar, and mail services
|
|
736
|
+
can produce the same relationship without common ownership or control.
|
|
737
|
+
|
|
738
|
+
`--observation-snapshot <file>` keeps a private versioned local projection of
|
|
739
|
+
registration state, confidence, and bounded A, AAAA, NS, MX, null-MX, SPF, and
|
|
740
|
+
DMARC summaries for the exact candidate set, mode, and resolver selection.
|
|
741
|
+
Version 2 records registration and DNS observation times and latest component
|
|
742
|
+
states separately; version 1 is read and normalised on the next write. A later
|
|
743
|
+
identical run reports material field changes only for components collected
|
|
744
|
+
with enough completeness to support the comparison. If registration or DNS is
|
|
745
|
+
partial or unavailable, the prior usable evidence for that component is
|
|
746
|
+
preserved and the attempt is reported as unavailable rather than converting
|
|
747
|
+
missing data into a removal. Raw registry
|
|
748
|
+
records, contacts, page content, and request details are excluded. Snapshot
|
|
749
|
+
differences are review prompts, not proof of ownership, control, intent,
|
|
750
|
+
safety, or maliciousness.
|
|
751
|
+
|
|
752
|
+
## Domain posture audit
|
|
753
|
+
|
|
754
|
+
`posture` runs the standard-profile form of the owned-domain DNS and
|
|
755
|
+
email-security audit used by Brand Profiles. It queries registry status and
|
|
756
|
+
DNSSEC evidence, nameserver delegation, SPF, DMARC, MX, CAA, MTA-STS, TLS-RPT,
|
|
757
|
+
and BIMI directly from the local machine. Literal SPF include and redirect
|
|
758
|
+
branches are expanded within fixed depth, policy-query, DNS-term, void-answer,
|
|
759
|
+
cycle, and time bounds. External DMARC reporting authorisation and external
|
|
760
|
+
nameserver, mail, SPF, and reporting dependencies are separately reported.
|
|
761
|
+
Supply up to ten known active and retired DKIM selectors in total with
|
|
762
|
+
`--selectors selector1,selector2` and `--retired-selectors selector0`;
|
|
763
|
+
selectors cannot be reliably discovered from DNS, so no-selector output
|
|
764
|
+
reports DKIM as not checked. Use `--mail-profile standard`,
|
|
765
|
+
`--mail-profile defensive-no-mail`, or `--mail-profile parked` to make the
|
|
766
|
+
intended mail posture explicit rather than inferring it from DNS.
|
|
767
|
+
|
|
768
|
+
Terminal output shows each pass, review, action, or informational result and
|
|
769
|
+
caps displayed records at five per check with an explicit omission notice.
|
|
770
|
+
Versioned JSON retains the complete bounded report, including SPF traversal,
|
|
771
|
+
DMARC authorisation, and dependency provenance. Warnings and dangers are
|
|
772
|
+
findings rather than command failures; transient resolver or policy-fetch
|
|
773
|
+
failures remain informational and should be retried before changing DNS.
|
|
774
|
+
|
|
775
|
+
## HTTP intelligence
|
|
776
|
+
|
|
777
|
+
`http` runs the same bounded safe homepage probe used by a deep lookup. It
|
|
778
|
+
tries HTTPS before HTTP, follows only validated public-network redirects, caps
|
|
779
|
+
each attempt at six seconds, and captures at most 300,000 response bytes for
|
|
780
|
+
hashing and metadata. A non-success HTTP response still proves that a web
|
|
781
|
+
service answered; failure of both schemes remains inconclusive rather than
|
|
782
|
+
evidence that no website exists.
|
|
783
|
+
|
|
784
|
+
The command reports normalised redirect, response, content, selected security-
|
|
785
|
+
header, body-hash, completeness, and attempt provenance. Captured homepage text
|
|
786
|
+
is never written to terminal or JSON output. Query strings are removed by the
|
|
787
|
+
shared HTTP evidence normalizer, and terminal values are additionally bounded
|
|
788
|
+
and control-safe.
|
|
789
|
+
|
|
790
|
+
## TLS intelligence
|
|
791
|
+
|
|
792
|
+
`tls` runs the same bounded one-connection TLS collector used by deep lookups.
|
|
793
|
+
It resolves the hostname once through the public-address guard, validates every
|
|
794
|
+
answer, connects directly to the first validated address on port 443, and keeps
|
|
795
|
+
the original hostname for SNI and certificate identity checking. Resolution
|
|
796
|
+
and handshake work share a five-second deadline.
|
|
797
|
+
|
|
798
|
+
The report retains the negotiated protocol, ALPN and cipher, runtime trust and
|
|
799
|
+
hostname checks, certificate validity, bounded subject and issuer names, SANs,
|
|
800
|
+
SAN class counts, signature algorithm and OID, extended-key-usage purposes,
|
|
801
|
+
classified Authority Information Access presence counts, SHA-256 certificate
|
|
802
|
+
and public-key fingerprints, a bounded certificate chain, and neutral
|
|
803
|
+
findings. AIA responder and issuer locations are classified as HTTP, HTTPS, or
|
|
804
|
+
other and then discarded; they are not retained or followed. The collector
|
|
805
|
+
stores no certificate bytes, session material, or application data and does
|
|
806
|
+
not enumerate supported protocol or cipher suites.
|
|
807
|
+
A failed collection is inconclusive rather than proof that no TLS service
|
|
808
|
+
exists.
|
|
809
|
+
|
|
810
|
+
## Registry-source comparison
|
|
811
|
+
|
|
812
|
+
`compare` reads one version-1 `whoisleuth.cli.lookup` domain document from a
|
|
813
|
+
file or stdin and emits a version-3 `whoisleuth.cli.compare` document. Its
|
|
814
|
+
primary comparison reconciles normalised registry RDAP and WHOIS fields. When
|
|
815
|
+
the saved deep lookup also represents the optional registrar RDAP follow-up,
|
|
816
|
+
an additional sibling comparison reconciles the portable registry and
|
|
817
|
+
registrar RDAP publication fields. The command does not repeat the lookup,
|
|
818
|
+
contact the hosted deployment, or treat a conflict as a command failure. Use a
|
|
819
|
+
deep lookup when multiple sources are wanted; a fast lookup deliberately
|
|
820
|
+
records WHOIS as skipped, and comparison preserves that state as unavailable
|
|
821
|
+
rather than misreporting RDAP-only publication.
|
|
822
|
+
|
|
823
|
+
Input is capped at 8 MiB and must retain the lookup schema, mode, source
|
|
824
|
+
diagnostics, and normalised parsed source sections. Per-value, list, and event
|
|
825
|
+
bounds are revalidated because saved JSON is treated as untrusted input. A
|
|
826
|
+
successful registrar source must include a normalised object and agree with
|
|
827
|
+
its diagnostic status. Raw RDAP JSON, registrar contacts and source-specific
|
|
828
|
+
handles, WHOIS response bodies, availability evidence, and unrelated lookup
|
|
829
|
+
fields are not copied into the comparison result.
|
|
830
|
+
|
|
831
|
+
When the saved Lookup contains valid version-5, version-6, or version-7, context-only
|
|
832
|
+
registry-access diagnostics, version 3 retains their bounded suffix, access profiles, and
|
|
833
|
+
limitation. Terminal output labels the same collection context explicitly.
|
|
834
|
+
It explains source reachability only and cannot decide registration,
|
|
835
|
+
availability, ownership, safety, or maliciousness. Older documents and saved
|
|
836
|
+
lookups without that context do not gain a registry-access field.
|
|
837
|
+
|
|
838
|
+
Terminal and versioned JSON output cover domain identity, registry object ID,
|
|
839
|
+
registrar, registrar IANA ID, lifecycle dates, DNSSEC, statuses, and name
|
|
840
|
+
servers. The shared comparison model normalises harmless case, punctuation,
|
|
841
|
+
ordering, and date-precision differences while distinguishing conflicts,
|
|
842
|
+
one-source publication, redaction, incomplete sources, and unavailable
|
|
843
|
+
sources. This is source reconciliation, not an availability, ownership, or
|
|
844
|
+
maliciousness decision.
|
|
845
|
+
|
|
846
|
+
## Static page comparison
|
|
847
|
+
|
|
848
|
+
`page-compare <left.json> <right.json>` reads two different version-1 saved
|
|
849
|
+
Deep domain Lookup documents and compares the already-retained static page
|
|
850
|
+
identity, exact and perceptual favicon evidence, technology identifiers, TLS
|
|
851
|
+
issuer label, and TLS public-key fingerprint. DOM structure is compared first
|
|
852
|
+
by its exact bounded tag-sequence digest, then, when both captures provide it,
|
|
853
|
+
by a parse5-tokenized structural SimHash. It makes no request and requires
|
|
854
|
+
complete supported static page-identity observations on both sides.
|
|
855
|
+
|
|
856
|
+
Each page component remains independent. Exact, similar, overlapping,
|
|
857
|
+
different, unavailable, and partial evidence are not collapsed into a score.
|
|
858
|
+
Matching components are investigative relationships rather than proof of
|
|
859
|
+
copying, common ownership, control, intent, safety, or maliciousness. Static
|
|
860
|
+
comparison does not execute JavaScript; use the optional local rendered
|
|
861
|
+
capture package only when that additional active behaviour is authorised.
|
|
862
|
+
|
|
863
|
+
## Passive mail exposure review
|
|
864
|
+
|
|
865
|
+
`mail-review [bulk.json|bulk.jsonl]` reads version-2 Bulk output locally and
|
|
866
|
+
summarises MX, null MX, SPF, DMARC, and mail-provider relationships. It keeps
|
|
867
|
+
authenticated mail, authentication gaps, incomplete authentication evidence,
|
|
868
|
+
no explicit MX, null MX, and incomplete DNS evidence as separate states.
|
|
869
|
+
Shared-provider relationships are based only on the registrable domain of an
|
|
870
|
+
observed MX hostname and do not establish shared ownership or control.
|
|
871
|
+
|
|
872
|
+
The command makes no DNS or SMTP request and retains no source path. It does
|
|
873
|
+
not test message acceptance, relay behaviour, mailbox existence, catch-all
|
|
874
|
+
behaviour, SMTP banners, or whether a mail server is rogue, safe, or malicious.
|
|
875
|
+
|
|
876
|
+
An analyst may add a bounded `tlsaEvidence` object to a version-2 Bulk item for
|
|
877
|
+
offline DANE review. It must name the exact `_25._tcp.<mx-host>` service, and
|
|
878
|
+
that host must occur in the same item's retained MX evidence. A certificate
|
|
879
|
+
association match is complete only when `dnssecState` is `validated`. TLSA
|
|
880
|
+
usages 0 and 1 additionally require `pkixValidationState: "validated"`.
|
|
881
|
+
Usages 0 and 2 compare against the bounded `authorityMaterials` array; usages
|
|
882
|
+
1 and 3 compare against the supplied leaf certificate or SPKI. Mismatched
|
|
883
|
+
service names, missing certificate roles, unvalidated prerequisites, malformed
|
|
884
|
+
material, and truncation remain invalid, partial, or untrusted rather than
|
|
885
|
+
becoming a DANE match.
|
|
886
|
+
|
|
887
|
+
## Offline supplied-evidence review
|
|
888
|
+
|
|
889
|
+
`review-evidence [evidence.json]` accepts one version-1 JSON input and performs
|
|
890
|
+
only deterministic local parsing, comparison, or request planning. Input is
|
|
891
|
+
capped at 16 MiB. The command never retrieves DNS, opens SMTP, contacts an
|
|
892
|
+
RDAP server, submits a resolver query, or looks up an address in a hosted
|
|
893
|
+
GeoIP service.
|
|
894
|
+
|
|
895
|
+
Supported input schemas are:
|
|
896
|
+
|
|
897
|
+
- `whoisleuth.dnssec-evidence-input`: compares bounded DS and DNSKEY records,
|
|
898
|
+
recalculates DNSKEY key tags and supported DS digests, and reviews supplied
|
|
899
|
+
RRSIG validity windows at `observedAt`. `consistent` describes only the
|
|
900
|
+
supplied DS/DNSKEY relationship. It does not authenticate a chain to a root
|
|
901
|
+
trust anchor, validate RRSIG cryptography, or prove that a missing record is
|
|
902
|
+
absent.
|
|
903
|
+
- `whoisleuth.tlsa-evidence-input`: binds TLSA records to one explicit
|
|
904
|
+
`_port._transport.hostname` service and compares supplied leaf or authority
|
|
905
|
+
certificate/SPKI bytes. `matched` requires independently validated DNSSEC;
|
|
906
|
+
PKIX-TA and PKIX-EE usages also require an independently validated PKIX path.
|
|
907
|
+
The command does not retrieve the certificate or negotiate STARTTLS.
|
|
908
|
+
- `whoisleuth.rdap-search-input`: normalises a supplied RDAP search-help
|
|
909
|
+
response and prepares an exact supported reverse-search request without
|
|
910
|
+
sending it.
|
|
911
|
+
- `whoisleuth.rpki-route-input`: compares a route prefix and origin ASN with a
|
|
912
|
+
bounded analyst-supplied VRP set.
|
|
913
|
+
- `whoisleuth.local-geoip-query`: queries an analyst-supplied bounded prefix
|
|
914
|
+
database whose source, version, and licence metadata remain in the result.
|
|
915
|
+
- `whoisleuth.local-mmdb-query`: with an explicit `--mmdb <database-file>`,
|
|
916
|
+
queries one analyst-supplied MaxMind DB file locally. The query document must
|
|
917
|
+
carry `address`, `sourceLabel`, `databaseVersion`, and `license`. WHOISleuth
|
|
918
|
+
does not bundle, download, update, license, transmit, or retain the database.
|
|
919
|
+
The nested result uses the same address, network, country, region, city, ASN,
|
|
920
|
+
source, and limitation fields as the bounded JSON-prefix path. This mode is
|
|
921
|
+
CLI-only because a hosted or browser deployment cannot open an analyst's
|
|
922
|
+
local file; its JSON result remains portable to website-compatible evidence
|
|
923
|
+
archives and reports.
|
|
924
|
+
- `whoisleuth.encrypted-dns-plan-input`: validates an explicitly reviewed
|
|
925
|
+
encrypted-DNS provider contract and prepares a bounded query plan. It does
|
|
926
|
+
not execute the plan.
|
|
927
|
+
|
|
928
|
+
The common output is `whoisleuth.cli.offline-evidence-review` version 1. It
|
|
929
|
+
retains the nested result's explicit state and limitations. A locally
|
|
930
|
+
consistent relationship is not converted into a claim about current
|
|
931
|
+
publication, ownership, safety, or maliciousness.
|
|
932
|
+
|
|
933
|
+
## Domain control manifests
|
|
934
|
+
|
|
935
|
+
`domain-control [input.json]` builds or reviews a bounded desired-state
|
|
936
|
+
manifest without making a request or changing configuration. A manifest input
|
|
937
|
+
uses `whoisleuth.domain-control-manifest-input` version 1, includes an expiry,
|
|
938
|
+
and may record expected nameservers, DS, MX, CAA, TLS issuer, TLS SPKI
|
|
939
|
+
fingerprint, registrar-lock preference, and a renewal-review date for up to 100
|
|
940
|
+
domains. Empty fields mean unconfigured; they do not require a record to be
|
|
941
|
+
absent.
|
|
942
|
+
|
|
943
|
+
The emitted `whoisleuth.domain-control-manifest` version 1 is normalised and
|
|
944
|
+
protected by a canonical SHA-256 digest. It can also be passed to
|
|
945
|
+
`sign-artifact` for an optional local Ed25519 signature. Integrity and
|
|
946
|
+
signature verification detect changes and authenticate possession of the
|
|
947
|
+
selected key; neither establishes that the desired state is correct. Manifest
|
|
948
|
+
input and verification reject unknown fields, and verification rejects
|
|
949
|
+
non-canonical content rather than returning a shape that differs from the
|
|
950
|
+
normalised contract covered by the digest.
|
|
951
|
+
|
|
952
|
+
A review input uses `whoisleuth.domain-control-review-input` version 1 and
|
|
953
|
+
contains one manifest plus separately attributed observations. Only a complete
|
|
954
|
+
`observed` field may produce `drift`. Partial, unavailable, unsupported, and
|
|
955
|
+
missing observations remain inconclusive, and unrelated observations are
|
|
956
|
+
counted but ignored. The review performs no DNS, RDAP, HTTP, TLS, SMTP, or
|
|
957
|
+
registrar request.
|
|
958
|
+
|
|
959
|
+
## Domain assurance review
|
|
960
|
+
|
|
961
|
+
`assurance [input.json]` reviews one bounded analyst-authored workflow entirely
|
|
962
|
+
offline. Version 1 inputs remain supported. Version 2 adds optional
|
|
963
|
+
`customChecks` to retirement inputs and remains the current normalised output
|
|
964
|
+
version. The input uses `whoisleuth.domain-assurance.input` and one of three
|
|
965
|
+
fixed kinds:
|
|
966
|
+
|
|
967
|
+
- `planned-change` records a bounded window, evidence-backed milestones,
|
|
968
|
+
rollback criteria, and post-change checks;
|
|
969
|
+
- `recovery-dependencies` reviews up to 100 domains across registrar, DNS,
|
|
970
|
+
mail, certificate, and recovery providers and highlights exact provider
|
|
971
|
+
concentration; and
|
|
972
|
+
- `retirement` checks the fixed decommissioning controls without treating an
|
|
973
|
+
unchecked item as complete. Version 2 can add up to 20 analyst-defined checks,
|
|
974
|
+
each with a bounded identifier, label, expected boolean, and true, false, or
|
|
975
|
+
null observed value.
|
|
976
|
+
|
|
977
|
+
Provider labels, readiness confirmations, evidence references, and expected
|
|
978
|
+
states remain analyst assertions. The output preserves incomplete and
|
|
979
|
+
needs-review states and gives every negative state an explicit review reason.
|
|
980
|
+
The versioned input rejects unknown fields and contradictory evidence attached
|
|
981
|
+
to an unfinished check. It stores no credentials, makes no request, and changes
|
|
982
|
+
no registrar, DNS, mail, certificate, or recovery configuration.
|
|
983
|
+
|
|
984
|
+
## Pre-sharing review
|
|
985
|
+
|
|
986
|
+
`sharing-review [artifact.json]` performs a redacted local preflight before an
|
|
987
|
+
analyst deliberately shares an artefact. The analyst must select a TLP 2.0
|
|
988
|
+
marking, a recipient scope, and a bounded purpose. Optional confirmations record
|
|
989
|
+
human review, personal-data review, and redaction review. The command validates
|
|
990
|
+
supported artefact integrity where possible, detects stricter embedded TLP
|
|
991
|
+
markings, and reports only key-category counts plus bounded, control-free schema
|
|
992
|
+
and integer version metadata. It emits no content values and copies no raw
|
|
993
|
+
evidence.
|
|
994
|
+
|
|
995
|
+
An imported stricter marking cannot be downgraded by the requested marking, and
|
|
996
|
+
recipient scope is checked against the effective marking. Evading this local
|
|
997
|
+
preflight remains possible, so the result is a review aid rather than access
|
|
998
|
+
control, legal advice, or recipient authorisation.
|
|
999
|
+
|
|
1000
|
+
## Fixed investigation plans
|
|
1001
|
+
|
|
1002
|
+
`workflow-plan <recipe> <domain|brand>` turns one of four fixed recipes into a
|
|
1003
|
+
versioned `whoisleuth.cli.investigation-plan` document. Supported recipes are
|
|
1004
|
+
`domain-triage`, `lookalike-review`, `owned-domain-review`, and
|
|
1005
|
+
`historical-comparison`. Each step names an existing CLI command, carries its
|
|
1006
|
+
arguments as a JSON array, declares whether it is offline or networked, and
|
|
1007
|
+
marks network-disclosure or analyst-selection approval gates.
|
|
1008
|
+
|
|
1009
|
+
The command is plan-only. It does not execute a step, construct a shell script,
|
|
1010
|
+
interpret a placeholder as a file path, read an artefact, make a request,
|
|
1011
|
+
change a case, or submit evidence. Analysts deliberately run selected commands
|
|
1012
|
+
after reviewing their collection boundaries. This provides repeatable
|
|
1013
|
+
domain-specific workflows without an arbitrary automation or plugin surface.
|
|
1014
|
+
|
|
1015
|
+
## Optional local rendered capture
|
|
1016
|
+
|
|
1017
|
+
`packages/web-capture` is a private repo-local Playwright package, not part of
|
|
1018
|
+
the distributable core CLI or hosted application. It requires an explicit
|
|
1019
|
+
authorisation flag and one new output directory:
|
|
1020
|
+
|
|
1021
|
+
```bash
|
|
1022
|
+
npm run capture:local -- https://example.test --output-dir ./capture-example --authorize-rendered-capture
|
|
1023
|
+
npm run capture:compare -- ./official/manifest.json ./candidate/manifest.json --json
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
It writes a fixed 1024x768 PNG, screenshot SHA-256 and perceptual dHash, a DOM
|
|
1027
|
+
digest containing hashes and bounded element counts rather than markup or page
|
|
1028
|
+
text, and a version-2 `whoisleuth.web-capture-manifest` compatible with the
|
|
1029
|
+
Cases importer. Version-1 manifests remain importable.
|
|
1030
|
+
|
|
1031
|
+
Rendered capture executes page JavaScript. It caps HTTP(S) requests and request
|
|
1032
|
+
hostnames, blocks downloads, service workers, WebSockets, non-read methods,
|
|
1033
|
+
credentials, non-default ports, and private or reserved addresses, and retains
|
|
1034
|
+
no request path, query, headers, bodies, cookies, credentials, DOM markup, or
|
|
1035
|
+
page text. The browser connection is not pinned to the address checked before
|
|
1036
|
+
each hostname's first request, so DNS rebinding remains a residual risk. Use a
|
|
1037
|
+
disposable network-restricted environment for untrusted targets.
|
|
1038
|
+
|
|
1039
|
+
The separate offline comparison command accepts two selected version-2 local
|
|
1040
|
+
capture manifests. It first verifies each referenced screenshot and DOM digest
|
|
1041
|
+
against its declared size, SHA-256, screenshot dimensions, and perceptual hash;
|
|
1042
|
+
it also validates bounded source and limitation metadata and requires the DOM,
|
|
1043
|
+
manifest, and source collection times to agree. It then
|
|
1044
|
+
compares screenshot distance, exact rendered DOM and visible-text digests,
|
|
1045
|
+
bounded element counts, page identity, technologies, and request-domain sets.
|
|
1046
|
+
It does not recrawl either target, reveal the input paths, or collapse the
|
|
1047
|
+
independent components into a similarity or maliciousness score. A missing
|
|
1048
|
+
perceptual hash remains unavailable rather than becoming a visual difference.
|
|
1049
|
+
|
|
1050
|
+
## Lookup evidence export
|
|
1051
|
+
|
|
1052
|
+
`export` converts one version-1 `whoisleuth.cli.lookup` domain document from a
|
|
1053
|
+
file or stdin into the same versioned `whoisleuth.lookup-evidence` JSON package
|
|
1054
|
+
produced by the web Lookup tool. It performs no lookup and writes only to
|
|
1055
|
+
stdout, so use ordinary shell redirection when a file is wanted. Pretty JSON is
|
|
1056
|
+
the default; `--compact` emits one compact JSON line for pipeline use, while
|
|
1057
|
+
`--markdown` produces a readable source-attributed summary and `--html`
|
|
1058
|
+
produces a self-contained printable report. Compact, Markdown, and HTML output
|
|
1059
|
+
are mutually exclusive.
|
|
1060
|
+
|
|
1061
|
+
The saved input is capped at 8 MiB and revalidated using the same schema,
|
|
1062
|
+
source-status, parsed-data, scalar, list, and event boundaries as `compare`.
|
|
1063
|
+
The export retains query context, source diagnostics, normalised registry data,
|
|
1064
|
+
raw registry RDAP JSON, the raw WHOIS referral chain, availability analysis,
|
|
1065
|
+
and the shared registry-source comparison. Version 23 can add the exact local
|
|
1066
|
+
SSLBL comparison already represented by a deep full Lookup. Version 21 adds the bounded registry
|
|
1067
|
+
lifecycle, disclosure, publication-quality, reconciliation, and abuse-routing
|
|
1068
|
+
interpretation derived from the already-collected sources. Registrar RDAP raw
|
|
1069
|
+
data, contacts, entities, links, notices, and source-specific handles remain
|
|
1070
|
+
excluded. Schema version 20 replaces selected security-policy values in
|
|
1071
|
+
retained HTTP evidence
|
|
1072
|
+
with presence-only markers and can include fixed response-policy findings from
|
|
1073
|
+
an already-represented deep lookup. Version 19 additionally retains the bounded credential-surface projection
|
|
1074
|
+
when the saved deep lookup represents it. Version 18 retains the bounded structured identity projection
|
|
1075
|
+
when the saved deep lookup represents it. Version 17 retains the normalised portable-field comparison, explicit
|
|
1076
|
+
source-health states, a strict bounded projection of observed network
|
|
1077
|
+
registration, an optional normalised security.txt disclosure source, HTTPS
|
|
1078
|
+
service-binding publications, and the passive browser-library profile when the
|
|
1079
|
+
saved deep lookup represents them. Raw IP RDAP payloads, security.txt response
|
|
1080
|
+
bodies, script references, matched script content, and contact entities remain
|
|
1081
|
+
excluded.
|
|
1082
|
+
|
|
1083
|
+
Markdown output summarises query context, assessment state, registry sources,
|
|
1084
|
+
source reconciliation, network observations, and collection diagnostics. It
|
|
1085
|
+
escapes all upstream text as untrusted content, bounds displayed values and
|
|
1086
|
+
lists, and deliberately omits raw RDAP JSON and full WHOIS response bodies.
|
|
1087
|
+
Use JSON when the complete evidence package or machine processing is required.
|
|
1088
|
+
The browser Console reuses this bounded Markdown renderer for domain results
|
|
1089
|
+
after first reducing the typed Lookup response to the known normalised fields
|
|
1090
|
+
needed by the report. It separately offers bounded IP and ASN summaries from
|
|
1091
|
+
their normalised RDAP and source-health fields; IP reports can also include
|
|
1092
|
+
bounded reverse-DNS context already collected by Lookup. The browser action
|
|
1093
|
+
does not change the CLI input or output contract.
|
|
1094
|
+
When a version-5, version-6, or version-7 lookup records a documented registry-access constraint, both
|
|
1095
|
+
Markdown and HTML include that context in collection diagnostics without
|
|
1096
|
+
changing its non-authoritative interpretation. Both formats also include the
|
|
1097
|
+
selected address and bounded network registration when present, together with
|
|
1098
|
+
the edge-versus-origin limitation.
|
|
1099
|
+
|
|
1100
|
+
HTML uses the same bounded summary model as Markdown. The generated document
|
|
1101
|
+
contains inline styling for screen and print, but no scripts, forms, active
|
|
1102
|
+
links, or external resources. A restrictive embedded Content Security Policy
|
|
1103
|
+
provides defence in depth when the local file is opened in a browser. All
|
|
1104
|
+
registry values are HTML-escaped and displayed as text.
|
|
1105
|
+
|
|
1106
|
+
This is a deliberately rich evidence package. Raw registry sources can contain
|
|
1107
|
+
publicly published contact data, and deep availability evidence can contain
|
|
1108
|
+
bounded website, DNS, mail, page-identity, and TLS observations. Review and
|
|
1109
|
+
secure the output before sharing it. The CLI does not add browser-only IDN
|
|
1110
|
+
profile analysis, so that optional evidence field is `null`.
|