example-fork-detection
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect initialization bugs in upgradeable contracts — missing `_disableInitializers()`, re-init attacks, parent-init not chained, constructor-vs-initializer confusion, public `initialize`. Activate on OZ Upgradeable, UUPS, Transparent proxy, Initializable, or any contract with
$ npx -y skills add omermaksutii/RugProof --skill initialization --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/initializationContext preview
The summary Claude sees to decide when to auto-load this skill.
Detect initialization bugs in upgradeable contracts — missing `_disableInitializers()`, re-init attacks, parent-init not chained, constructor-vs-initializer confusion, public `initialize`. Activate on OZ Upgradeable, UUPS, Transparent proxy, Initializable, or any contract with
name: initialization description: Detect initialization bugs in upgradeable contracts — missing `_disableInitializers()`, re-init attacks, parent-init not chained, constructor-vs-initializer confusion, public `initialize`. Activate on OZ Upgradeable, UUPS, Transparent proxy, Initializable, or any contract with `initialize` / `__init`.
contract Vault is Initializable, OwnableUpgradeable {
function initialize() external initializer {
__Ownable_init(msg.sender);
}
// ← no constructor disabling initializers
}Anyone can call `initialize` on the *implementation* address directly. Fix:
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() { _disableInitializers(); }Proxy deployment script forgets to call `initialize` atomically → attacker calls it first → owns the contract. Fix: deploy + initialize in same tx via factory or initializer-on-deploy proxy.
function reinitialize() external reinitializer(2) { // ← no auth
__Pausable_init();
}Add `onlyOwner` or `onlyProxyAdmin`.
function initialize() external initializer {
// __Pausable_init() missing — pausable state defaults to wrong values
}Verify every parent's `__X_init()` is chained.
constructor() OwnableUpgradeable() { owner = msg.sender; } // ← runs on impl, not proxyUpgradeable contracts must do setup in `initialize`, not constructor.
Devs sometimes write constructor logic expecting it to run on the proxy. It doesn't — constructor runs at deployment of the *implementation*, and the proxy never re-runs it.
EIP-1167 clones share implementation; each clone's `_initialized` slot starts at 0. If `initialize` has no auth, anyone can initialize a fresh clone before the deployer.
OZ v4: `_initialized` is uint8; v5: `_initialized` is uint64. Custom upgradeable contracts that don't use OZ may have bypassable guards.
| Pattern | Severity | |---|---| | Implementation init not disabled (anyone owns impl) | **Critical** | | Deployed proxy with public init un-called | **Critical** | | Re-initializer with no auth | **High** | | Clones with no atomic init | **High** | | Parent `__init` missing → broken inheritance | **High** | | Constructor used instead of initializer (upgradeable) | **High** | | Initializer parameter validation missing (e.g. zero-address admin) | **Medium** | | Initial state value seems wrong (defaults to 0) | **Medium** |
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
Repo: omermaksutii/RugProof
TEMPLATE — replace with the description of your rule. Should activate on the specific code patterns your fork has. Activate on `<your trigger keywords or…
Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom storage slots, returns bools via…
Detect front-runnable ownership initialization in Solady Ownable / OwnableRoles. Solady's `_initializeOwner` is a guarded one-time setter (it reverts with…
Detect Solady SafeTransferLib calls that assume the token has code. SafeTransferLib.safeTransfer/safeTransferFrom/safeApprove deliberately skip the EXTCODESIZE…
Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates (BeforeSwapDelta, afterSwap hookDelta,…
Detect Uniswap V4 hooks whose address-encoded permission flags don't match the callbacks the hook actually implements. In V4 the hook's permissions live in the…