Spaces:
Running
Running
File size: 4,843 Bytes
55fd638 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | import re
from dataclasses import dataclass, field
from typing import List, Dict, Any
@dataclass
class VulnerabilityPattern:
name: str
description: str
patterns: List[str]
severity: str # HIGH | MEDIUM | LOW
category: str
recommendation: str = ""
VULNERABILITY_PATTERNS: List[VulnerabilityPattern] = [
VulnerabilityPattern(
name="reentrancy",
description="Reentrancy vulnerability – external call before state update",
patterns=[r"\.call\s*\(", r"\.send\s*\(", r"msg\.sender\.call"],
severity="HIGH",
category="reentrancy",
recommendation=(
"Use the checks-effects-interactions pattern: update state before "
"external calls. Consider OpenZeppelin's ReentrancyGuard."
),
),
VulnerabilityPattern(
name="integer_overflow",
description="Integer overflow / underflow (Solidity < 0.8)",
patterns=[r"pragma solidity\s+\^?0\.[0-7]", r"\+\+", r"--", r"\+=", r"-=", r"\*="],
severity="HIGH",
category="arithmetic",
recommendation="Use Solidity ^0.8.0 (built-in checks) or SafeMath for older versions.",
),
VulnerabilityPattern(
name="unchecked_call",
description="External call return value not checked",
patterns=[r"\.call\s*\([^)]*\)\s*;", r"\.send\s*\([^)]*\)\s*;"],
severity="MEDIUM",
category="unchecked_call",
recommendation="Always capture and verify the boolean return of low-level calls.",
),
VulnerabilityPattern(
name="tx_origin",
description="Authorization via tx.origin (phishing risk)",
patterns=[r"tx\.origin"],
severity="MEDIUM",
category="authorization",
recommendation="Replace tx.origin with msg.sender for access control checks.",
),
VulnerabilityPattern(
name="access_control",
description="Public function missing access control",
patterns=[r"function\s+\w+\s*\([^)]*\)\s*(public|external)(?!\s+view|\s+pure)"],
severity="MEDIUM",
category="access_control",
recommendation="Add onlyOwner or role-based modifiers to sensitive functions.",
),
VulnerabilityPattern(
name="timestamp_dependency",
description="Logic depends on block.timestamp (miner manipulation)",
patterns=[r"block\.timestamp", r"\bnow\b", r"block\.number"],
severity="LOW",
category="timestamp",
recommendation="Avoid using block.timestamp for critical decisions; use Chainlink VRF where randomness is needed.",
),
]
SEVERITY_SCORE = {"HIGH": 3, "MEDIUM": 2, "LOW": 1}
def analyze_with_patterns(solidity_code: str) -> Dict[str, Any]:
"""Run regex-based vulnerability detection and return structured report."""
lines = solidity_code.split("\n")
vulnerabilities: List[Dict[str, Any]] = []
severity_score = 0
for vp in VULNERABILITY_PATTERNS:
finding: Dict[str, Any] = {
"name": vp.name,
"description": vp.description,
"severity": vp.severity,
"category": vp.category,
"recommendation": vp.recommendation,
"detected": False,
"line_numbers": [],
"matches": [],
}
for i, line in enumerate(lines, start=1):
for pat in vp.patterns:
for m in re.finditer(pat, line, re.IGNORECASE):
finding["detected"] = True
finding["line_numbers"].append(i)
finding["matches"].append(
{"line": i, "text": line.strip(), "match": m.group()}
)
if finding["detected"]:
finding["line_numbers"] = sorted(set(finding["line_numbers"]))
vulnerabilities.append(finding)
severity_score += SEVERITY_SCORE[vp.severity]
high = [v for v in vulnerabilities if v["severity"] == "HIGH"]
medium = [v for v in vulnerabilities if v["severity"] == "MEDIUM"]
low = [v for v in vulnerabilities if v["severity"] == "LOW"]
if high:
risk_level = "CRITICAL"
elif medium:
risk_level = "MEDIUM"
elif low:
risk_level = "LOW"
else:
risk_level = "SAFE"
version_match = re.search(r"pragma\s+solidity\s+([^;]+);", solidity_code)
solidity_version = version_match.group(1).strip() if version_match else "unknown"
return {
"analysis_type": "pattern",
"solidity_version": solidity_version,
"total_lines": len(lines),
"risk_level": risk_level,
"severity_score": severity_score,
"vulnerabilities_count": len(vulnerabilities),
"vulnerabilities": vulnerabilities,
"high_count": len(high),
"medium_count": len(medium),
"low_count": len(low),
}
|