Command-line reference¶
Every command's --help, exactly as veridor 0.1.0 prints it at commit
d215e1a. This page is generated from the tool itself, so a flag listed here
is a flag the code accepts. For what each command is for and how to use it,
start with the guides linked in each section.
verify is an alias for run, and also accepts a capsule file
(see capsule and verify).
veridor¶
usage: veridor [-h] [--version] <command> ...
Check that your API still agrees with the authorization you declared.
THE THREE VERBS
map What don't I cover, and what would a compromise reach?
Projects an OpenAPI inventory into an access matrix, a blast radius
per identity, boundary coverage, and ranked anomaly candidates.
Read-only by default, rate limited, and it refuses its own plan
before the first request. Candidates are hypotheses, never findings.
run Does the boundary hold, and did it change?
Issues every declared expectation as every declared identity, grades
marker evidence, runs multi-step sequences, diffs against a baseline
and sets the CI exit code. This is the gate.
mutate Would my suite actually catch a regression?
Rewrites your own expectations and reports the mutants your checks
failed to kill. A surviving mutant is a check that cannot fail.
Discover, declare, gate, then verify the gate. `map` proposes; you decide what
to declare; `run` holds you to it; `mutate` says whether holding you to it
means anything.
UTILITIES
capsule write a portable, runnable proof of ONE finding, to attach to
a disclosure. Carries no credential; the reader verifies it
against their own instance with `veridor verify <file>`
init write a starter configuration from an OpenAPI specification
coverage measure boundary coverage alone, issuing no request
new-marker print a fresh identity marker
report render a run as SARIF, JUnit, GitLab SAST or Code Quality,
or Markdown
serve open the local run history UI
`verify` remains an alias for `run`, and additionally takes a capsule:
`veridor verify proof.json` prints CONFIRMED / NOT_REPRODUCED / INCONCLUSIVE.
options:
-h, --help show this help message and exit
--version show program's version number and exit
commands:
<command> one of the verbs or utilities above; each takes its own --help
map¶
usage: veridor map [-h] --config CONFIG [--output-dir OUTPUT_DIR]
[--base-url BASE_URL] [--wait-for-target SECONDS] [--matrix MATRIX]
[--blast-radius BLAST_RADIUS] [--candidates CANDIDATES]
[--promote PROMOTE] [--require-complete] [--dry-run]
[--max-requests MAX_REQUESTS] [--rate REQUESTS_PER_SECOND]
[--max-run-seconds MAX_RUN_SECONDS]
[--on-retry-after {continue,stop}] [--timeout MATRIX_TIMEOUT]
[--include [METHOD ]/path/template]
[--ignore [METHOD ]/path/template] [--allow-mutating-method METHOD]
[--acknowledge-mutating TEXT] [--acknowledge-target TEXT]
[--baseline BASELINE] [--drift DRIFT_PATH]
[--fail-on-drift {none,opened,any}] --openapi OPENAPI
[--coverage COVERAGE_PATH] [--badge BADGE] [--base-path BASE_PATH]
[--allow-no-matches]
Project an OpenAPI inventory into an access matrix, then report every identity's
observed reach, the boundary coverage of your declaration, and the inconsistencies
worth declaring a check for. Nothing here is a finding: `map` produces no SARIF,
writes nothing into the run history, and nothing it discovers can fail a build. It
exits 0 when it produced a map -- unless you asked --fail-on-drift to gate on how this
matrix differs from an earlier one, which is a fact about two runs rather than a
verdict on one.
options:
-h, --help show this help message and exit
--config CONFIG path to veridor JSON config
--output-dir OUTPUT_DIR
artifact directory (default: .veridor)
--base-url BASE_URL override target.base_url
--wait-for-target SECONDS
before the first request, try a plain TCP connection to the
target's host and port once a second until one is accepted or
SECONDS pass (default: 0, no wait). Either way the run then
goes ahead and one stderr line says which it was; a target
that never accepted shows up as the probes' own transport
errors. For a target that is still starting; an accepted
connection does not mean the new version is serving (a
previous version's pod accepts at once). The check connects
directly even when a proxy carries the requests, and its line
then says so. Never changes a verdict
--matrix MATRIX also write the access matrix JSON here
--blast-radius BLAST_RADIUS
also write the blast radius JSON here
--candidates CANDIDATES
also write the candidates JSON here
--promote PROMOTE write a paste-ready, deliberately incomplete check per
candidate to this path. They are stubs: what the boundary IS
is your decision
safety envelope (read-only by default):
--require-complete additionally exit 3 when any probed cell went unmeasured -- a
rate limit, a 5xx, a timeout. Those operations are unverified
rather than verified. A grid where NOTHING was measured --
every probed cell unmeasured or a redirect, which Veridor does
not follow -- exits 3 with or without this flag. An --ignore,
matrix.ignore or --include that leaves nothing to probe -- no
operation, or only writes the read-only posture skips and
routes with no specimen -- is refused before the first request
(exit 2), so it never produces that empty grid
--dry-run print the plan and the safety posture, then stop. Nothing is
sent. Run this first against any target you did not provision
yourself
--max-requests MAX_REQUESTS
refuse to start when the plan is larger than this (default:
500). The refusal happens before the first request, and
reports the number it refused
--rate REQUESTS_PER_SECOND
requests per second, sequential (default: 2)
--max-run-seconds MAX_RUN_SECONDS
wall clock the whole matrix may take before it stops and
reports a partial grid (default: 1800)
--on-retry-after {continue,stop}
what a 429 carrying Retry-After means. continue (default)
counts it toward the ordinary rate-limit budget -- three
consecutive or five cumulative -- so a target that sends the
header gets the same tolerance as one that does not. stop
abandons the grid on the first one, which was the only
behaviour and cost a spec-compliant limiter 3.5x the coverage
of a rude one
--timeout MATRIX_TIMEOUT
per-request timeout (default: target.timeout_seconds)
--include [METHOD ]/path/template
probe only matching operations; repeatable. Matches by PATH
PREFIX, not as a glob or a regex: a match selects the route
and everything beneath it, '*' is an ordinary character, and
'{id}' must be spelled as the specification spells it
--ignore [METHOD ]/path/template
never probe matching operations; repeatable, adds to
matrix.ignore
--allow-mutating-method METHOD
name one mutating verb the matrix may send, e.g. DELETE.
Repeatable, never a wildcard, and inert without --acknowledge-
mutating
--acknowledge-mutating TEXT
the exact acknowledgement string mutating verbs require. Two
independent gates, so one stray flag cannot open both
--acknowledge-target TEXT
required verbatim when the target is not loopback or a private
address: 'I am authorized to test HOST'. The refusal prints
the exact string. This is friction, not a security boundary
drift -- authorization regression with nothing declared:
--baseline BASELINE a previous matrix JSON, or the directory `map` writes them to
(e.g. .veridor/matrix). Reports what moved since. Nobody
declares anything: the first run records, later runs diff. A
directory is for discovery only -- --fail-on-drift refuses it,
because this run writes into the same directory
--drift DRIFT_PATH write the machine-readable drift report JSON here. Needs
--baseline; without one it is refused, before anything is sent
--fail-on-drift {none,opened,any}
exit non-zero on drift (default: none). `opened` fails only on
changes that mean MORE access than the baseline -- a refusal
that became a success, or a response that now carries another
identity's marker. A redirect in the baseline that is served
now is not a refusal Veridor saw, so `opened` reports it and
`any` fails on it. `any` fails on any change. Either policy
exits 3 instead of 0 when not one cell was measured in both
this run and the baseline (a baseline recorded while the
target was down, say): no change could have been reported, so
that is not a pass. Discovery does not gate a build unless you
ask it to
boundary coverage (offline, static):
--openapi OPENAPI OpenAPI 3 or Swagger 2 JSON specification
--coverage COVERAGE_PATH
write the machine-readable coverage report JSON to this path
--badge BADGE write shields.io endpoint JSON for the coverage badge to this
path
--base-path BASE_PATH
spec path prefix to apply (default: auto-detect and show the
scoring)
--allow-no-matches report coverage as 'not measured' instead of failing when no
check path matches any operation
run¶
Guide: run and Write your access policy.
usage: veridor run [-h] [--config CONFIG] [--output-dir OUTPUT_DIR]
[--baseline BASELINE] [--base-url BASE_URL]
[--wait-for-target SECONDS] [--json JSON_PATH] [--sarif SARIF]
[--fail-on {new+confirmed,new,any,confirmed,none}] [--read-only]
[--require-clean-teardown] [--no-prove-liveness]
[--require-complete] [--report FORMAT=PATH] [--openapi OPENAPI]
[--coverage COVERAGE_PATH] [--badge BADGE] [--base-path BASE_PATH]
[--allow-no-matches]
[capsule]
Issue every declared expectation as every declared identity, grade the marker
evidence, execute any declared multi-step sequences, diff against a baseline, and set
the exit code. Sequences, their captures and identity sessions are declared in the
configuration (version 2) and run as part of this command.
positional arguments:
capsule a capsule written by `veridor capsule`. Runs the single check
it carries and prints CONFIRMED / NOT_REPRODUCED /
INCONCLUSIVE. This is what a maintainer receives attached to a
disclosure
options:
-h, --help show this help message and exit
--config CONFIG path to veridor JSON config
--output-dir OUTPUT_DIR
run history directory (default: .veridor)
--baseline BASELINE prior run JSON for regression comparison
--base-url BASE_URL override target.base_url
--wait-for-target SECONDS
before the first request, try a plain TCP connection to the
target's host and port once a second until one is accepted or
SECONDS pass (default: 0, no wait). Either way the run then
goes ahead and one stderr line says which it was; a target
that never accepted shows up as the probes' own transport
errors. For a target that is still starting; an accepted
connection does not mean the new version is serving (a
previous version's pod accepts at once). The check connects
directly even when a proxy carries the requests, and its line
then says so. Never changes a verdict
--json JSON_PATH write run JSON to this path
--sarif SARIF write SARIF 2.1.0 to this path
--fail-on {new+confirmed,new,any,confirmed,none}
policy used for the process exit code (default:
new+confirmed). new+confirmed: anything absent from the
baseline, PLUS anything proved by a marker regardless of age
-- the default because `new` alone is decided entirely by a
file the README asks you to commit, so a pull request that
adds a finding id to the baseline turns a live confirmed leak
into a green build. new: a finding absent from the baseline.
any: any finding at all. confirmed: only a finding whose
response actually carried another identity's marker. none:
never fail
--read-only refuse to load a configuration that can issue any request
outside GET, HEAD, or OPTIONS. Fails with exit 2 before a
single request is sent. Use this for any target you did not
personally provision
--require-clean-teardown
additionally exit 3 when a sequence teardown did not complete
cleanly. A real finding always wins the exit code
--no-prove-liveness skip the liveness controls. ON by default: for each GET, HEAD
or OPTIONS request whose declaration accepts only denial
statuses, Veridor issues ONE probe carrying a credential it
fabricated, written into every header the identities declare
but a standard non-credential one such as Accept (a header two
or more identities send with one value and whose name is not a
credential's, such as an API version, is sent unchanged, and
one declared only beside another credential, such as a tenant,
is left out), so a denial that any caller would have received
cannot pass as a boundary that refused this one; and the same
for a GET, HEAD or OPTIONS request that serves an identity
whose credential must be proven live, so a route that serves
anyone proves nothing. One extra request per such request --
not per identity -- and the fabricated value authenticates to
nothing; none is sent with POST, PUT, PATCH or DELETE, inside
a sequence, or when no identity declares a credential header.
A denial counts as unproven only for an identity that presents
a credential, in a check or sequence where some identity was
served, that nothing else in the run proved live (a 2xx it
drew on a request where an identity with no credential, or the
fabricated credential, was refused; or its marker_probe), and
whose fabricated credential drew the same denial, was not
sent, failed, or drew neither a refusal nor a success. Each is
printed and recorded in liveness.unproven; it moves the exit
code (to 3) only with --require-complete, so under the default
flags an expired token on an identity expected only to be
denied still exits 0. Turn it off and that denial is not even
reported
--require-complete additionally exit 3 when any probe went unanswered -- a rate
limit, a 5xx, a timeout -- when an expectation accepts both
access and denial, or when a denial counts as unproven in
liveness.unproven (see --no-prove-liveness). Those boundaries
are unverified rather than verified, and a CI job reads an
exit code, not a summary. A real finding always wins the exit
code
--report FORMAT=PATH also write this run as FORMAT to PATH; repeatable. FORMAT is
one of sarif, junit, gitlab-sast, codequality, markdown.
Written whatever the gate decides, since a failing gate is
when CI needs them. PATH may not be '-': `run` prints its own
summary on stdout
boundary coverage (offline, static):
--openapi OPENAPI OpenAPI 3 or Swagger 2 JSON specification; enables boundary
coverage
--coverage COVERAGE_PATH
write the machine-readable coverage report JSON to this path
--badge BADGE write shields.io endpoint JSON for the coverage badge to this
path
--base-path BASE_PATH
spec path prefix to apply (default: auto-detect and show the
scoring)
--allow-no-matches report coverage as 'not measured' instead of failing when no
check path matches any operation
mutate¶
Guide: mutate.
usage: veridor mutate [-h] --config CONFIG [--output-dir OUTPUT_DIR]
[--require-evaluable] [--base-url BASE_URL] [--json JSON_PATH]
[--text TEXT_PATH] [--badge BADGE] [--read-only]
[--max-requests MAX_REQUESTS] [--min-interval MIN_INTERVAL]
[--request-budget REQUEST_BUDGET] [--tier {1,2}] [--hooks HOOKS]
[--i-understand-tier-2-runs-my-commands]
Rewrite your declared expectations one at a time and report which mutants your suite
failed to kill. A surviving mutant is a check that cannot fail, which is a vacuous
check. This measures your declaration, not your target: nothing it reports is a
finding, it emits no SARIF, and its exit code is its own (0 all killed, 1 at least one
survivor, 2 nothing measured or something unresolved). Unlike `map`, this verb does
NOT require --acknowledge-target for a public host: it re-issues the requests YOU
declared in a configuration you wrote naming a base_url, which is what `veridor run`
does, so it takes `run`'s posture there rather than `map`'s. `map` fans out from a
specification across every identity and is gated for that reason. --read-only and
--max-requests apply here.
options:
-h, --help show this help message and exit
--config CONFIG path to veridor JSON config
--output-dir OUTPUT_DIR
artifact directory; reports land under <dir>/mutation
(default: .veridor)
--require-evaluable additionally exit 3 when a mutant could not be evaluated at
all. Those sit under LIMITS ON THE EVIDENCE and are kept out
of the score, which is right -- an unanswerable mutant is not
a measurement -- but it leaves a suite whose only cross-tenant
assertion cannot fail reporting 1.00 and exiting 0
--base-url BASE_URL override target.base_url
--json JSON_PATH also write the report JSON here
--text TEXT_PATH also write the rendered report here
--badge BADGE also write shields.io endpoint JSON here
--read-only refuse to load a configuration that can issue any request
outside GET, HEAD, or OPTIONS. Fails with exit 2 before a
single request is sent, at either tier. `mutate` re-issues
every request you declared, DELETE included, and then issues
more of its own: this is the same escape hatch `run --read-
only` gives you, on the same configuration
--max-requests MAX_REQUESTS
refuse to start when the plan -- baseline probes included --
is larger than this (default: 5,000). The refusal happens
before the first request and reports the number it refused.
--min-interval bounds the rate this target is hit at; this
bounds the volume, and unlike the two flags below it applies
at BOTH tiers. At tier 2 the baseline is charged once per hook
plus once for the unmutated run, each with its liveness
controls (one per denial-only GET/HEAD/OPTIONS request).
Session logins and sequence steps are not counted at tier 2
tier 1 -- expectation mutation (the default), and its request envelope:
--min-interval MIN_INTERVAL
minimum seconds between requests -- ALL of them, the baseline
probes included, because they are most of the traffic
(default: 0.5, i.e. 2 req/s, matching `map`). Pass 0 to turn
it off against a fixture you own
--request-budget REQUEST_BUDGET
cap the ADDITIONAL target requests mutation may issue
(default: 500). The baseline probes are the ones `veridor run`
issues anyway, and are governed by --max-requests below rather
than by this flag
tier 2 -- target mutation (RUNS COMMANDS YOU SUPPLY):
--tier {1,2} mutation tier (default: 1)
--hooks HOOKS JSON file of target mutation hooks. EVERY HOOK'S apply_command
AND restore_command IS EXECUTED ON THIS MACHINE, as you, with
your environment. They are run as an argv list with no shell,
so nothing is expanded or interpreted -- but they are still
your commands and they still run. A hook without a
restore_command is refused before anything executes. Requires
--tier 2
--i-understand-tier-2-runs-my-commands
required for --tier 2. Tier 2 alters your target and then puts
it back; without this flag nothing runs
init¶
Guide: init.
usage: veridor init [-h] [--for {run,drift}] --openapi OPENAPI --output OUTPUT
[--base-url BASE_URL] [--force]
Scaffold a configuration from an OpenAPI specification, for one of two paths. --for
run (default) writes one starter check per in-scope operation: every expectation is a
placeholder, not an observation, and the first run is red by design. --for drift
writes no checks at all -- two identities and one specimen per templated route --
because `map` grades no expectation and drift compares this run's matrix with an
earlier one. Edit before you trust.
options:
-h, --help show this help message and exit
--for {run,drift} which path to scaffold: 'run' declares expectations and gates
on them (default); 'drift' declares nothing and gates on what
moved
--openapi OPENAPI OpenAPI 3 or Swagger 2 JSON spec
--output OUTPUT configuration path to create
--base-url BASE_URL override the target base URL
--force replace an existing --output file
coverage¶
Guide: coverage.
usage: veridor coverage [-h] --config CONFIG --openapi OPENAPI
[--coverage COVERAGE_PATH] [--badge BADGE]
[--base-path BASE_PATH] [--allow-no-matches]
Measure how much of the OpenAPI inventory your declaration asserts an authorization
boundary for. Issues no request.
options:
-h, --help show this help message and exit
--config CONFIG path to veridor JSON config
boundary coverage (offline, static):
--openapi OPENAPI OpenAPI 3 or Swagger 2 JSON specification
--coverage COVERAGE_PATH
write the machine-readable coverage report JSON to this path
--badge BADGE write shields.io endpoint JSON for the coverage badge to this
path
--base-path BASE_PATH
spec path prefix to apply (default: auto-detect and show the
scoring)
--allow-no-matches report coverage as 'not measured' instead of failing when no
check path matches any operation
capsule¶
Guide: capsule and verify.
usage: veridor capsule [-h] --config CONFIG --check CHECK [--from-run FROM_RUN]
[--image IMAGE] [--output OUTPUT]
Write a portable, runnable proof of ONE finding: the request, the identities, the
declared expectation and the marker, in a single file you can attach to a disclosure.
The reader verifies it with `veridor verify <file>` against their own instance.
Credentials are never included -- a capsule that would carry one is refused, because a
published bearer token cannot be recalled.
options:
-h, --help show this help message and exit
--config CONFIG path to veridor JSON config
--check CHECK id of the check to prove
--from-run FROM_RUN a run JSON whose finding for this check becomes the CLAIM the
capsule carries, so the reader sees what was originally
observed
--image IMAGE container image the reporter used, ideally digest-pinned;
recorded verbatim
--output OUTPUT write here (default: stdout)
report¶
Guide: report, serve and new-marker.
usage: veridor report [-h] [--from-run RUN.json] [--from-drift DRIFT.json]
[--format {sarif,junit,gitlab-sast,codequality,markdown}]
[--output PATH] [--sarif SARIF]
Render a run JSON document as SARIF, JUnit XML, a GitLab SAST or Code Quality report,
or Markdown. Every format except SARIF reads the gate decision `veridor run` recorded
in the document, so a run that exited 3 because nothing was answered never renders as
a pass. SARIF carries findings only and has no gate: a run that exited 3 for an
unproven denial or an unclean teardown renders as zero results, so read the job's exit
code, not the SARIF, for the gate. A drift report (`map --baseline ... --drift PATH`)
renders to Markdown only: drift is never a finding.
options:
-h, --help show this help message and exit
--from-run, --input RUN.json
the run JSON `veridor run --json` wrote (--input is the older
spelling)
--from-drift DRIFT.json
a drift report written by `veridor map --baseline ... --drift`
--format {sarif,junit,gitlab-sast,codequality,markdown}
the report to write
--output PATH where to write it; '-' or omitted writes to stdout
--sarif SARIF older spelling of --format sarif --output PATH
serve¶
Guide: report, serve and new-marker.
usage: veridor serve [-h] [--output-dir OUTPUT_DIR] [--host HOST]
[--i-understand-this-is-unauthenticated] [--port PORT]
Open the local, read-only run history UI.
options:
-h, --help show this help message and exit
--output-dir OUTPUT_DIR
run history directory (default: .veridor)
--host HOST address to bind (default: 127.0.0.1). Anything other than
loopback publishes every run document -- targets, finding
messages, config paths -- to whoever can reach it, with no
authentication of any kind, and is refused without
--i-understand-this-is-unauthenticated
--i-understand-this-is-unauthenticated
required to bind an address other than loopback. The viewer
has no login and never has: it is a local reader for artifacts
you already have on disk
--port PORT
new-marker¶
Guide: report, serve and new-marker.