building-secure-contracts
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBuilding Secure Contracts Skill
构建安全合约Skill
<!-- Agent: skill-updater | Task: #6 | Session: 2026-03-01 -->
<identity>
Smart contract and secure API contract security analysis skill. Implements Trail of Bits and OpenSCV-aligned methodology for detecting reentrancy attacks, access control failures, integer overflows, and invariant violations in Solidity (EVM) and Rust (Solana) contracts. Addresses the $1.8B+ DeFi exploit landscape (Q3 2025) through systematic vulnerability analysis.
</identity>
<capabilities>
- Checks-Effects-Interactions (CEI) pattern enforcement and verification
- Reentrancy attack surface mapping (cross-function, cross-contract, read-only)
- Access control audit: missing modifiers, privilege escalation, role confusion
- Integer arithmetic analysis: overflow, underflow, precision loss, rounding direction
- Contract invariant identification and formal verification setup
- Storage collision and proxy upgrade security analysis
- Oracle manipulation and price feed dependency analysis
- Flash loan attack surface enumeration
- EVM vs Solana security model comparison and platform-specific risk identification
- OpenSCV vulnerability taxonomy classification for all findings
</capabilities>
<!-- Agent: skill-updater | Task: #6 | Session: 2026-03-01 -->
<identity>
智能合约与安全API合约安全分析技能。采用与Trail of Bits和OpenSCV对齐的方法论,用于检测Solidity(EVM)和Rust(Solana)合约中的重入攻击、访问控制故障、整数溢出和不变量违反问题。通过系统化漏洞分析,应对2025年第三季度累计超18亿美元的DeFi漏洞攻击风险。
</identity>
<capabilities>
- Checks-Effects-Interactions(CEI)模式强制校验与验证
- 重入攻击面映射(跨函数、跨合约、只读场景)
- 访问控制审计:缺失修饰符、权限提升、角色混淆
- 整数运算分析:溢出、下溢、精度损失、舍入方向问题
- 合约不变量识别与形式化验证配置
- 存储冲突与代理升级安全分析
- 预言机操纵与价格喂价依赖分析
- 闪电贷攻击面枚举
- EVM与Solana安全模型对比与平台特有风险识别
- 所有漏洞发现遵循OpenSCV漏洞分类标准
</capabilities>
Overview
概述
This skill applies systematic security analysis to smart contracts and secure API contracts. The core principle: every state mutation must be proven safe through invariant verification before an external call executes. It covers both EVM (Solidity) and Solana (Rust) ecosystems with platform-specific vulnerability patterns.
Vulnerability taxonomy: OpenSCV (94 classified security issues)
Critical patterns: CEI, reentrancy guards, access modifiers, SafeMath equivalents
Risk landscape: $1.8B+ in DeFi exploits Q3 2025 (access control: $953M, reentrancy: $420M)
本技能对智能合约和安全API合约执行系统化安全分析,核心原则为:每次状态变更必须在外部调用执行前通过不变量验证证明其安全性。覆盖EVM(Solidity)和Solana(Rust)生态,支持平台特有漏洞模式检测。
漏洞分类体系:OpenSCV(94个已分类安全问题)
关键校验模式:CEI、重入防护、访问修饰符、SafeMath等效实现
风险现状:2025年第三季度DeFi攻击累计损失超18亿美元(访问控制:9.53亿美元,重入攻击:4.2亿美元)
When to Use
适用场景
- Before deploying any smart contract to mainnet
- When auditing existing contracts for security vulnerabilities
- When reviewing API contracts for invariant violations
- When adding new entry points or external calls to existing contracts
- When upgrading proxy contracts (storage slot collision risk)
- When integrating oracles, flash loans, or third-party DeFi protocols
- 任意智能合约部署到主网前
- 审计已上线合约的安全漏洞
- 排查API合约的不变量违反问题
- 为现有合约新增入口点或外部调用时
- 升级代理合约时(排查存储槽冲突风险)
- 集成预言机、闪电贷或第三方DeFi协议时
Iron Laws
铁律
- NEVER make external calls before updating state — Checks-Effects-Interactions (CEI) is non-negotiable; any external call before state update is a reentrancy vector regardless of perceived safety.
- NEVER assume access control is correct without reading every modifier — access control failures account for ~53% of 2024 DeFi losses; verify every ,
onlyOwner, and custom guard.onlyRole - NEVER trust integer arithmetic without explicit bounds checking — Solidity 0.8+ has native overflow protection but custom assembly, unchecked blocks, and Rust/Solana code require explicit verification.
- ALWAYS enumerate all contract invariants before analysis — invariants are the ground truth for correctness; a violation is always a bug; document them in NatSpec before reviewing the implementation.
- ALWAYS test reentrancy across full call chains, not just single functions — cross-function reentrancy (withdraw + transfer sharing state) is as dangerous as direct reentrancy.
- 永远不要在更新状态前发起外部调用 —— Checks-Effects-Interactions(CEI)是不可妥协的原则;无论看起来多安全,状态更新前的任意外部调用都是重入攻击向量。
- 永远不要在未读取所有修饰符实现的前提下假设访问控制是安全的 —— 访问控制故障占2024年DeFi损失的约53%;需逐一校验每个、
onlyOwner和自定义防护逻辑。onlyRole - 永远不要在没有显式边界检查的情况下信任整数运算 —— Solidity 0.8+自带溢出防护,但自定义汇编、unchecked代码块和Rust/Solana代码仍需显式验证。
- 分析前必须枚举所有合约不变量 —— 不变量是正确性的基础事实,违反就必然是bug;审查实现前需将所有不变量记录在NatSpec中。
- 必须测试完整调用链的重入风险,而非仅测试单个函数 —— 跨函数重入(提现和转账共享状态)与直接重入风险等级一致。
Phase 1: Contract Reconnaissance
阶段1:合约勘察
Goal: Map the attack surface before deep analysis.
目标:深度分析前完成攻击面梳理。
Steps
步骤
- Enumerate entry points: All external/public functions, fallback, receive
- Identify state-mutating functions: Functions that modify storage
- Map access control boundaries: Roles, modifiers, ownership checks
- Catalog external calls: ,
call(), ERC20 hooks, interface callstransfer() - Identify trust boundaries: User input, oracle feeds, cross-contract calls
- 枚举入口点:所有external/public函数、fallback、receive函数
- 识别状态变更函数:修改存储的函数
- 映射访问控制边界:角色、修饰符、所有权校验
- 盘点外部调用:、
call()、ERC20钩子、接口调用transfer() - 识别信任边界:用户输入、预言机喂价、跨合约调用
Output Format
输出格式
markdown
undefinedmarkdown
undefinedContract Reconnaissance
合约勘察
Entry Points
入口点
- — external, state-mutating, calls msg.sender
withdraw(uint256 amount) - — payable, updates balances mapping
deposit()
- — external, 状态变更, 调用msg.sender
withdraw(uint256 amount) - — payable, 更新balances映射
deposit()
Access Control Map
访问控制映射
- : [list of functions]
onlyOwner - : [list of functions]
onlyRole(ADMIN_ROLE) - No modifier (verify intent): [list of functions]
- : [函数列表]
onlyOwner - : [函数列表]
onlyRole(ADMIN_ROLE) - 无修饰符(校验设计意图): [函数列表]
External Calls
外部调用
- at withdraw():L45
msg.sender.call{value: amount}("") - at deposit():L23
token.transferFrom(...)
- 位于 withdraw():L45
msg.sender.call{value: amount}("") - 位于 deposit():L23
token.transferFrom(...)
Trust Boundaries
信任边界
- User-supplied amount at withdraw():L40
- Oracle price feed at getPrice():L67 — manipulation risk
undefined- withdraw():L40的用户输入amount
- getPrice():L67的预言机价格喂价 —— 存在操纵风险
undefinedPhase 2: Reentrancy Analysis
阶段2:重入攻击分析
Goal: Identify all reentrancy vectors (direct, cross-function, read-only).
目标:识别所有重入向量(直接、跨函数、只读)。
Checks-Effects-Interactions Verification
Checks-Effects-Interactions验证
For each function with external calls:
markdown
undefined针对每个含外部调用的函数:
markdown
undefinedFunction: withdraw(uint256 amount)
函数: withdraw(uint256 amount)
CEI Order Analysis
CEI顺序分析
- L40: CHECK — require(balances[msg.sender] >= amount) ✓
- L45: EXTERNAL CALL — msg.sender.call{value: amount}("") ← VIOLATION
- L48: EFFECT — balances[msg.sender] -= amount ← STATE AFTER CALL
FINDING: Classic reentrancy — balance updated after external call.
Fix: Move L48 before L45 (CEI pattern)
Severity: Critical
- L40: 校验 — require(balances[msg.sender] >= amount) ✓
- L45: 外部调用 — msg.sender.call{value: amount}("") ← 违反CEI
- L48: 生效 — balances[msg.sender] -= amount ← 状态更新在调用后
发现: 典型重入漏洞 —— 外部调用后才更新余额
修复方案: 将L48移至L45之前(遵循CEI模式)
严重等级: 危急
Fixed Pattern
修复后代码
solidity
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // Effect BEFORE external call
(bool success, ) = msg.sender.call{value: amount}("");
require(success);undefinedsolidity
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // 状态更新在外部调用之前
(bool success, ) = msg.sender.call{value: amount}("");
require(success);undefinedCross-Function Reentrancy Check
跨函数重入检查
Identify shared state between functions that both make external calls:
markdown
undefined识别存在外部调用的函数之间的共享状态:
markdown
undefinedShared State: balances mapping
共享状态: balances映射
- withdraw() reads + writes balances + makes external call
- emergencyWithdraw() reads + writes balances + makes external call RISK: Reentrancy from withdraw() into emergencyWithdraw() bypasses checks
undefined- withdraw() 读写balances + 发起外部调用
- emergencyWithdraw() 读写balances + 发起外部调用 风险: 从withdraw()重入到emergencyWithdraw()可绕过校验
undefinedPhase 3: Access Control Audit
阶段3:访问控制审计
Goal: Verify every state-mutating function has appropriate guards.
目标:验证每个状态变更函数都有合适的防护逻辑。
Access Control Checklist
访问控制检查清单
For each function:
markdown
undefined针对每个函数:
markdown
undefinedFunction Audit: updateTreasury(address newTreasury)
函数审计: updateTreasury(address newTreasury)
- Has access modifier? → NO ← FINDING: Missing onlyOwner
- Modifier verified in contract? → N/A (not present)
- Owner transferable safely? → N/A
- Time lock for critical changes? → NO
Severity: Critical — anyone can redirect protocol treasury
Fix: Add modifier and time-lock for parameter changes
onlyOwnerundefined- 存在访问修饰符? → 否 ← 漏洞: 缺失onlyOwner
- 修饰符在合约中已验证? → 不适用(不存在)
- 所有权转移逻辑安全? → 不适用
- 关键变更配置时间锁? → 否
严重等级: 危急 —— 任意用户均可转移协议国库资产
修复方案: 新增修饰符,并为参数变更配置时间锁
onlyOwnerundefinedRole Confusion Patterns
角色混淆模式
markdown
undefinedmarkdown
undefinedRole Check: PAUSER_ROLE vs ADMIN_ROLE
角色校验: PAUSER_ROLE vs ADMIN_ROLE
- pause() requires: PAUSER_ROLE
- unpause() requires: PAUSER_ROLE (RISK: pauser can also unpause)
- grantRole() requires: ADMIN_ROLE
Issue: Pauser can unilaterally pause and unpause — should require separate roles
Severity: Medium
undefined- pause() 要求: PAUSER_ROLE
- unpause() 要求: PAUSER_ROLE(风险: 暂停者可自行解除暂停)
- grantRole() 要求: ADMIN_ROLE
问题: 暂停者可单方面执行暂停和解除暂停操作,应拆分角色
严重等级: 中危
undefinedPhase 4: Integer Arithmetic Analysis
阶段4:整数运算分析
Goal: Identify overflow, underflow, precision loss, and rounding direction bugs.
目标:识别溢出、下溢、精度损失和舍入方向bug。
Arithmetic Boundary Analysis
运算边界分析
markdown
undefinedmarkdown
undefinedFunction: calculateReward(uint256 principal, uint256 rate)
函数: calculateReward(uint256 principal, uint256 rate)
- L88:
uint256 reward = principal * rate / 1e18- Multiplication before division: OK (avoids precision loss)
- Overflow check: principal * rate could overflow if both > sqrt(uint256.max)
- Rounding: truncates toward zero — check if favors protocol or user
- block? → NO → Solidity 0.8+ protects this
unchecked
- L88:
uint256 reward = principal * rate / 1e18- 先乘后除: 符合规范(避免精度损失)
- 溢出检查: 若principal和rate均大于sqrt(uint256.max),相乘会溢出
- 舍入规则: 向零截断 —— 需确认对协议或用户是否有利
- 是否在块中? → 否 → Solidity 0.8+ 自带防护
unchecked
Unchecked Block Analysis
Unchecked块分析
- L102-108:
unchecked { ... }- Why unchecked? Check comment and verify mathematician's claim
- Is the claimed impossibility of overflow actually proven?
- [UNVERIFIED] claim: "amount < balance guarantees no underflow"
undefined- L102-108:
unchecked { ... }- 为什么使用unchecked? 检查注释并验证算法合理性
- 所谓的不会溢出的声明是否得到了实际证明?
- [未验证] 声明: "amount < balance保证不会发生下溢"
undefinedPhase 5: Invariant Verification
阶段5:不变量验证
Goal: Identify and verify all contract-level invariants.
markdown
undefined目标:识别并验证所有合约级不变量。
markdown
undefinedContract Invariants: LiquidityPool
合约不变量: 流动性池
- Solvency: sum(balances) == address(this).balance — [VERIFIED L90]
- Total supply: totalSupply == sum(all user shares) — [UNVERIFIED]
- Fee bound: fee <= MAX_FEE (1000 bps) — [VERIFIED by require at L45]
- Non-zero denominator: totalSupply > 0 before share calculation — [VIOLATED at L67, division-by-zero risk on first deposit]
- 偿付能力: sum(balances) == address(this).balance — [已验证 L90]
- 总供应量: totalSupply == sum(所有用户份额) — [未验证]
- 手续费上限: fee <= MAX_FEE (1000 bps) — [已通过L45的require校验]
- 分母非零: 份额计算前totalSupply > 0 — [违反 L67, 首次存款存在除零风险]
Invariant Violation Findings
不变量违反发现
FINDING: Invariant 4 violated — first depositor can cause division by zero
- Location: L67
shares = amount * totalSupply / totalAssets - When: totalSupply == 0 on first deposit
- Impact: DoS attack on first deposit; protocol initialization blocked
- Fix: Handle zero totalSupply case separately with initial share ratio
undefined漏洞: 违反不变量4 —— 首次存款可能触发除零错误
- 位置: L67
shares = amount * totalSupply / totalAssets - 触发条件: 首次存款时totalSupply == 0
- 影响: 首次存款触发DoS攻击,协议初始化被阻断
- 修复方案: 单独处理totalSupply为零的场景,设置初始份额比例
undefinedOutput: Security Report
输出:安全报告
markdown
undefinedmarkdown
undefinedSecurity Report: [Contract Name]
安全报告: [合约名称]
Summary
摘要
- Functions analyzed: N
- Findings: N (Critical: X, High: Y, Medium: Z, Low: W)
- Invariants verified: N of M
- CEI violations: N
- 分析函数数量: N
- 漏洞数: N(危急: X, 高危: Y, 中危: Z, 低危: W)
- 已验证不变量: M个中的N个
- CEI违反数: N
Critical Findings
危急漏洞
[F-01] Reentrancy in withdraw()
[F-01] withdraw()存在重入漏洞
- Location:
src/Pool.sol:L45 - Pattern: External call before state update (CEI violation)
- Impact: Complete fund drainage
- Fix: Apply CEI pattern — update state before external call
- 5 Whys: [root cause chain]
- 位置:
src/Pool.sol:L45 - 模式: 状态更新前发起外部调用(CEI违反)
- 影响: 资产被完全盗走
- 修复方案: 遵循CEI模式 —— 外部调用前更新状态
- 5Why分析: [根因链]
Invariant Status
不变量状态
| Invariant | Status | Evidence |
|---|---|---|
| sum(balances) == balance | VERIFIED | L90 invariant check |
| totalSupply == sum(shares) | UNVERIFIED | No test coverage |
| 不变量 | 状态 | 证据 |
|---|---|---|
| sum(balances) == balance | 已验证 | L90 不变量检查 |
| totalSupply == sum(shares) | 未验证 | 无测试覆盖 |
Recommendations
建议
- [Critical] Fix reentrancy in withdraw() before deployment
- [High] Add reentrancy guard as defense-in-depth
- [Medium] Add formal invariant tests via Foundry invariant suite
undefined- [危急] 部署前修复withdraw()的重入漏洞
- [高危] 新增重入防护作为纵深防御措施
- [中危] 通过Foundry不变量测试套件添加形式化不变量测试
undefinedIntegration with Agent-Studio
与Agent-Studio集成
Recommended Workflow
推荐工作流
- Invoke for initial code reconnaissance
audit-context-building - Invoke for contract-specific analysis
building-secure-contracts - Feed findings into for threat modeling
security-architect - Use (Semgrep/CodeQL) for automated confirmation
static-analysis - Use for fuzzing-based invariant testing
medusa-security
- 调用完成初始代码勘察
audit-context-building - 调用执行合约专属分析
building-secure-contracts - 将漏洞发现输入完成威胁建模
security-architect - 使用(Semgrep/CodeQL)自动确认人工发现的漏洞
static-analysis - 使用执行基于模糊测试的不变量测试
medusa-security
Complementary Skills
互补技能
| Skill | Relationship |
|---|---|
| Builds initial mental model before contract analysis |
| Consumes findings for threat modeling and STRIDE |
| Automated SAST confirmation of manual findings |
| Fuzzing and property-based testing for invariants |
| Finds similar vulnerability patterns across codebase |
| Solidity/Ethereum ecosystem expertise |
| 技能名称 | 关联关系 |
|---|---|
| 合约分析前构建初始认知模型 |
| 消费漏洞发现结果用于威胁建模和STRIDE分析 |
| 自动化SAST验证人工发现的漏洞 |
| 不变量的模糊测试和基于属性的测试 |
| 挖掘代码库中相似的漏洞模式 |
| Solidity/Ethereum生态专业支持 |
Anti-Patterns
反模式
| Anti-Pattern | Why It Fails | Correct Approach |
|---|---|---|
| Auditing only the happy path | Reentrancy and access control bugs are invisible in happy path | Explicitly trace every error path and external call |
| Trusting function name for access control | | Read the modifier implementation, not just its name |
| Assuming Solidity 0.8 prevents all integer bugs | | Audit all |
| Skipping cross-function reentrancy | Cross-function reentrancy bypasses single-function guards | Map shared state across ALL functions making external calls |
| Leaving invariants implicit | Unwritten invariants are unverified risks | Document every invariant in NatSpec before analysis |
| 反模式 | 缺陷原因 | 正确做法 |
|---|---|---|
| 仅审计正常业务路径 | 重入和访问控制bug在正常路径下不可见 | 显式追踪每一条错误路径和外部调用 |
| 通过函数名判断访问控制安全性 | | 读取修饰符的实现,而非仅看名称 |
| 假设Solidity 0.8能防范所有整数bug | | 显式审计所有 |
| 忽略跨函数重入检查 | 跨函数重入可绕过单函数防护 | 映射所有发起外部调用的函数的共享状态 |
| 不显性声明不变量 | 未记录的不变量是未验证的风险 | 分析前将所有不变量记录在NatSpec中 |
Memory Protocol
记忆协议
Before starting: Check for prior contract audits of the same protocol or token standard.
.claude/context/memory/learnings.mdDuring analysis: Write incremental findings to context report as discovered. Do not wait until the end.
After completion: Record key findings and patterns to . Record architecture decisions (CEI enforcement patterns, invariant frameworks) to .
.claude/context/memory/learnings.mddecisions.md开始前: 检查中同一协议或代币标准的历史合约审计记录。
.claude/context/memory/learnings.md分析中: 发现漏洞时即时写入上下文报告,不要等到分析结束。
完成后: 将关键漏洞和模式记录到,将架构决策(CEI强制模式、不变量框架)记录到。
.claude/context/memory/learnings.mddecisions.md