Install in Claude Code
Copygit clone --depth 1 https://github.com/PurpleAILAB/Vigilo /tmp/access-control && cp -r /tmp/access-control/packages/opencode/skills/access-control ~/.claude/skills/access-controlThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# Access Control Vulnerability Analysis
**OWASP SC01:2025** - Access Control is **#1 vulnerability class** in OWASP Smart Contract Top 10 (2025), with **$1.2B+ in cumulative losses** through 2025, including the Bybit hack ($1.4B) and multiple privilege escalation exploits.
---
## Why Access Control Bugs Happen (Root Causes)
Understanding root causes helps detect vulnerabilities more effectively.
### Root Cause 1: Intent-Implementation Gap
Developer thinks "only admin can call this" but forgets to add modifier.
```solidity
// Developer INTENDED: only admin
// ACTUAL: anyone can call
function setPrice(uint256 newPrice) external {
price = newPrice; // @audit No modifier!
}
```
**Detection**: Find external/public functions without modifiers, then verify intent.
### Root Cause 2: Visibility ≠ Permission
`external`/`public` means "anyone can call" - not a permission system.
```solidity
// Visibility is NOT access control
function withdraw() public { // @audit public ≠ "user's own funds"
// Without checks, ANYONE withdraws ANYONE's funds
}
```
**Detection**: Every state-changing external/public function needs explicit permission checks.
### Root Cause 3: Undefined Trust Boundary
Who is "admin"? What can they do? Often undocumented.
```solidity
// VULNERABLE: Admin powers undefined
function emergencyWithdraw() external onlyAdmin {
// Can admin steal all user funds?
// Is this documented? Intended?
}
```
**Detection**: Map all admin powers. Flag undocumented capabilities as centralization risks.
### Root Cause 4: Broken Permission Hierarchy
Admin can create admins → single key compromise = total system takeover.
```solidity
// VULNERABLE: Flat admin hierarchy
function addAdmin(address newAdmin) external onlyAdmin {
admins[newAdmin] = true; // @audit Compromised admin adds attacker
}
```
**Detection**: Trace role grant paths. Flag self-granting or circular hierarchies.
---
## The Permission Matrix (Core Artifact)
Build this for every contract:
| Contract | Function | Sensitivity | Required Role | Actual Check | Gap? |
|----------|----------|-------------|---------------|--------------|------|
| Vault | withdraw | CRITICAL | User (own funds) | None | **YES** |
| Vault | setFee | HIGH | Admin | onlyOwner | No |
| Vault | pause | HIGH | Guardian | onlyAdmin | **WRONG ROLE** |
| Token | mint | CRITICAL | Minter | None | **YES** |
### Sensitivity Classification
| Level | Examples | Impact if Missing |
|-------|----------|-------------------|
| **CRITICAL** | withdraw, transfer, mint, upgrade | Direct fund loss |
| **HIGH** | pause, setFee, setOracle | Protocol malfunction |
| **MEDIUM** | setParameter, whitelist | Degraded operation |
| **LOW** | view, pure functions | Information leak |
---
## Detection Patterns
### Pattern 1: Missing Access Control
**Root Cause**: Intent-Implementation Gap
```solidity
// VULNERABLE: Anyone can call
function setPrice(uint256 newPrice) external {
price = newPrice; // @audit Anyone can manipulate price!
}
function withdrawAll() external {
payable(msg.sender).transfer(address(this).balance); // @audit No modifier!
}
```
**Search Queries**:
```
Grep("function.*external(?!.*view)(?!.*pure)", glob="**/*.sol")
Grep("function.*public(?!.*view)(?!.*pure)", glob="**/*.sol")
```
**Verification Questions**:
- Does this function modify state?
- Is there a modifier or require statement?
- What is the intended caller?
### Pattern 2: Privilege Escalation
**Root Cause**: Broken Permission Hierarchy
```solidity
// VULNERABLE: Admin can add arbitrary admins
function addAdmin(address newAdmin) external {
require(admins[msg.sender], "Not admin");
admins[newAdmin] = true; // @audit Compromised admin adds attacker
}
// VULNERABLE: Self-grant role
function grantRole(bytes32 role, address account) public {
_grantRole(role, account); // @audit No permission check!
}
```
**Search Queries**:
```
Grep("grantRole|addAdmin|setAdmin", glob="**/*.sol")
Grep("_setupRole|_grantRole", glob="**/*.sol")
```
**Verification Questions**:
- Can a role grant itself or other roles?
- Is there a role hierarchy (admin > moderator > user)?
- What happens if the top role is compromised?
### Pattern 3: tx.origin Phishing
**Root Cause**: Confusing transaction origin with message sender
```solidity
// VULNERABLE: Phishing via malicious contract
function withdraw() external {
require(tx.origin == owner); // @audit Phishing target!
// Attacker tricks owner to call malicious contract
// Malicious contract calls this function
// tx.origin is still owner!
}
```
**Search Queries**:
```
Grep("tx\\.origin", glob="**/*.sol")
```
**Verification Questions**:
- Is tx.origin used for authorization?
- Can an attacker trick the owner into calling a malicious contract?
### Pattern 4: Incorrect Permission Logic (OR vs AND)
**Root Cause**: Logic error in permission checks
```solidity
// VULNERABLE: Should be AND, not OR
function sensitiveAction() external {
require(hasRole(ADMIN) || hasRole(GUARDIAN)); // @audit OR allows either
// Should require BOTH roles for high-sensitivity actions
}
// Also check for inverted logic
function withdraw() external {
require(!blacklisted[msg.sender]); // What if blacklist is empty?
}
```
**Search Queries**:
```
Grep("require.*\\|\\|", glob="**/*.sol")
Grep("require.*&&", glob="**/*.sol")
```
**Verification Questions**:
- Should this be AND or OR?
- What is the minimum permission needed?
- Can the condition be bypassed?
### Pattern 5: Missing Two-Step Transfer
**Root Cause**: No confirmation for critical ownership changes
```solidity
// VULNERABLE: Single transaction transfer
function transferOwnership(address newOwner) external onlyOwner {
owner = newOwner; // @audit Typo in address = permanent loss
}
// SECURE: Two-step pattern
function transferOwnership(address newOwner) external onlyOwner {
pendingOwner = newOwner;
}
function acceptOwnership() external {