一键导入
solidity-security-review
Smart contract security review methodology. Use when auditing Solidity code, analyzing vulnerabilities, or verifying security findings.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Smart contract security review methodology. Use when auditing Solidity code, analyzing vulnerabilities, or verifying security findings.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Comprehensive smart contract development advisor based on Trail of Bits' best practices. Analyzes codebase to generate documentation/specifications, review architecture, check upgradeability patterns, assess implementation quality, identify pitfalls, review dependencies, and evaluate testing. Provides actionable recommendations.
ALWAYS load before writing or modifying Foundry test files (.t.sol). Covers fuzz testing, gas benchmarks, naming conventions, and test patterns.
Deep design principles for Towns Protocol smart contracts. Use when creating new facets, major refactoring, reviewing architecture decisions, or designing new abstractions.
| name | solidity-security-review |
| description | Smart contract security review methodology. Use when auditing Solidity code, analyzing vulnerabilities, or verifying security findings. |
Run these skills in order, then verify findings before reporting:
Step 1: entry-point-analyzer Map all public/external entry points and their parameters.
Step 2: audit-context-building Deep code analysis. Apply 5 Whys to every conditional branch involving user-controlled parameters.
Step 3: guidelines-advisor Best practices and common pitfalls check.
Step 4: Verification For each raw finding, verify by tracing exact execution path. Filter findings with confidence < 8/10. Never report without verification—false positives waste developer time.
After completing all steps, output ONLY a findings table.
Include ONLY findings that could lead to: loss of funds, unauthorized access, reward manipulation, or denial of service.
Exclude: documentation gaps, gas optimizations, code style, theoretical issues with unsupported token types.
| ID | Finding | Severity | File:Line | Evidence |
|----|---------|----------|-----------|----------|
| 1 | [Description] | CRITICAL/HIGH/MEDIUM/LOW | `File.sol:XX` | [Proof or attack path] |
Every user-provided input must be validated. Apply 5 Whys to understand why each branch exists and whether an attacker can exploit it:
Observation: Token transfer is skipped when owner == address(this)
Why #1: Why is the transfer skipped?
→ The code assumes tokens are already in the contract
Why #2: Why would tokens already be there?
→ Internal flow deposits tokens before calling
Why #3: Why is there no check that internal flow actually called?
→ The code trusts only internal flows use owner == address(this)
Why #4: Why would only internal flows use that value?
→ No reason - external callers can pass any owner value
Why #5: Why is this dangerous?
→ Attacker creates unbacked stake, drains rewards
Special values to test for each user-controlled parameter:
address(0) - zero addressaddress(this) - the contract itselfmsg.sender - caller addresstype(uintX).max - maximum valuesValidation questions:
Example vulnerability:
// VULNERABLE - allows arbitrary owner without validation:
function stakeOnBehalf(..., address owner, ...) external {
// Missing: if (owner == address(this)) revert InvalidOwner();
depositId = _stake(amount, delegatee, beneficiary, owner, false);
}
// In _stake():
if (owner != address(this)) {
ds.staking.stakeToken.safeTransferFrom(msg.sender, proxy, amount);
} else {
// NO TRANSFER - assumes internal use only
// But stakeOnBehalf allows external callers to reach this path!
}
Always trace: (1) where tokens come FROM, (2) where tokens go TO, (3) who gets ownership/benefits
// Tokens: FROM msg.sender, TO proxy
ds.staking.stakeToken.safeTransferFrom(msg.sender, proxy, amount);
// Questions to answer:
// - Who pays the tokens? (msg.sender vs arbitrary parameter)
// - Who receives the tokens/deposit?
// - Who gets the benefits (rewards, ownership)?
// - What happens if transfer is skipped?
Both regular function calls and low-level calls create new call frames. The difference is how reverts are handled:
// Regular function call - revert propagates automatically:
function foo() external {
bar(); // If bar() reverts, foo() reverts too, all state rolled back
}
// Low-level call - revert is captured, caller decides:
function foo() external {
storage.value = 1; // This persists even if inner call reverts!
(bool success, ) = address(this).call(
abi.encodeCall(IContract.method, (arg))
);
if (!success) {
// Inner call reverted - but we continue execution
// State changes INSIDE the .call() are rolled back
// State changes BEFORE the .call() persist!
}
}
Key points:
address.call(): captures revert, returns (false, errorData), caller decides to bubble up or continuetry-catch: syntactic sugar that also captures reverts, state before try persists on catch.call() or try persist even if inner call reverts| Claim | Reality Check |
|---|---|
| "State persists after revert" | Only if low-level call captures the revert |
| "Overflow possible" | Check if token supply constrains values |
| "No validation on read" | Check if write-side is protected |
| "Silent failure is bug" | May be intentional for batch protection |
| "Anyone can call" | Check if caller pays tokens (gift vs theft) |
Recognize when "vulnerabilities" are intentional:
| Severity | Criteria |
|---|---|
| CRITICAL | Direct fund loss, unauthorized access to funds |
| HIGH | Significant economic impact, DoS on critical functions |
| MEDIUM | Limited impact, requires specific conditions |
| LOW | Best practice violations, design concerns |