Skip to content
Back to skills

Initialization

ASecurity

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

  • 36 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
blockchain

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add iktok90-design/ai-smart-contract-auditor --skill initialization --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Initialization?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Initialization
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/iktok90-design-initialization/badge)](https://www.skillsdirectory.com/skills/iktok90-design-initialization)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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`.
---

# Initialization detection

## When this applies

- OZ `Initializable` / `*Upgradeable` contracts
- Custom upgradeable contracts with `initialize()` functions
- Beacon proxies, UUPS, Transparent — all flavors
- Factory-deployed clones (EIP-1167 minimal proxies)
- Any deploy script that uses `delegatecall` for setup

## Detection patterns

### Implementation contract not init-locked (CRITICAL)
```solidity
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:
```solidity
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() { _disableInitializers(); }
```

### Public initializer on deployed proxy (CRITICAL)
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.

### Re-init via `reinitializer(N)` accessible to anyone (HIGH)
```solidity
function reinitialize() external reinitializer(2) {   // ← no auth
    __Pausable_init();
}
```
Add `onlyOwner` or `onlyProxyAdmin`.

### Parent init not called (HIGH)
```solidity
function initialize() external initializer {
    // __Pausable_init() missing — pausable state defaults to wrong values
}
```
Verify every parent's `__X_init()` is chained.

### Logic in constructor instead of initializer (HIGH for upgradeable)
```solidity
constructor() OwnableUpgradeable() { owner = msg.sender; }   // ← runs on impl, not proxy
```
Upgradeable contracts must do setup in `initialize`, not constructor.

### Constructor runs on proxy (incorrect mental model) (MEDIUM)
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.

### Clones (minimal proxies) without per-clone init guard (HIGH)
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.

### Re-init via storage-slot manipulation (HIGH)
OZ v4: `_initialized` is uint8; v5: `_initialized` is uint64. Custom upgradeable contracts that don't use OZ may have bypassable guards.

## Severity rubric

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

## Remediation patterns

- Implementation constructor: `_disableInitializers()`.
- Deploy proxy + call initializer atomically (single factory tx).
- Mark `initialize` as `initializer`, `reinitialize` as `reinitializer(N)` with auth.
- Chain every parent's `__X_init()` in order matching inheritance.
- For clones: use a factory that deploys-and-initializes in one call.
- Test: from a fresh deploy, attempt `initialize()` twice and from a non-admin EOA — both should revert.

## False-positive notes

- Non-upgradeable contracts that just happen to use `initialize` as a name — not the same pattern.
- `Initializable` used purely as a one-shot setup guard with no proxy involved — lower severity.

## Related

- [[storage-layout]]
- [[access-control]]
- [[delegatecall-risks]]

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…