"在 CI 中运行模糊测试,比在生产中遇到攻击者便宜无数倍。" 好的合约工程 = 自动化流水线,让每次
git push都触发编译→测试→安全扫描→部署的完整链路。
13.6.1 GitHub Actions 流水线示例
graph LR
subgraph Push[git push / PR 触发]
S1[lint: prettier/solhint]
S2[compile: solc / foundry build]
S3[test: unit / integration / fuzz]
S4[coverage: codecov]
S5[security: Slither scan]
S6[gas-snapshot: 对比基线]
end
S1 --> S2 --> S3 --> S4 --> S5 --> S6
S5 --> |"高危发现"| Block[❌ 阻塞合并]
S6 --> |"regression > 1%"| Warn[⚠️ 警告]
style Block fill:#ffebee
style S1 fill:#c8e6c9
style S2 fill:#c8e6c9
style S3 fill:#c8e6c9
style S4 fill:#fff3e0
style S5 fill:#ffcdD2
style S6 fill:#fff3e0
基于 TypeScript 的 CI 配置模拟
typescript
/**
* 模拟 CI 流水线中的质量关卡(gate)检查
*/
interface CIGate {
name: string;
check(): { pass: boolean; message: string };
}
class CIPipeline {
private gates: CIGate[] = [];
addGate(gate: CIGate) { this.gates.push(gate); }
run(): { passed: boolean; results: { name: string; pass: boolean; message: string }[] } {
const results = this.gates.map(g => ({ name: g.name, ...g.check() }));
const passed = results.every(r => r.pass);
return { passed, results };
}
}
const pipeline = new CIPipeline();
// Step 1: 代码风格
pipeline.addGate({
name: "lint",
check() {
// solhint 检查:无未使用变量、无危险函数
const issues: string[] = [];
return {
pass: issues.length === 0,
message: issues.length === 0 ? "✅ All clean" : `❌ ${issues.join(", ")}`,
};
},
});
// Step 2: 编译
pipeline.addGate({
name: "compile",
check() {
return { pass: true, message: "✅ Foundry build succeeded" };
},
});
// Step 3: 测试
pipeline.addGate({
name: "unit-test",
check() {
const tests = 150; const failures = 0;
return {
pass: failures === 0,
message: `✅ {failures} failed`,
};
},
});
// Step 4: 覆盖率
pipeline.addGate({
name: "coverage",
check() {
const line = 92;
const branch = 84;
return {
pass: line >= 90 && branch >= 80,
message: `📊 Coverage: line {branch}%`,
};
},
});
// Step 5: 安全扫描(Slither)
pipeline.addGate({
name: "slither",
check() {
const results = {
critical: 0,
high: 0,
medium: 2,
low: 5,
};
const pass = results.critical === 0 && results.high === 0;
return {
pass,
message: pass
? `⚠️ Slither: {results.low} low`
: `❌ {results.high} high findings!`,
};
},
});
// Step 6: Gas 快照比对
pipeline.addGate({
name: "gas-snapshot",
check() {
const baseline = 1450000n;
const current = 1462000n;
const regression = Number((current - baseline) * 10000n / baseline) / 100;
return {
pass: regression < 1.0,
message: `⛽ Gas: {regression}%)`,
};
},
});
const result = pipeline.run();
console.log(`Pipeline: ${result.passed ? "✅ PASSED" : "❌ BLOCKED"}`);
for (const r of result.results) {
console.log(` {r.name})`);
}13.6.2 覆盖率标准
| 指标 | 目标值 | 说明 |
|---|---|---|
| 行覆盖率 | ≥ 90% | 每行代码至少被执行一次 |
| 分支覆盖率 | ≥ 80% | 每个 if/else 两个分支都执行过 |
| 函数覆盖率 | 100% | 所有 public/external 函数都被测试 |
| 状态覆盖 | 手动评估 | 关键状态组合(边界、异常路径) |
注意:100% 行覆盖率 != 没有漏洞。测试的是"已写代码的行为",但无法测试"遗漏的逻辑"。
13.6.3 Slither CI 集成配置
yaml
# .github/workflows/slither.yml 关键片段模拟
name: Slither
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: crytic/slither-action@v0.3.0
with:
node-version: 16
fail-on: 'high' # 发现高危则阻断
slither-args: '--filter-paths "node_modules|test"'> ← 13.5 合约验证 | 前往 → ch13-summary(本章总结) |*
评论
0评论加载中…