Coverage for src/alprina_cli/agents/web3_auditor/economic_impact_calculator.py: 24%
125 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-14 11:27 +0100
« prev ^ index » next coverage.py v7.11.3, created at 2025-11-14 11:27 +0100
1"""
2Economic Impact Calculator for Smart Contract Vulnerabilities
4WEEK 2 DAY 3: Quantifies potential financial losses from detected vulnerabilities
5Based on OWASP Risk Rating + DeFi TVL analysis + 2024 exploit data
7Methodology:
8- Risk = Likelihood × Impact × Exploitability
9- TVL-adjusted loss estimates
10- Historical exploit data (2024: $730M in losses)
11- Severity-based impact multipliers
12"""
14from typing import Dict, Any, List, Optional, Tuple
15from dataclasses import dataclass
16from enum import Enum
17import re
20class ImpactCategory(Enum):
21 """Financial impact categories based on OWASP + DeFi standards"""
22 CATASTROPHIC = "catastrophic" # Complete protocol loss (>$10M or >80% TVL)
23 CRITICAL = "critical" # Major loss ($1M-$10M or 50-80% TVL)
24 HIGH = "high" # Significant loss ($100K-$1M or 20-50% TVL)
25 MEDIUM = "medium" # Moderate loss ($10K-$100K or 5-20% TVL)
26 LOW = "low" # Minor loss (<$10K or <5% TVL)
29@dataclass
30class EconomicImpact:
31 """Quantified economic impact assessment"""
32 vulnerability_type: str
33 severity: str
34 impact_category: ImpactCategory
35 estimated_loss_usd: Tuple[int, int] # (min, max) range
36 estimated_loss_percentage: Tuple[float, float] # (min, max) % of TVL
37 likelihood_score: float # 0.0-1.0
38 exploitability_score: float # 0.0-1.0
39 risk_score: float # 0-100 (combined)
40 time_to_exploit: str # "immediate", "hours", "days", "weeks"
41 attack_complexity: str # "low", "medium", "high"
42 historical_precedent: Optional[str] # Reference to real exploit
43 remediation_cost: str # Estimated developer hours
44 confidence: int # 0-100
47class EconomicImpactCalculator:
48 """
49 Calculate financial impact of smart contract vulnerabilities
51 WEEK 2 DAY 3: Economic Impact Assessment
53 Features:
54 - TVL-based loss estimation
55 - Severity-impact mapping (OWASP methodology)
56 - Historical exploit data integration
57 - Attack complexity analysis
58 - Time-to-exploit assessment
59 """
61 def __init__(self):
62 # 2024 DeFi statistics
63 self.defi_tvl_2024 = 100_000_000_000 # $100B (December 2024)
64 self.total_losses_2024 = 730_000_000 # $730M in hacks (2024)
66 # Historical exploit data
67 self.exploit_database = self._initialize_exploit_database()
69 # OWASP-based impact multipliers
70 self.severity_multipliers = {
71 "critical": 1.0, # Full impact
72 "high": 0.6, # 60% of full impact
73 "medium": 0.3, # 30% of full impact
74 "low": 0.1, # 10% of full impact
75 "info": 0.01 # 1% of full impact
76 }
78 def calculate_impact(
79 self,
80 vulnerability_type: str,
81 severity: str,
82 contract_context: Optional[Dict[str, Any]] = None
83 ) -> EconomicImpact:
84 """
85 Calculate economic impact for a vulnerability
87 Args:
88 vulnerability_type: Type of vulnerability (e.g., "oracle_manipulation")
89 severity: Severity level ("critical", "high", "medium", "low")
90 contract_context: Optional context (TVL, protocol type, etc.)
92 Returns:
93 EconomicImpact assessment with loss estimates
94 """
95 # Extract contract context
96 estimated_tvl = self._estimate_tvl(contract_context)
97 protocol_type = self._get_protocol_type(contract_context)
99 # Calculate base impact from vulnerability type
100 base_impact = self._get_base_impact(vulnerability_type, severity)
102 # Calculate likelihood of exploitation
103 likelihood = self._calculate_likelihood(vulnerability_type, severity)
105 # Calculate exploitability
106 exploitability = self._calculate_exploitability(vulnerability_type)
108 # Get time-to-exploit and attack complexity
109 time_to_exploit = self._get_time_to_exploit(vulnerability_type, severity)
110 attack_complexity = self._get_attack_complexity(vulnerability_type)
112 # Calculate TVL-adjusted loss range
113 loss_usd_range = self._calculate_loss_range(
114 base_impact,
115 estimated_tvl,
116 severity,
117 protocol_type
118 )
120 # Calculate percentage of TVL at risk
121 loss_percentage = (
122 (loss_usd_range[0] / estimated_tvl * 100) if estimated_tvl > 0 else 0,
123 (loss_usd_range[1] / estimated_tvl * 100) if estimated_tvl > 0 else 0
124 )
126 # Calculate overall risk score (OWASP methodology)
127 risk_score = self._calculate_risk_score(
128 likelihood,
129 base_impact,
130 exploitability
131 )
133 # Determine impact category
134 impact_category = self._categorize_impact(loss_usd_range[1], loss_percentage[1])
136 # Find historical precedent
137 historical_precedent = self._find_historical_exploit(vulnerability_type)
139 # Estimate remediation cost
140 remediation_cost = self._estimate_remediation_cost(vulnerability_type, severity)
142 # Calculate confidence based on data availability
143 confidence = self._calculate_confidence(
144 vulnerability_type,
145 contract_context,
146 historical_precedent
147 )
149 return EconomicImpact(
150 vulnerability_type=vulnerability_type,
151 severity=severity,
152 impact_category=impact_category,
153 estimated_loss_usd=loss_usd_range,
154 estimated_loss_percentage=loss_percentage,
155 likelihood_score=likelihood,
156 exploitability_score=exploitability,
157 risk_score=risk_score,
158 time_to_exploit=time_to_exploit,
159 attack_complexity=attack_complexity,
160 historical_precedent=historical_precedent,
161 remediation_cost=remediation_cost,
162 confidence=confidence
163 )
165 def _estimate_tvl(self, context: Optional[Dict[str, Any]]) -> int:
166 """Estimate TVL for the contract"""
167 if not context:
168 # Default: assume small protocol
169 return 1_000_000 # $1M
171 # Check if TVL provided
172 if 'tvl' in context:
173 return int(context['tvl'])
175 # Estimate based on protocol type
176 protocol_type = context.get('protocol_type', 'unknown')
178 tvl_estimates = {
179 'dex': 50_000_000, # $50M (average DEX)
180 'lending': 100_000_000, # $100M (average lending protocol)
181 'yield': 20_000_000, # $20M (average yield aggregator)
182 'bridge': 200_000_000, # $200M (average bridge)
183 'staking': 30_000_000, # $30M (average staking protocol)
184 'nft': 5_000_000, # $5M (average NFT platform)
185 'unknown': 1_000_000 # $1M (conservative default)
186 }
188 return tvl_estimates.get(protocol_type, 1_000_000)
190 def _get_protocol_type(self, context: Optional[Dict[str, Any]]) -> str:
191 """Determine protocol type from context"""
192 if not context:
193 return 'unknown'
195 return context.get('protocol_type', 'unknown')
197 def _get_base_impact(self, vulnerability_type: str, severity: str) -> float:
198 """
199 Get base impact score (0.0-1.0) for vulnerability type
201 Based on 2024 exploit data and OWASP methodology
202 """
203 # Vulnerability type impact mapping
204 impact_map = {
205 # Critical vulnerabilities (0.8-1.0)
206 'oracle_manipulation': 0.95, # $70M+ in 2024, 34.3% of exploits
207 'flash_loan_attack': 0.90, # $33.8M in 2024
208 'pool_reserve_manipulation': 0.95, # Most common DeFi exploit
209 'reentrancy': 0.85, # $35.7M in 2024
211 # High severity (0.6-0.8)
212 'access_control': 0.75, # $953.2M (but often preventable)
213 'unchecked_external_call': 0.70, # $550.7K in 2024
214 'input_validation': 0.65, # $14.6M in 2024
216 # Medium severity (0.3-0.6)
217 'integer_overflow': 0.50, # Reduced in Solidity 0.8+
218 'timestamp_dependence': 0.40,
219 'logic_error': 0.45, # $63.8M in 2024 (varied)
221 # Low severity (0.1-0.3)
222 'gas_limit': 0.15,
223 'denial_of_service': 0.20,
224 'uninitialized_storage': 0.25,
225 }
227 base = impact_map.get(vulnerability_type, 0.50) # Default: medium
229 # Apply severity multiplier
230 multiplier = self.severity_multipliers.get(severity.lower(), 0.50)
232 return base * multiplier
234 def _calculate_likelihood(self, vulnerability_type: str, severity: str) -> float:
235 """
236 Calculate likelihood of exploitation (0.0-1.0)
238 Based on:
239 - Exploit difficulty
240 - Public knowledge
241 - Attacker motivation (financial gain)
242 """
243 # Base likelihood by vulnerability type
244 likelihood_map = {
245 # High likelihood (public, easy to exploit)
246 'oracle_manipulation': 0.90, # Well-known, financially motivated
247 'flash_loan_attack': 0.85, # Common attack vector
248 'pool_reserve_manipulation': 0.95, # Easy to exploit
249 'unchecked_external_call': 0.80, # Simple to exploit
250 'input_validation': 0.75, # Common oversight
252 # Medium likelihood
253 'reentrancy': 0.70, # Well-known but requires setup
254 'access_control': 0.65, # Depends on configuration
255 'logic_error': 0.60, # Requires analysis
257 # Low likelihood
258 'integer_overflow': 0.40, # Mostly fixed in modern Solidity
259 'timestamp_dependence': 0.45,
260 'gas_limit': 0.30,
261 'denial_of_service': 0.35,
262 }
264 base_likelihood = likelihood_map.get(vulnerability_type, 0.50)
266 # Severity adjustment
267 severity_adjustments = {
268 'critical': 1.0,
269 'high': 0.9,
270 'medium': 0.7,
271 'low': 0.5,
272 'info': 0.2
273 }
275 adjustment = severity_adjustments.get(severity.lower(), 0.7)
277 return min(base_likelihood * adjustment, 1.0)
279 def _calculate_exploitability(self, vulnerability_type: str) -> float:
280 """
281 Calculate exploitability score (0.0-1.0)
283 Factors:
284 - Technical complexity
285 - Required resources
286 - Automation potential
287 """
288 exploitability_map = {
289 # Easy to exploit (0.8-1.0)
290 'pool_reserve_manipulation': 0.95, # Flash loan, single tx
291 'unchecked_external_call': 0.90, # Simple call, no validation
292 'input_validation': 0.85, # Straightforward parameter manipulation
293 'flash_loan_attack': 0.90, # Well-documented, tools available
295 # Moderate (0.5-0.8)
296 'oracle_manipulation': 0.75, # Requires capital or flash loan
297 'reentrancy': 0.70, # Requires contract creation
298 'access_control': 0.65, # May need specific permissions
300 # Complex (0.3-0.5)
301 'logic_error': 0.50, # Varies by specific issue
302 'timestamp_dependence': 0.45, # Limited manipulation window
303 'integer_overflow': 0.40, # Mostly prevented in 0.8+
304 }
306 return exploitability_map.get(vulnerability_type, 0.60)
308 def _calculate_loss_range(
309 self,
310 base_impact: float,
311 tvl: int,
312 severity: str,
313 protocol_type: str
314 ) -> Tuple[int, int]:
315 """
316 Calculate realistic loss range in USD
318 Returns (min_loss, max_loss) tuple
319 """
320 # Protocol-specific risk factors
321 protocol_risk = {
322 'dex': 0.60, # High liquidity drain risk
323 'lending': 0.70, # Flash loan risk
324 'bridge': 0.90, # Full TVL at risk (critical infrastructure)
325 'yield': 0.50, # Moderate risk
326 'staking': 0.40, # Lower risk (withdrawal delays)
327 'nft': 0.30, # Limited per-item value
328 'unknown': 0.50 # Default
329 }
331 risk_factor = protocol_risk.get(protocol_type, 0.50)
333 # Calculate maximum potential loss
334 max_loss = int(tvl * base_impact * risk_factor)
336 # Calculate minimum loss (20-40% of max, depending on severity)
337 severity_confidence = {
338 'critical': 0.60, # 60% of max (high confidence)
339 'high': 0.40, # 40% of max
340 'medium': 0.25, # 25% of max
341 'low': 0.15, # 15% of max
342 'info': 0.05 # 5% of max
343 }
345 min_multiplier = severity_confidence.get(severity.lower(), 0.30)
346 min_loss = int(max_loss * min_multiplier)
348 # Floor values (at least $1K for critical, scale down)
349 min_floors = {
350 'critical': 10_000,
351 'high': 5_000,
352 'medium': 1_000,
353 'low': 100,
354 'info': 10
355 }
357 floor = min_floors.get(severity.lower(), 1_000)
358 min_loss = max(min_loss, floor)
359 max_loss = max(max_loss, floor * 2)
361 return (min_loss, max_loss)
363 def _calculate_risk_score(
364 self,
365 likelihood: float,
366 impact: float,
367 exploitability: float
368 ) -> float:
369 """
370 Calculate overall risk score (0-100)
372 OWASP Formula: Risk = Likelihood × Impact × Exploitability
373 Scaled to 0-100
374 """
375 risk = likelihood * impact * exploitability * 100
376 return min(risk, 100.0)
378 def _categorize_impact(
379 self,
380 max_loss_usd: int,
381 max_loss_percentage: float
382 ) -> ImpactCategory:
383 """Categorize impact based on absolute and relative loss"""
384 # Catastrophic: >$10M or >80% TVL
385 if max_loss_usd > 10_000_000 or max_loss_percentage > 80:
386 return ImpactCategory.CATASTROPHIC
388 # Critical: $1M-$10M or 50-80% TVL
389 if max_loss_usd > 1_000_000 or max_loss_percentage > 50:
390 return ImpactCategory.CRITICAL
392 # High: $100K-$1M or 20-50% TVL
393 if max_loss_usd > 100_000 or max_loss_percentage > 20:
394 return ImpactCategory.HIGH
396 # Medium: $10K-$100K or 5-20% TVL
397 if max_loss_usd > 10_000 or max_loss_percentage > 5:
398 return ImpactCategory.MEDIUM
400 # Low: <$10K or <5% TVL
401 return ImpactCategory.LOW
403 def _get_time_to_exploit(self, vulnerability_type: str, severity: str) -> str:
404 """Estimate time required to exploit"""
405 # Immediate exploits (flash loan, single transaction)
406 immediate = ['flash_loan_attack', 'pool_reserve_manipulation', 'unchecked_external_call']
408 # Hours (requires setup, but straightforward)
409 hours = ['oracle_manipulation', 'reentrancy', 'input_validation']
411 # Days (requires analysis or complex setup)
412 days = ['access_control', 'logic_error']
414 # Weeks (requires deep analysis)
415 weeks = ['timestamp_dependence', 'gas_limit', 'denial_of_service']
417 if vulnerability_type in immediate:
418 return "immediate" if severity in ['critical', 'high'] else "hours"
419 elif vulnerability_type in hours:
420 return "hours" if severity == 'critical' else "days"
421 elif vulnerability_type in days:
422 return "days"
423 else:
424 return "weeks"
426 def _get_attack_complexity(self, vulnerability_type: str) -> str:
427 """Assess attack complexity"""
428 # Low complexity (well-documented, tools available)
429 low = ['flash_loan_attack', 'unchecked_external_call', 'input_validation',
430 'pool_reserve_manipulation']
432 # Medium complexity (requires some expertise)
433 medium = ['oracle_manipulation', 'reentrancy', 'access_control']
435 # High complexity (requires deep analysis)
436 high = ['logic_error', 'timestamp_dependence', 'integer_overflow']
438 if vulnerability_type in low:
439 return "low"
440 elif vulnerability_type in medium:
441 return "medium"
442 else:
443 return "high"
445 def _find_historical_exploit(self, vulnerability_type: str) -> Optional[str]:
446 """Find real-world exploit example"""
447 return self.exploit_database.get(vulnerability_type)
449 def _estimate_remediation_cost(self, vulnerability_type: str, severity: str) -> str:
450 """Estimate developer time to fix"""
451 # Base hours by type
452 base_hours = {
453 'oracle_manipulation': '8-16 hours',
454 'flash_loan_attack': '16-24 hours',
455 'reentrancy': '4-8 hours',
456 'access_control': '2-4 hours',
457 'unchecked_external_call': '1-2 hours',
458 'input_validation': '1-2 hours',
459 'logic_error': '8-40 hours', # Highly variable
460 'pool_reserve_manipulation': '16-32 hours',
461 }
463 return base_hours.get(vulnerability_type, '4-8 hours')
465 def _calculate_confidence(
466 self,
467 vulnerability_type: str,
468 context: Optional[Dict[str, Any]],
469 historical_precedent: Optional[str]
470 ) -> int:
471 """Calculate confidence in impact estimate (0-100)"""
472 confidence = 50 # Base confidence
474 # Boost if we have historical precedent
475 if historical_precedent:
476 confidence += 20
478 # Boost if we have TVL data
479 if context and 'tvl' in context:
480 confidence += 15
482 # Boost for well-studied vulnerabilities
483 well_studied = ['oracle_manipulation', 'flash_loan_attack', 'reentrancy',
484 'unchecked_external_call']
485 if vulnerability_type in well_studied:
486 confidence += 15
488 return min(confidence, 95) # Cap at 95% (never 100% certain)
490 def _initialize_exploit_database(self) -> Dict[str, str]:
491 """Initialize database of real exploits for reference"""
492 return {
493 'oracle_manipulation': 'Moby (Jan 2025, flash loan), The Vow (Aug 2024), Polter Finance (2024) - $70M+ total',
494 'flash_loan_attack': 'bZx (2020), Harvest Finance (2020), Cream Finance (2021) - $33.8M in 2024',
495 'pool_reserve_manipulation': 'Multiple DEX exploits - Most common DeFi attack (34.3%)',
496 'reentrancy': 'The DAO (2016, $60M), Lendf.Me (2020), Uniswap V1 (2020) - $35.7M in 2024',
497 'access_control': 'Poly Network (2021, $611M), Ronin Bridge (2022, $625M) - $953.2M in 2024',
498 'unchecked_external_call': 'Parity Wallet (2017, $150M+) - $550.7K in 2024',
499 'input_validation': 'Multiple protocols - $14.6M in 2024 (most common vulnerability)',
500 'logic_error': 'Various protocols - $63.8M in 2024',
501 }
503 def format_impact_report(self, impact: EconomicImpact) -> str:
504 """Format economic impact for human-readable display"""
505 return f"""
506╔══════════════════════════════════════════════════════════════════
507║ 💰 ECONOMIC IMPACT ASSESSMENT
508╠══════════════════════════════════════════════════════════════════
509║ Vulnerability: {impact.vulnerability_type}
510║ Severity: {impact.severity.upper()}
511║ Impact Category: {impact.impact_category.value.upper()}
512║
513║ 💵 Estimated Financial Loss:
514║ └─ Range: ${impact.estimated_loss_usd[0]:,} - ${impact.estimated_loss_usd[1]:,} USD
515║ └─ TVL %: {impact.estimated_loss_percentage[0]:.1f}% - {impact.estimated_loss_percentage[1]:.1f}%
516║
517║ 📊 Risk Assessment:
518║ └─ Overall Risk Score: {impact.risk_score:.1f}/100
519║ └─ Likelihood: {impact.likelihood_score*100:.0f}%
520║ └─ Exploitability: {impact.exploitability_score*100:.0f}%
521║
522║ ⚡ Attack Profile:
523║ └─ Time to Exploit: {impact.time_to_exploit}
524║ └─ Complexity: {impact.attack_complexity}
525║
526║ 🔧 Remediation:
527║ └─ Est. Developer Time: {impact.remediation_cost}
528║
529{"║ 📚 Historical Precedent:" if impact.historical_precedent else ""}
530{"║ └─ " + impact.historical_precedent if impact.historical_precedent else ""}
531║
532║ Confidence: {impact.confidence}%
533╚══════════════════════════════════════════════════════════════════
534"""