Rule

spoofable-authorization

A raw witness equality with public data is required for a state-changing effect; review the intended authentication.

Release v0.1.1View Markdown

Specimen 23 of 96 · Audit-default ordinary rules

Severity
lowhighcritical
Confidence
varies
Maturity
experimental
Selection
Audit default
Policy intent
not required
Release
v0.1.1
Engine facts used
successful-predicates private-provenance state-writes runtime-operations completed-entries

Selection and scope#

Selected by the default audit profile, so an ordinary scan runs it.

Shell
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors spoofable-authorization
./.nightshade/node_modules/.bin/nightshade detector show spoofable-authorization

The first command runs only this rule; --detectors takes a comma-separated list. The second prints the rule's metadata from your installed version.

Severity is review priority; this rule assigns low, high, critical depending on the evidence. It does not express exploitability or impact. Confidence is detector metadata, not a probability. All built-in rules are experimental.

Compiler scope: the engine supports the pinned profiles 0.28.0, 0.29.0, 0.30.0, 0.31.1, 0.33.0-rc.2, 0.34.0, 0.35.0-rc.1 and 0.35.0, with ZKIR2 by default. Support for this rule can be narrower; the rule documentation below states any narrower scope.

Verified example#

Synthetic examplecompact-0.34.0-zkir2compiler-validated

Comparing a caller-supplied public identity with the stored owner does not prove possession of its secret. From the proof run of the released CLI; the examples page also has the lookalike and the files.

Compactvulnerable.compactline 6 flagged
import CompactStandardLibrary;
export ledger owner: ZswapCoinPublicKey;
export ledger stored: Field;

export circuit change(next: Field): [] {
  assert(ownPublicKey() == owner, "owner");
  stored = disclose(next);
}
output.txtexit 1
$ nightshade vulnerable.compact --config config.json --compiler /path/to/compactc

HIGH — Public-key equality gates a state-changing effect [spoofable-authorization]
vulnerable.compact:6:3
  6 |   assert(ownPublicKey() == owner, "owner");
    |   ^
change requires raw witness data from ownPublicKey to equal ledger owner on every modeled successful reach. The guard covers 1 effect site: ledger-write stored. Every successful history reaching these effects has only raw-witness/public-data checks; no additional modeled gate is present. Equality alone does not demonstrate possession of a secret; review the intended authentication and protocol role.
Source: vulnerable.compact:6:10
Constraint: vulnerable.compact:6:3
Call path: change
Remediation: If this check is intended as authentication, enforce a suitable secret-preimage or protocol-specific proof. Review key initialization, secrecy and replay assumptions. Document and triage intentional public-data validation.

Semantic profile: compact-0.34.0-zkir2.
Baseline analysis: complete; selected rules: complete.
Rule coverage: 1 selected rule(s); 0 with incomplete entries.
1 finding(s), 0 suppressed, 0 baseline; 1/1 entrypoint(s) completed; analysis complete. Compiler validation passed.

Corrected: Authorize with a private key preimage checked against the stored owner hash. Changed lines are marked.

Compactcorrected.compactrule quiet
import CompactStandardLibrary;
export ledger owner: Bytes<32>;
export ledger stored: Field;
witness privateKey(): Bytes<32>;

export circuit change(next: Field): [] {
  assert(persistentHash<Bytes<32>>(privateKey()) == owner, "secret preimage");
  stored = disclose(next);
}
corrected-output.txtexit 0
$ nightshade corrected.compact --config config.json --compiler /path/to/compactc

Semantic profile: compact-0.34.0-zkir2.
Baseline analysis: complete; selected rules: complete.
Rule coverage: 1 selected rule(s); 0 with incomplete entries.
0 finding(s), 0 suppressed, 0 baseline; 1/1 entrypoint(s) completed; analysis complete. Compiler validation passed.

The command line is shortened from the recorded run to what you would type; the output below it is verbatim, with absolute file paths shortened to the example folder. Long lines wrap. A quiet result shows that this rule stopped firing, not that the contract is secure.

Corpus checks#

Synthetic corpus checks for this rule in the 0.1.1 proof run. A check counts only with compiler acceptance and complete analysis.

Positive detectedCorrected quietLookalike quietOther negative quietIncomplete
28/2828/2828/280/00

Deviations recorded for this rule:

CaseKindExpected to observed linesIssue
known-bugs.json / authorization-raw-native-witnesspositive[7] to [6]exact expectation mismatch

These are behavior checks on synthetic fixtures, not a precision or recall estimate. See evaluation.

Rule documentation#

From docs/detectors/spoofable-authorization.md at v0.1.1.

This default detector reviews exported circuits where a prover-supplied witness is required to equal a constant or public ledger value before a state-changing effect. Such an equality proves that the supplied value matches public data; it does not prove possession of a secret. Public-data validation can be intentional and still produce a finding.

The detector follows required conditions across successful paths and groups affected effects by guard source and circuit. It preserves operand identity, branch polarity, call site, storage revision, and the guard's position relative to effects. A comparison that is computed but not enforced does not qualify. Identity-preserving copies and casts remain raw witness values; hashing a private preimage is a different relationship.

Severity and confidence describe the structural evidence, not a conclusion about application intent. A sole modeled raw-witness or constant gate on a privileged effect can receive critical severity. Other privileged witness checks and public ledger key equalities receive high priority. Setup and nonprivileged validation remain low priority. A canonical receipt-coordination effect alone is classified as low-priority receipt validation; other affected effects in a mixed group are assessed separately. Circuit, ledger, and witness names are labeled heuristics, not proof of privilege or authentication intent. Signature-like witness names can raise review priority but cannot establish signature verification or support critical severity.

For example, checking identity() == owner before changing state establishes only equality with the public owner value. If the protocol requires secret-key authorization, it should constrain a private preimage to trusted, preexisting owner data using the protocol's reviewed authentication construction. Review initialization, key handling, replay protection, and disclosure independently.

Critical grading is deliberately conservative: every enforced condition must be a definite constant or a fully classified raw-witness comparison with public data. Additional private inputs, ledger conditions, ranges, collection predicates, checked conversions, arithmetic, lookups, or derived transaction operations prevent sole-gate certification. This test is sufficient evidence for a structural grade, not a general authentication proof.

Private-preimage checks suppress the raw-equality finding only when they are required on every successful reach and bind the exact private input to trusted, preexisting ledger data. Self-written hashes, unrelated inputs, possible may-alias values, or a check present on only some paths do not establish that relationship. Public constraints on the preimage, entropy, later disclosure, and arbitrary signature or protocol authentication are outside the detector's proof.

Unsupported rollback behavior and exhausted traversal or value budgets produce incomplete diagnostics; incomplete analysis does not establish absence of a finding. Aggregate conditions must be fully classified. Receipt-only findings describe receipt coordination and do not establish whole-transaction validity.

Suppress a match#

If a match is intentional, suppress it on the line before with the exact rule ID and a reason of at least eight characters:

Compact
// nightshade-disable-next-line spoofable-authorization -- Reason this match is intentional

Diagnostics cannot be suppressed, and an unmatched directive for a selected rule makes analysis incomplete. See configuration for fingerprint suppressions and baselines.

References#