Rule

either-payload-mismatch

A native Either's known inactive arm reaches an output, state effect or successful effect gate.

Release v0.1.1View Markdown

Specimen 07 of 96 · Audit-default ordinary rules

Severity
info
Confidence
high
Maturity
experimental
Selection
Audit default
Policy intent
not required
Release
v0.1.1
Engine facts used
resolved-types successful-either-selections public-sinks state-writes predicates

Selection and scope#

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

Shell
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors either-payload-mismatch
./.nightshade/node_modules/.bin/nightshade detector show either-payload-mismatch

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. 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.

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
2/22/22/20/00

No deviations were recorded for this rule.

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

Rule documentation#

From docs/detectors/either-payload-mismatch.md at v0.1.1.

Availability: default-on; select only this rule with --detectors either-payload-mismatch; severity medium, confidence high. Explicit selection replaces the default set.

The rule reports a canonical native Either arm selection when that receiver's immutable is_left tag selects the other arm on a modeled successful history, and the selected payload reaches a public output, a state/transaction effect, an effect's actual control dependence, or a non-tautological assertion gating an effect. It uses typed receiver snapshots, not common witness provenance. Imported and nominal native aliases retain their identity; same-shaped user structs are quiet.

Example and scenario#

Compact
export circuit gatedIncrement(allowed: Either<Field, Field>, claim: Field): [] {
  assert(!allowed.is_left, "right admission");
  assert(allowed.left == claim, "admission"); // validates the inactive arm
  round.increment(1);
}

Validate allowed.right after requiring the right tag. Both payload fields are represented in Compact; projecting one does not itself constrain is_left. The finding is an established tag/selection mismatch with a modeled consequence, not a proof of an authorization bypass or deployed exploit.

Recommendation and limits#

Check the same receiver's tag and use its selected payload. An assertion after an effect can still constrain successful completion because effects are atomic. A tag check on replacement ledger storage cannot repair a retained old snapshot. Independent dynamic Map lookups do not share a definite tag merely because their regions may alias. Helpers, destructuring, aggregate payloads and aliases use the same observation transfer.

Bare unused reads, unknown tags, same-shaped application structs, tautological assertions, identical total/effect-free conditional arms, and branches that neither reject nor control an effect stay quiet. A direct untransformed arm copied into an explicit native Either construction establishes a new receiver choice; that copy alone is exempt. Subsequent selection uses the new receiver's tag. Computations over the original arm preserve their dependencies. General variant lifecycle/protocol validation, branch-rejection proofs for effects after joins, unconstrained witness-output dependence, and correlated may-history certificates are outside this rule.

Evidence and known false positives#

Branch-expanding entries first try a conservative-absence certificate (see docs/coverage.md): a complete conservative may-view in which every native Either payload selection reads the arm selected by its receiver tag, refined to that constant by the joined current constraints. Any unknown or inactive tag is a candidate and falls back to exact observation with the remaining allowance.

Observation shares the configured work/value allowances, retains at most 256 reads per history, 4,096 Boolean/control observations, conditional chains of at most 64 observations, and limits consequence traversal to 200,000 work units. either-evidence-budget, either-finding-budget, or missing exact evidence produce an explicit incomplete diagnostic. Incomplete roots supply no negative coverage claim. Instruction/reference and requested-cache/uncached tests cover these semantics; cached requests currently use the uncached transfer.

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 either-payload-mismatch -- 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#