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

1""" 

2Economic Impact Calculator for Smart Contract Vulnerabilities 

3 

4WEEK 2 DAY 3: Quantifies potential financial losses from detected vulnerabilities 

5Based on OWASP Risk Rating + DeFi TVL analysis + 2024 exploit data 

6 

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""" 

13 

14from typing import Dict, Any, List, Optional, Tuple 

15from dataclasses import dataclass 

16from enum import Enum 

17import re 

18 

19 

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) 

27 

28 

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 

45 

46 

47class EconomicImpactCalculator: 

48 """ 

49 Calculate financial impact of smart contract vulnerabilities 

50 

51 WEEK 2 DAY 3: Economic Impact Assessment 

52 

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 """ 

60 

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) 

65 

66 # Historical exploit data 

67 self.exploit_database = self._initialize_exploit_database() 

68 

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 } 

77 

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 

86 

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.) 

91 

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) 

98 

99 # Calculate base impact from vulnerability type 

100 base_impact = self._get_base_impact(vulnerability_type, severity) 

101 

102 # Calculate likelihood of exploitation 

103 likelihood = self._calculate_likelihood(vulnerability_type, severity) 

104 

105 # Calculate exploitability 

106 exploitability = self._calculate_exploitability(vulnerability_type) 

107 

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) 

111 

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 ) 

119 

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 ) 

125 

126 # Calculate overall risk score (OWASP methodology) 

127 risk_score = self._calculate_risk_score( 

128 likelihood, 

129 base_impact, 

130 exploitability 

131 ) 

132 

133 # Determine impact category 

134 impact_category = self._categorize_impact(loss_usd_range[1], loss_percentage[1]) 

135 

136 # Find historical precedent 

137 historical_precedent = self._find_historical_exploit(vulnerability_type) 

138 

139 # Estimate remediation cost 

140 remediation_cost = self._estimate_remediation_cost(vulnerability_type, severity) 

141 

142 # Calculate confidence based on data availability 

143 confidence = self._calculate_confidence( 

144 vulnerability_type, 

145 contract_context, 

146 historical_precedent 

147 ) 

148 

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 ) 

164 

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 

170 

171 # Check if TVL provided 

172 if 'tvl' in context: 

173 return int(context['tvl']) 

174 

175 # Estimate based on protocol type 

176 protocol_type = context.get('protocol_type', 'unknown') 

177 

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 } 

187 

188 return tvl_estimates.get(protocol_type, 1_000_000) 

189 

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' 

194 

195 return context.get('protocol_type', 'unknown') 

196 

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 

200 

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 

210 

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 

215 

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) 

220 

221 # Low severity (0.1-0.3) 

222 'gas_limit': 0.15, 

223 'denial_of_service': 0.20, 

224 'uninitialized_storage': 0.25, 

225 } 

226 

227 base = impact_map.get(vulnerability_type, 0.50) # Default: medium 

228 

229 # Apply severity multiplier 

230 multiplier = self.severity_multipliers.get(severity.lower(), 0.50) 

231 

232 return base * multiplier 

233 

234 def _calculate_likelihood(self, vulnerability_type: str, severity: str) -> float: 

235 """ 

236 Calculate likelihood of exploitation (0.0-1.0) 

237 

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 

251 

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 

256 

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 } 

263 

264 base_likelihood = likelihood_map.get(vulnerability_type, 0.50) 

265 

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 } 

274 

275 adjustment = severity_adjustments.get(severity.lower(), 0.7) 

276 

277 return min(base_likelihood * adjustment, 1.0) 

278 

279 def _calculate_exploitability(self, vulnerability_type: str) -> float: 

280 """ 

281 Calculate exploitability score (0.0-1.0) 

282 

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 

294 

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 

299 

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 } 

305 

306 return exploitability_map.get(vulnerability_type, 0.60) 

307 

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 

317 

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 } 

330 

331 risk_factor = protocol_risk.get(protocol_type, 0.50) 

332 

333 # Calculate maximum potential loss 

334 max_loss = int(tvl * base_impact * risk_factor) 

335 

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 } 

344 

345 min_multiplier = severity_confidence.get(severity.lower(), 0.30) 

346 min_loss = int(max_loss * min_multiplier) 

347 

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 } 

356 

357 floor = min_floors.get(severity.lower(), 1_000) 

358 min_loss = max(min_loss, floor) 

359 max_loss = max(max_loss, floor * 2) 

360 

361 return (min_loss, max_loss) 

362 

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) 

371 

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) 

377 

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 

387 

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 

391 

392 # High: $100K-$1M or 20-50% TVL 

393 if max_loss_usd > 100_000 or max_loss_percentage > 20: 

394 return ImpactCategory.HIGH 

395 

396 # Medium: $10K-$100K or 5-20% TVL 

397 if max_loss_usd > 10_000 or max_loss_percentage > 5: 

398 return ImpactCategory.MEDIUM 

399 

400 # Low: <$10K or <5% TVL 

401 return ImpactCategory.LOW 

402 

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'] 

407 

408 # Hours (requires setup, but straightforward) 

409 hours = ['oracle_manipulation', 'reentrancy', 'input_validation'] 

410 

411 # Days (requires analysis or complex setup) 

412 days = ['access_control', 'logic_error'] 

413 

414 # Weeks (requires deep analysis) 

415 weeks = ['timestamp_dependence', 'gas_limit', 'denial_of_service'] 

416 

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" 

425 

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'] 

431 

432 # Medium complexity (requires some expertise) 

433 medium = ['oracle_manipulation', 'reentrancy', 'access_control'] 

434 

435 # High complexity (requires deep analysis) 

436 high = ['logic_error', 'timestamp_dependence', 'integer_overflow'] 

437 

438 if vulnerability_type in low: 

439 return "low" 

440 elif vulnerability_type in medium: 

441 return "medium" 

442 else: 

443 return "high" 

444 

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) 

448 

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 } 

462 

463 return base_hours.get(vulnerability_type, '4-8 hours') 

464 

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 

473 

474 # Boost if we have historical precedent 

475 if historical_precedent: 

476 confidence += 20 

477 

478 # Boost if we have TVL data 

479 if context and 'tvl' in context: 

480 confidence += 15 

481 

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 

487 

488 return min(confidence, 95) # Cap at 95% (never 100% certain) 

489 

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 } 

502 

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"""