Rule

received-coin-not-validated

Received shielded coin lacks a recorded application constraint on color or value.

Release v0.1.1View Markdown

Specimen 69 of 96 · Other active opt-ins / Token, coin and value transfers

Severity
low
Confidence
low
Maturity
experimental
Selection
Opt-in
Policy intent
not required
Release
v0.1.1
Engine facts used
successful-predicates all-predicates runtime-operations completed-entries

Selection and scope#

Not selected by default. Select it with --detectors or the detectors list in nightshade.json. Either one replaces profile selection, so list every rule you want.

Shell
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors received-coin-not-validated
./.nightshade/node_modules/.bin/nightshade detector show received-coin-not-validated

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
1/11/11/10/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/received-coin-not-validated.md at v0.1.1.

Availability: opt-in with --detectors received-coin-not-validated; no policy required. Severity low, confidence low. Explicit selection replaces the default detector set.

Owned example: the compiler-qualified fixture adapts an SDK deposit with an owned fixed-payment requirement, then substitutes a tautological amount check. The correction checks the expected color and value 10 before receipt. Review a fixed-price payment flow; recommendation: constrain the exact received coin to the intended trusted terms. The original unrestricted SDK deposit is not labeled vulnerable.

For example, a payment circuit that calls receiveShielded(disclose(coin)) without checking its price can accept a payment outside the intended terms. Check coin.color == expectedColor and coin.value >= price against trusted ledger terms or constants. The rule also recognizes exact value equality and positive constant lower bounds. It does not prove those terms implement the intended business policy.

Evidence comes from completed entry histories, canonical standard-library call identity, exact received operands and enforced predicates. Equality aliases, helpers, negated comparisons, branch alternatives and checks after receipt are supported. Later checks constrain atomic success. Checks on another coin, ignored comparisons, self-equality, Uint >= 0, arbitrary caller-supplied terms and self-written ledger values do not establish validation. Exhausted query or analysis budgets remain incomplete.

Known limits: custom receipt protocols, unshielded receipts, arithmetic-derived pricing and arbitrary policy inference are outside this rule. A generic wallet, forwarder, donation or deposit handler may intentionally accept any coin. Select explicitly with --detectors received-coin-not-validated.

The compiler-valid positive/corrected/lookalike cases live in the existing corpus.

Source examples#

These short regression examples are included here for installed-package readers. Rule-specific policy requirements and limits above still apply.

Example 1#

Fixture (source: tests/corpus/vulnerable/received-coin-not-validated.compact).

Compact
// SDK shielded-map fixture with an owned fixed-payment policy; see known-bugs.json.
import CompactStandardLibrary;

export ledger counter: Counter;
export ledger reward: QualifiedShieldedCoinInfo;

witness dummy(): [];

export circuit deposit(coin: ShieldedCoinInfo): [] {
  assert(coin.color == default<Bytes<32>>, "expected color");
  assert(coin.value == coin.value, "expected amount");
  receiveShielded(disclose(coin));
  reward.writeCoin(disclose(coin), right<ZswapCoinPublicKey, ContractAddress>(kernel.self()));
  counter.increment(1);
  dummy();
}

Example 2#

Correction (source: tests/corpus/safe/received-coin-not-validated.compact).

Compact
// SDK shielded-map fixture with an owned fixed-payment policy; see known-bugs.json.
import CompactStandardLibrary;

export ledger counter: Counter;
export ledger reward: QualifiedShieldedCoinInfo;

witness dummy(): [];

export circuit deposit(coin: ShieldedCoinInfo): [] {
  assert(coin.color == default<Bytes<32>>, "expected color");
  assert(coin.value == (10 as Uint<128>), "expected amount");
  receiveShielded(disclose(coin));
  reward.writeCoin(disclose(coin), right<ZswapCoinPublicKey, ContractAddress>(kernel.self()));
  counter.increment(1);
  dummy();
}

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 received-coin-not-validated -- 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#