1use std::collections::BTreeMap;
7
8#[derive(Debug, Clone)]
10pub struct AttackPattern {
11 pub id: String,
13 pub name: String,
15 pub description: String,
17 pub year: u32,
19 pub incidents: Vec<String>,
21 pub vulnerable_patterns: Vec<String>,
23 pub defensive_invariants: Vec<String>,
25 pub affected_chains: Vec<String>,
27 pub cvss_score: f32,
29}
30
31pub struct AttackPatternDB {
33 patterns: BTreeMap<String, AttackPattern>,
34}
35
36impl AttackPatternDB {
37 pub fn new() -> Self {
39 let mut patterns = BTreeMap::new();
40
41 patterns.insert(
43 "reentrancy".to_string(),
44 AttackPattern {
45 id: "reentrancy".to_string(),
46 name: "Reentrancy".to_string(),
47 description:
48 "Attacker calls back into contract during execution, modifying state before \
49 previous execution completes"
50 .to_string(),
51 year: 2016,
52 incidents: vec!["The DAO (2016) - $50M loss".to_string()],
53 vulnerable_patterns: vec![
54 "transfer_funds(); /* state update after */".to_string(),
55 "transfer(amount)".to_string(),
56 "delegatecall".to_string(),
57 "state update AFTER external call".to_string(),
58 "payable(msg.sender).transfer".to_string(),
59 "call.value()() without checking re-entry".to_string(),
60 "state_change_after_external_call".to_string(),
61 ],
62 defensive_invariants: vec![
63 "state_update_before_external_call".to_string(),
64 "mutex_lock_during_transfer".to_string(),
65 "checks_effects_interactions_order".to_string(),
66 "balance_matches_sum_before_and_after".to_string(),
67 ],
68 affected_chains: vec!["evm".to_string()],
69 cvss_score: 9.8,
70 },
71 );
72
73 patterns.insert(
75 "integer_overflow".to_string(),
76 AttackPattern {
77 id: "integer_overflow".to_string(),
78 name: "Integer Overflow/Underflow".to_string(),
79 description:
80 "Arithmetic operations exceed max/min bounds, wrapping to opposite extreme"
81 .to_string(),
82 year: 2018,
83 incidents: vec![
84 "BEC Token (2018) - $7.6M frozen".to_string(),
85 "BeautyChain (2018) - batch transfer bug".to_string(),
86 ],
87 vulnerable_patterns: vec![
88 "unchecked_addition".to_string(),
89 "unchecked_subtraction".to_string(),
90 "balance + amount without overflow check".to_string(),
91 ],
92 defensive_invariants: vec![
93 "addition_with_overflow_check".to_string(),
94 "subtraction_with_underflow_check".to_string(),
95 "total_supply_constant".to_string(),
96 "balance_never_negative".to_string(),
97 ],
98 affected_chains: vec!["evm".to_string(), "move".to_string()],
99 cvss_score: 8.5,
100 },
101 );
102
103 patterns.insert(
105 "access_control_bypass".to_string(),
106 AttackPattern {
107 id: "access_control_bypass".to_string(),
108 name: "Access Control Bypass".to_string(),
109 description:
110 "Attacker circumvents permission checks to perform privileged operations"
111 .to_string(),
112 year: 2017,
113 incidents: vec!["Parity Wallet (2017) - $30M frozen".to_string()],
114 vulnerable_patterns: vec![
115 "missing_require(is_owner())".to_string(),
116 "tx.origin != msg.sender".to_string(),
117 "no_signature_validation".to_string(),
118 "public_function_without_auth".to_string(),
119 ],
120 defensive_invariants: vec![
121 "only_owner_can_transfer".to_string(),
122 "multisig_required_for_critical_ops".to_string(),
123 "all_privileged_ops_checked".to_string(),
124 "authorization_before_state_change".to_string(),
125 ],
126 affected_chains: vec!["evm".to_string(), "solana".to_string(), "move".to_string()],
127 cvss_score: 9.9,
128 },
129 );
130
131 patterns.insert(
133 "flash_loan".to_string(),
134 AttackPattern {
135 id: "flash_loan".to_string(),
136 name: "Flash Loan Attack".to_string(),
137 description:
138 "Attacker borrows large amount in single transaction to manipulate price"
139 .to_string(),
140 year: 2020,
141 incidents: vec![
142 "bZx (2020) - $350K + $600K losses".to_string(),
143 "Harvest Finance (2020) - $34M loss".to_string(),
144 ],
145 vulnerable_patterns: vec![
146 "price_oracle_single_source".to_string(),
147 "no_price_validation".to_string(),
148 "lending_without_collateral_check".to_string(),
149 ],
150 defensive_invariants: vec![
151 "price_from_multiple_sources".to_string(),
152 "collateral_check_before_lending".to_string(),
153 "price_deviation_limits".to_string(),
154 "no_same_block_operations".to_string(),
155 ],
156 affected_chains: vec!["evm".to_string()],
157 cvss_score: 8.7,
158 },
159 );
160
161 patterns.insert(
163 "frontrunning".to_string(),
164 AttackPattern {
165 id: "frontrunning".to_string(),
166 name: "Frontrunning / MEV Extraction".to_string(),
167 description:
168 "Attacker observes pending transaction and places own transaction first"
169 .to_string(),
170 year: 2018,
171 incidents: vec!["General vulnerability since Ethereum inception".to_string()],
172 vulnerable_patterns: vec![
173 "price_depends_on_order".to_string(),
174 "state_visible_in_mempool".to_string(),
175 "no_slippage_protection".to_string(),
176 ],
177 defensive_invariants: vec![
178 "slippage_limits_enforced".to_string(),
179 "atomic_swap_no_intermediate_states".to_string(),
180 "timestamp_deadline_checks".to_string(),
181 "sorted_by_priority_not_order".to_string(),
182 ],
183 affected_chains: vec!["evm".to_string()],
184 cvss_score: 7.5,
185 },
186 );
187
188 patterns.insert(
190 "type_confusion".to_string(),
191 AttackPattern {
192 id: "type_confusion".to_string(),
193 name: "Type Confusion / Implicit Conversion".to_string(),
194 description: "Implicit type conversions cause incorrect comparisons or operations"
195 .to_string(),
196 year: 2019,
197 incidents: vec!["Multiplier Finance (2021) - $1M loss".to_string()],
198 vulnerable_patterns: vec![
199 "implicit_type_conversion".to_string(),
200 "comparison_different_types".to_string(),
201 "address_to_uint_conversion".to_string(),
202 ],
203 defensive_invariants: vec![
204 "no_implicit_conversions".to_string(),
205 "explicit_type_matching_required".to_string(),
206 "type_checked_before_comparison".to_string(),
207 ],
208 affected_chains: vec!["evm".to_string()],
209 cvss_score: 7.2,
210 },
211 );
212
213 patterns.insert(
215 "delegatecall_misuse".to_string(),
216 AttackPattern {
217 id: "delegatecall_misuse".to_string(),
218 name: "Delegatecall to Untrusted Code".to_string(),
219 description: "Contract delegatecalls to address that can be controlled by attacker"
220 .to_string(),
221 year: 2016,
222 incidents: vec!["King of the Ether (2016) - theft of contract funds".to_string()],
223 vulnerable_patterns: vec![
224 "delegatecall(attacker_address)".to_string(),
225 "delegatecall_to_user_input".to_string(),
226 "no_validation_before_delegatecall".to_string(),
227 ],
228 defensive_invariants: vec![
229 "delegatecall_target_hardcoded".to_string(),
230 "delegatecall_target_audited".to_string(),
231 "no_delegatecall_to_untrusted".to_string(),
232 "delegatecall_results_validated".to_string(),
233 ],
234 affected_chains: vec!["evm".to_string()],
235 cvss_score: 9.8,
236 },
237 );
238
239 patterns.insert(
241 "timestamp_dependence".to_string(),
242 AttackPattern {
243 id: "timestamp_dependence".to_string(),
244 name: "Timestamp Dependence".to_string(),
245 description: "Miner/validator can manipulate block timestamp for advantage"
246 .to_string(),
247 year: 2015,
248 incidents: vec!["Various lottery and randomness exploits".to_string()],
249 vulnerable_patterns: vec![
250 "random_number = block.timestamp".to_string(),
251 "critical_logic_depends_on_block.timestamp".to_string(),
252 "no_time_bounds_checking".to_string(),
253 ],
254 defensive_invariants: vec![
255 "no_randomness_from_timestamp".to_string(),
256 "randomness_from_external_oracle".to_string(),
257 "time_bounds_enforced".to_string(),
258 "timestamp_within_reasonable_bounds".to_string(),
259 ],
260 affected_chains: vec!["evm".to_string()],
261 cvss_score: 6.5,
262 },
263 );
264
265 Self { patterns }
266 }
267
268 pub fn all_patterns(&self) -> Vec<&AttackPattern> {
270 self.patterns.values().collect()
271 }
272
273 pub fn patterns_for_chain(&self, chain: &str) -> Vec<&AttackPattern> {
275 self.patterns
276 .values()
277 .filter(|p| p.affected_chains.contains(&chain.to_string()))
278 .collect()
279 }
280
281 pub fn get_pattern(&self, id: &str) -> Option<&AttackPattern> {
283 self.patterns.get(id)
284 }
285
286 pub fn check_code(&self, code: &str, attack_id: &str) -> Vec<String> {
288 let mut issues = Vec::new();
289
290 if let Some(pattern) = self.get_pattern(attack_id) {
291 for vulnerable_pattern in &pattern.vulnerable_patterns {
292 if code.contains(vulnerable_pattern) {
293 issues.push(format!(
294 "Found vulnerable pattern '{}' from {} attack",
295 vulnerable_pattern, pattern.name
296 ));
297 }
298 }
299 }
300
301 issues
302 }
303}
304
305impl Default for AttackPatternDB {
306 fn default() -> Self {
307 Self::new()
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn test_attack_db_creation() {
317 let db = AttackPatternDB::new();
318 assert_eq!(db.all_patterns().len(), 8);
319 }
320
321 #[test]
322 fn test_get_pattern() {
323 let db = AttackPatternDB::new();
324 let pattern = db.get_pattern("reentrancy").unwrap();
325 assert_eq!(pattern.name, "Reentrancy");
326 assert_eq!(pattern.year, 2016);
327 }
328
329 #[test]
330 fn test_patterns_for_chain() {
331 let db = AttackPatternDB::new();
332 let evm_patterns = db.patterns_for_chain("evm");
333 assert!(!evm_patterns.is_empty());
334
335 let solana_patterns = db.patterns_for_chain("solana");
336 assert!(solana_patterns
337 .iter()
338 .any(|p| p.id == "access_control_bypass"));
339 }
340
341 #[test]
342 fn test_code_vulnerability_check() {
343 let db = AttackPatternDB::new();
344 let vulnerable_code = "transfer_funds(); /* state update after */";
345 let issues = db.check_code(vulnerable_code, "reentrancy");
346 assert!(!issues.is_empty());
347 }
348
349 #[test]
350 fn test_cvss_scores() {
351 let db = AttackPatternDB::new();
352 for pattern in db.all_patterns() {
353 assert!(pattern.cvss_score > 0.0 && pattern.cvss_score <= 10.0);
354 }
355 }
356}