example-fork-detection
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on…
Detect ZK proof-verifier contract bugs — missing public-input binding (unconstrained input → forgery), proof malleability, BN254 field-element range checks (input >= field modulus), unchecked pairing/ecAdd/ecMul precompile returns, nullifier reuse / double-spend,
Claude Code
$ npx -y skills add omermaksutii/RugProof --skill zk-verifier-bugs --agent claude-code
Codex
$ npx skills add omermaksutii/RugProof --skill zk-verifier-bugs -a codex -y
Cursor
$ npx skills add omermaksutii/RugProof --skill zk-verifier-bugs -a cursor -y
Gemini CLI
$ npx skills add omermaksutii/RugProof --skill zk-verifier-bugs -a gemini-cli -y
GitHub Copilot
$ npx skills add omermaksutii/RugProof --skill zk-verifier-bugs -a github-copilot -y
OpenCode
$ npx skills add omermaksutii/RugProof --skill zk-verifier-bugs -a opencode -y
Windsurf
$ npx skills add omermaksutii/RugProof --skill zk-verifier-bugs -a windsurf -y
name: zk-verifier-bugs description: Detect ZK proof-verifier contract bugs — missing public-input binding (unconstrained input → forgery), proof malleability, BN254 field-element range checks (input >= field modulus), unchecked pairing/ecAdd/ecMul precompile returns, nullifier reuse / double-spend, verification-key upgradeability, and trusted-setup assumptions. Activate whenever a contract verifies a SNARK/STARK proof, calls the bn256/altbn128 precompiles, or consumes nullifiers.
Trigger on any of:
function withdraw(uint256[8] proof, uint256 root, uint256 nullifier, address recipient) external {
require(verifier.verifyProof(proof, [root, nullifier])); // ← recipient NOT in inputs
payable(recipient).transfer(amount);
}**Signal:** a value the contract acts on (`recipient`, `amount`, `chainId`) is not among the proof's public inputs. The proof says nothing about it, so an attacker reuses a valid proof with a different recipient — front-runnable theft. Every value the proof is supposed to authorize must be a bound public input.
// public input passed straight to the verifier without bounds require(input < SNARK_SCALAR_FIELD); // ← if MISSING:
**Signal:** public inputs (and proof point coordinates) must be `< r` (the BN254 scalar/base field modulus, `21888242871839275222246405745257275088548364400416034343698204186575808495617`). An out-of-range input wraps mod r, letting a different field element pass as a valid input — input forgery. Verifiers must `require(x < FIELD_MODULUS)` for every input.
assembly { let ok := staticcall(gas(), 0x08, ...) } // 'ok' / returndata ignored**Signal:** the pairing precompile (`0x08`) returns success flag + a 32-byte result that must equal 1; `ecAdd`/`ecMul` return whether the input points were valid. Ignoring the success bool or the pairing result accepts invalid proofs (precompile reverts on malformed points only sometimes). Always check `success` AND the returned value.
require(verifier.verifyProof(...)); // ← no check that nullifier was unused; no marking after payout(recipient);
**Signal:** nullifier not checked against `spent[nullifier]` before payout, or not marked spent after (or marked after an external call → reentrant double-spend). Same note must not redeem twice.
Groth16 proofs are malleable — a valid `(A,B,C)` can be transformed to another valid proof for the same inputs. If proof bytes are used as a dedup/replay key, the twin bypasses it. Dedup on public inputs / nullifiers, never on proof bytes.
An upgradeable or owner-settable verification key lets an admin swap in a vk that validates forged proofs — a centralization backdoor over the entire system's soundness.
| Pattern | Severity | Notes | |---|---|---| | Action value not a bound public input | **Critical** | Proof reuse → theft | | Nullifier not checked / marked late | **Critical** | Double-spend | | Missing BN254 field range check | **High** | Input forgery via wraparound | | Unchecked pairing/ecMul return | **High** | Invalid proofs accepted | | Mutable vk without timelock/governance | **High** | Soundness backdoor | | Proof bytes used as replay key (malleable) | **Medium** | Twin-proof bypass |
1. **Bind every authorized value** as a public input committed in-circuit (`recipient`, `amount`, `chainId`, `contractAddress`); verify the input vector matches what the contract acts on. 2. **Range-check all public inputs** `< FIELD_MODULUS` (BN254 `r`) before verification — use the snarkjs/circomlib generated verifier's checks; don't strip them. 3. **Check precompile results** — `require(success)` and `require(out == 1)` for pairing; validate `ecAdd`/`ecMul` success. 4. **Nullifier set:** `require(!spent[n]); spent[n] = true;` *before* any external call (CEI). 5. **Dedup on inputs/nullifiers, not proof bytes**, to neutralize Groth16 malleability. 6. **Govern the vk** behind a timelock/multisig; document the trusted-setup ceremony and its toxic-waste assumptions.
Rugproof your code before someone else does. 🌐 Live site: omermaksutii.github.io/RugProof 📦 Latest: v1.0.0 — 45 commands · 23 agents · 45 skills · 13 MCP servers · tested, offline-first, with rule packs, a benchmark, non-EVM coverage, and post-deploy
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on…
Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom…
Detect front-runnable ownership initialization in Solady Ownable / OwnableRoles. Solady's `_initializeOwner` is a guarded…
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove…
Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates…
Detect Uniswap V4 hooks whose address-encoded permission flags don't match the callbacks the hook actually implements. In V4 the…
Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…
Extract and decrypt Windows DPAPI-protected secrets (Credential Manager, browser logins/cookies, Wi-Fi credentials, KeePass keys)…
Clean and finalize authorized natural-language text intended for readers by auditing suspicious invisible Unicode and rewriting…