Rule

current-root-policy-mismatch

An explicitly selected membership guard lacks current-root evidence at enforcement time.

Release v0.1.1View Markdown

Specimen 34 of 96 · Policy-dependent rules

Severity
medium
Confidence
medium
Maturity
experimental
Selection
Opt-in + policy
Policy intent
required
Release
v0.1.1
Engine facts used
declared-current-root-policy all-successful-predicates native-merkle-queries ordered-writes

Selection and scope#

Not selected by default. Select it with --detectors or the detectors list in nightshade.json, and declare the matching application intent in policies. Selecting it alone does not supply that intent.

Shell
./.nightshade/node_modules/.bin/nightshade ./contracts --detectors current-root-policy-mismatch
./.nightshade/node_modules/.bin/nightshade detector show current-root-policy-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
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/current-root-policy-mismatch.md at v0.1.1.

Availability: opt-in policy. Severity/confidence: medium/high. Select with --detectors current-root-policy-mismatch and declare the application requirement:

JSON
{"version":1,"policies":{"currentRootMembership":[
  {"circuit":"record","ledger":"permitted"}
]}}

The policy applies to positive native checkRoot predicates enforced on modeled successful histories of the selected exported circuit. It requires the checked root to be current when that predicate is enforced. The ledger selector is an unambiguous canonical ledger ID or name; all native Merkle receivers below that root are selected, including nested Maps. Helpers retain their actual invocation operands and sites. This policy does not infer freshness intent. Equivalent repeated queries list every candidate invocation; the report never silently identifies an unused call as the saved Boolean's exact occurrence.

Compact
import CompactStandardLibrary;
ledger permitted: HistoricMerkleTree<2,Field>;
export ledger accepted: MerkleTreeDigest;
export circuit record(root: MerkleTreeDigest): [] {
  assert(permitted.checkRoot(disclose(root)), "membership");
  accepted = disclose(root);
}

Historic trees accept previous roots. An application that promises latest-snapshot acknowledgment should use MerkleTree, or establish a recognized current-only invariant with resetHistory/resetToDefault and perform a fresh query. Query-time proof alone is insufficient: saving a positive check, inserting into its tree, then asserting the saved Boolean does not certify the tree at that guard. Ordered real writes are checked between query and enforcement. Exact sibling writes preserve certification; possible aliases and ancestor replacements remove it. No comparison between Merkle epochs and ledger revisions supplies a proof. Native current trees remain exclusive for dynamic receivers too.

The diagnostic reports a missing guard-time certificate under the explicit policy. It does not establish that historical roots are actually stored, that an overlapping write changes the root, that a stale root is accepted at runtime, or that a modeled history is feasible. Intentional historical membership is a known policy-dependent review shape: leave this policy unset. Negative checks, mere optional queries, and tautologies do not become required positive membership. A true branch does enforce membership on its own successful history.

Later-effect freshness is outside this contract: a fresh guard followed by an insertion is quiet even if an effect happens afterward. Missing membership guards are also outside this rule; see merkle-membership-not-enforced. Independently bound current roots and unmodeled application invariants can make a reported certificate gap intentional. Nonliteral Boolean equality chains, lost predicate structure, unavailable/checkpoint-projected positions and exhausted budgets give explicit rule/entry-scoped incomplete diagnostics, never clean policy results. Boolean-to-numeric encodings in enforced predicates are also explicitly unavailable; conversions cannot erase a native check into apparent clean absence. Opaque native digest/commitment relations involving observed check Booleans retain a loss marker and are unavailable too; hashing is not decoded as Boolean equality or positive membership evidence. No successful invocation supplies no complete guard evidence.

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 current-root-policy-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#