Spaces:
Sleeping
Sleeping
tanaymitra98 commited on
Commit ·
7998b79
1
Parent(s): 6e43eb6
Rebuild task setup with 4 enabled graded tasks and multi-task inference
Browse files- README.md +4 -2
- data/manifest.json +55 -1
- graders.py +48 -0
- inference.py +93 -45
- openenv.yaml +23 -6
- server/app.py +142 -86
README.md
CHANGED
|
@@ -15,10 +15,11 @@ SolidityGuard is an OpenEnv RL environment that trains agents to review Solidity
|
|
| 15 |
|
| 16 |
## Overview
|
| 17 |
|
| 18 |
-
SolidityGuard provides a comprehensive auditing platform for Solidity smart contracts with
|
| 19 |
- **Task 1 (Easy)**: Best Practices & Syntax Issues
|
| 20 |
- **Task 2 (Medium)**: Gas Optimization Opportunities
|
| 21 |
- **Task 3 (Hard)**: Security Vulnerabilities
|
|
|
|
| 22 |
|
| 23 |
## Quick Start
|
| 24 |
|
|
@@ -68,6 +69,7 @@ JSON array of findings:
|
|
| 68 |
- Task 1: Best practices and syntax (missing SPDX, old compiler, missing NatSpec, deprecated constructor)
|
| 69 |
- Task 2: Gas optimization (unbounded loops, redundant storage reads, custom errors)
|
| 70 |
- Task 3: Security vulnerabilities (reentrancy, missing access control, tx.origin auth)
|
|
|
|
| 71 |
|
| 72 |
## API Endpoints
|
| 73 |
|
|
@@ -102,4 +104,4 @@ Health check endpoint.
|
|
| 102 |
## Notes
|
| 103 |
- Runtime should stay under 20 minutes on 2 vCPU / 8 GB.
|
| 104 |
- Docker build must succeed for submission.
|
| 105 |
-
- Use Hugging Face Inference Providers for LLM inference.
|
|
|
|
| 15 |
|
| 16 |
## Overview
|
| 17 |
|
| 18 |
+
SolidityGuard provides a comprehensive auditing platform for Solidity smart contracts with four tasks:
|
| 19 |
- **Task 1 (Easy)**: Best Practices & Syntax Issues
|
| 20 |
- **Task 2 (Medium)**: Gas Optimization Opportunities
|
| 21 |
- **Task 3 (Hard)**: Security Vulnerabilities
|
| 22 |
+
- **Task 4 (Hard)**: Comprehensive Audit (cross-category)
|
| 23 |
|
| 24 |
## Quick Start
|
| 25 |
|
|
|
|
| 69 |
- Task 1: Best practices and syntax (missing SPDX, old compiler, missing NatSpec, deprecated constructor)
|
| 70 |
- Task 2: Gas optimization (unbounded loops, redundant storage reads, custom errors)
|
| 71 |
- Task 3: Security vulnerabilities (reentrancy, missing access control, tx.origin auth)
|
| 72 |
+
- Task 4: Comprehensive audit across best practices, gas, and security
|
| 73 |
|
| 74 |
## API Endpoints
|
| 75 |
|
|
|
|
| 104 |
## Notes
|
| 105 |
- Runtime should stay under 20 minutes on 2 vCPU / 8 GB.
|
| 106 |
- Docker build must succeed for submission.
|
| 107 |
+
- Use Hugging Face Inference Providers for LLM inference.
|
data/manifest.json
CHANGED
|
@@ -412,5 +412,59 @@
|
|
| 412 |
"severity": "Medium"
|
| 413 |
}
|
| 414 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
}
|
| 416 |
-
]
|
|
|
|
| 412 |
"severity": "Medium"
|
| 413 |
}
|
| 414 |
]
|
| 415 |
+
},
|
| 416 |
+
{
|
| 417 |
+
"id": "t4_sample_1",
|
| 418 |
+
"task_id": "task_4_comprehensive_audit",
|
| 419 |
+
"source_path": "data/samples/task1/old_pragma.sol",
|
| 420 |
+
"metadata": {
|
| 421 |
+
"contract_name": "OldPragma",
|
| 422 |
+
"compiler_version": "0.4.25",
|
| 423 |
+
"file_path": "old_pragma.sol"
|
| 424 |
+
},
|
| 425 |
+
"labels": [
|
| 426 |
+
{
|
| 427 |
+
"issue_type": "old_compiler_version",
|
| 428 |
+
"line_number": 2,
|
| 429 |
+
"description": "Compiler version below 0.8.x",
|
| 430 |
+
"severity": "Low"
|
| 431 |
+
}
|
| 432 |
+
]
|
| 433 |
+
},
|
| 434 |
+
{
|
| 435 |
+
"id": "t4_sample_2",
|
| 436 |
+
"task_id": "task_4_comprehensive_audit",
|
| 437 |
+
"source_path": "data/samples/task2/inefficient_loop.sol",
|
| 438 |
+
"metadata": {
|
| 439 |
+
"contract_name": "LoopGas",
|
| 440 |
+
"compiler_version": "0.8.17",
|
| 441 |
+
"file_path": "inefficient_loop.sol"
|
| 442 |
+
},
|
| 443 |
+
"labels": [
|
| 444 |
+
{
|
| 445 |
+
"issue_type": "unbounded_loop",
|
| 446 |
+
"line_number": 10,
|
| 447 |
+
"description": "Loop uses dynamic array length without bounds",
|
| 448 |
+
"severity": "Medium"
|
| 449 |
+
}
|
| 450 |
+
]
|
| 451 |
+
},
|
| 452 |
+
{
|
| 453 |
+
"id": "t4_sample_3",
|
| 454 |
+
"task_id": "task_4_comprehensive_audit",
|
| 455 |
+
"source_path": "data/samples/task3/tx_origin_auth.sol",
|
| 456 |
+
"metadata": {
|
| 457 |
+
"contract_name": "OriginAuth",
|
| 458 |
+
"compiler_version": "0.8.20",
|
| 459 |
+
"file_path": "tx_origin_auth.sol"
|
| 460 |
+
},
|
| 461 |
+
"labels": [
|
| 462 |
+
{
|
| 463 |
+
"issue_type": "tx_origin_auth",
|
| 464 |
+
"line_number": 11,
|
| 465 |
+
"description": "Authorization uses tx.origin",
|
| 466 |
+
"severity": "Critical"
|
| 467 |
+
}
|
| 468 |
+
]
|
| 469 |
}
|
| 470 |
+
]
|
graders.py
CHANGED
|
@@ -24,6 +24,30 @@ class Grader:
|
|
| 24 |
"""Grade the action against expected results. Returns (score, details)."""
|
| 25 |
return grade_action(action, expected)
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
def _normalize_issue(issue: Dict[str, Any]) -> Issue:
|
| 29 |
return Issue(
|
|
@@ -202,3 +226,27 @@ def grade(
|
|
| 202 |
) -> Tuple[float, Dict[str, Any]]:
|
| 203 |
"""Compatibility alias for validators expecting graders:grade."""
|
| 204 |
return grade_action(action, expected)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
"""Grade the action against expected results. Returns (score, details)."""
|
| 25 |
return grade_action(action, expected)
|
| 26 |
|
| 27 |
+
@staticmethod
|
| 28 |
+
def grade_task_1(
|
| 29 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 30 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 31 |
+
return grade_action(action, expected)
|
| 32 |
+
|
| 33 |
+
@staticmethod
|
| 34 |
+
def grade_task_2(
|
| 35 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 36 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 37 |
+
return grade_action(action, expected)
|
| 38 |
+
|
| 39 |
+
@staticmethod
|
| 40 |
+
def grade_task_3(
|
| 41 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 42 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 43 |
+
return grade_action(action, expected)
|
| 44 |
+
|
| 45 |
+
@staticmethod
|
| 46 |
+
def grade_task_4(
|
| 47 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 48 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 49 |
+
return grade_action(action, expected)
|
| 50 |
+
|
| 51 |
|
| 52 |
def _normalize_issue(issue: Dict[str, Any]) -> Issue:
|
| 53 |
return Issue(
|
|
|
|
| 226 |
) -> Tuple[float, Dict[str, Any]]:
|
| 227 |
"""Compatibility alias for validators expecting graders:grade."""
|
| 228 |
return grade_action(action, expected)
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def grade_task_1(
|
| 232 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 233 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 234 |
+
return grade_action(action, expected)
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def grade_task_2(
|
| 238 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 239 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 240 |
+
return grade_action(action, expected)
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def grade_task_3(
|
| 244 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 245 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 246 |
+
return grade_action(action, expected)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def grade_task_4(
|
| 250 |
+
action: List[Dict[str, Any]], expected: List[Dict[str, Any]]
|
| 251 |
+
) -> Tuple[float, Dict[str, Any]]:
|
| 252 |
+
return grade_action(action, expected)
|
inference.py
CHANGED
|
@@ -30,9 +30,15 @@ API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
|
|
| 30 |
API_BASE_URL = os.getenv("API_BASE_URL") or "https://router.huggingface.co/v1"
|
| 31 |
MODEL_NAME = os.getenv("MODEL_NAME") or "Qwen/Qwen2.5-72B-Instruct"
|
| 32 |
|
| 33 |
-
# Task configuration - validator
|
| 34 |
-
TASK_NAME = os.getenv("SOLIDITYGUARD_TASK") or os.getenv("TASK_NAME")
|
| 35 |
BENCHMARK = os.getenv("SOLIDITYGUARD_BENCHMARK", "solidityguard")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
MAX_STEPS = 1 # Each task has 1 step in our environment
|
| 38 |
SUCCESS_SCORE_THRESHOLD = 0.1
|
|
@@ -46,7 +52,9 @@ def log_start(task: str, env: str, model: str) -> None:
|
|
| 46 |
print(f"[{START_TAG}] task={task} env={env} model={model}", flush=True)
|
| 47 |
|
| 48 |
|
| 49 |
-
def log_step(
|
|
|
|
|
|
|
| 50 |
error_val = error if error else "null"
|
| 51 |
done_val = str(done).lower()
|
| 52 |
print(
|
|
@@ -84,6 +92,7 @@ def _build_prompt(source_code: str, task_id: str) -> str:
|
|
| 84 |
"task_1_best_practices": "Find syntax and best-practice issues: missing SPDX license, old compiler version (<0.8.x), missing NatSpec comments, deprecated constructor syntax.",
|
| 85 |
"task_2_gas_optimization": "Find gas optimization opportunities: unbounded loops, redundant storage reads, missing custom errors (use custom errors instead of require strings).",
|
| 86 |
"task_3_security": "Find security vulnerabilities: reentrancy bugs, missing access control, tx.origin usage for authorization, integer overflow/underflow.",
|
|
|
|
| 87 |
}
|
| 88 |
return (
|
| 89 |
"Review this Solidity contract and return ONLY a JSON array of findings. "
|
|
@@ -96,7 +105,7 @@ def _build_prompt(source_code: str, task_id: str) -> str:
|
|
| 96 |
def _fallback_actions(source_code: str, task_id: str) -> List[Dict[str, Any]]:
|
| 97 |
"""Deterministic fallback when LLM call fails or returns empty."""
|
| 98 |
lowered = source_code.lower()
|
| 99 |
-
|
| 100 |
if task_id == "task_1_best_practices":
|
| 101 |
return [
|
| 102 |
{
|
|
@@ -169,6 +178,46 @@ def _fallback_actions(source_code: str, task_id: str) -> List[Dict[str, Any]]:
|
|
| 169 |
}
|
| 170 |
]
|
| 171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
# Default fallback
|
| 173 |
return [
|
| 174 |
{
|
|
@@ -197,13 +246,13 @@ def _call_model(client: OpenAI, prompt: str) -> List[Dict[str, Any]]:
|
|
| 197 |
stream=False,
|
| 198 |
)
|
| 199 |
content = (response.choices[0].message.content or "[]").strip()
|
| 200 |
-
|
| 201 |
# Handle markdown code blocks
|
| 202 |
if content.startswith("```"):
|
| 203 |
parts = content.split("```")
|
| 204 |
if len(parts) >= 2:
|
| 205 |
content = parts[1].replace("json", "", 1).strip()
|
| 206 |
-
|
| 207 |
parsed = json.loads(content)
|
| 208 |
if isinstance(parsed, list):
|
| 209 |
return parsed
|
|
@@ -220,48 +269,47 @@ def main() -> None:
|
|
| 220 |
steps_taken = 0
|
| 221 |
score = 0.0
|
| 222 |
success = False
|
|
|
|
| 223 |
|
| 224 |
-
log_start(task=
|
| 225 |
|
| 226 |
try:
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
if done:
|
| 264 |
-
break
|
| 265 |
|
| 266 |
# Calculate final score
|
| 267 |
score = _safe_score(sum(rewards) / max(len(rewards), 1))
|
|
|
|
| 30 |
API_BASE_URL = os.getenv("API_BASE_URL") or "https://router.huggingface.co/v1"
|
| 31 |
MODEL_NAME = os.getenv("MODEL_NAME") or "Qwen/Qwen2.5-72B-Instruct"
|
| 32 |
|
| 33 |
+
# Task configuration - validator may set this for single-task runs
|
| 34 |
+
TASK_NAME = os.getenv("SOLIDITYGUARD_TASK") or os.getenv("TASK_NAME")
|
| 35 |
BENCHMARK = os.getenv("SOLIDITYGUARD_BENCHMARK", "solidityguard")
|
| 36 |
+
DEFAULT_TASKS = [
|
| 37 |
+
"task_1_best_practices",
|
| 38 |
+
"task_2_gas_optimization",
|
| 39 |
+
"task_3_security",
|
| 40 |
+
"task_4_comprehensive_audit",
|
| 41 |
+
]
|
| 42 |
|
| 43 |
MAX_STEPS = 1 # Each task has 1 step in our environment
|
| 44 |
SUCCESS_SCORE_THRESHOLD = 0.1
|
|
|
|
| 52 |
print(f"[{START_TAG}] task={task} env={env} model={model}", flush=True)
|
| 53 |
|
| 54 |
|
| 55 |
+
def log_step(
|
| 56 |
+
step: int, action: str, reward: float, done: bool, error: Optional[str]
|
| 57 |
+
) -> None:
|
| 58 |
error_val = error if error else "null"
|
| 59 |
done_val = str(done).lower()
|
| 60 |
print(
|
|
|
|
| 92 |
"task_1_best_practices": "Find syntax and best-practice issues: missing SPDX license, old compiler version (<0.8.x), missing NatSpec comments, deprecated constructor syntax.",
|
| 93 |
"task_2_gas_optimization": "Find gas optimization opportunities: unbounded loops, redundant storage reads, missing custom errors (use custom errors instead of require strings).",
|
| 94 |
"task_3_security": "Find security vulnerabilities: reentrancy bugs, missing access control, tx.origin usage for authorization, integer overflow/underflow.",
|
| 95 |
+
"task_4_comprehensive_audit": "Find a complete mix of issues across best-practices, gas optimization, and security vulnerabilities.",
|
| 96 |
}
|
| 97 |
return (
|
| 98 |
"Review this Solidity contract and return ONLY a JSON array of findings. "
|
|
|
|
| 105 |
def _fallback_actions(source_code: str, task_id: str) -> List[Dict[str, Any]]:
|
| 106 |
"""Deterministic fallback when LLM call fails or returns empty."""
|
| 107 |
lowered = source_code.lower()
|
| 108 |
+
|
| 109 |
if task_id == "task_1_best_practices":
|
| 110 |
return [
|
| 111 |
{
|
|
|
|
| 178 |
}
|
| 179 |
]
|
| 180 |
|
| 181 |
+
if task_id == "task_4_comprehensive_audit":
|
| 182 |
+
findings: List[Dict[str, Any]] = []
|
| 183 |
+
if "pragma solidity" in lowered:
|
| 184 |
+
findings.append(
|
| 185 |
+
{
|
| 186 |
+
"issue_type": "old_compiler_version",
|
| 187 |
+
"line_number": _find_line_number(source_code, "pragma solidity", 2),
|
| 188 |
+
"description": "Compiler pragma should use ^0.8.x",
|
| 189 |
+
"severity": "Low",
|
| 190 |
+
}
|
| 191 |
+
)
|
| 192 |
+
if "for" in lowered and ".length" in lowered:
|
| 193 |
+
findings.append(
|
| 194 |
+
{
|
| 195 |
+
"issue_type": "unbounded_loop",
|
| 196 |
+
"line_number": _find_line_number(source_code, "for", 10),
|
| 197 |
+
"description": "Loop uses dynamic array length without bounds",
|
| 198 |
+
"severity": "Medium",
|
| 199 |
+
}
|
| 200 |
+
)
|
| 201 |
+
if "tx.origin" in lowered:
|
| 202 |
+
findings.append(
|
| 203 |
+
{
|
| 204 |
+
"issue_type": "tx_origin_auth",
|
| 205 |
+
"line_number": _find_line_number(source_code, "tx.origin", 11),
|
| 206 |
+
"description": "Authorization uses tx.origin",
|
| 207 |
+
"severity": "Critical",
|
| 208 |
+
}
|
| 209 |
+
)
|
| 210 |
+
if not findings:
|
| 211 |
+
findings = [
|
| 212 |
+
{
|
| 213 |
+
"issue_type": "missing_spdx",
|
| 214 |
+
"line_number": 1,
|
| 215 |
+
"description": "Missing SPDX license identifier",
|
| 216 |
+
"severity": "Low",
|
| 217 |
+
}
|
| 218 |
+
]
|
| 219 |
+
return findings
|
| 220 |
+
|
| 221 |
# Default fallback
|
| 222 |
return [
|
| 223 |
{
|
|
|
|
| 246 |
stream=False,
|
| 247 |
)
|
| 248 |
content = (response.choices[0].message.content or "[]").strip()
|
| 249 |
+
|
| 250 |
# Handle markdown code blocks
|
| 251 |
if content.startswith("```"):
|
| 252 |
parts = content.split("```")
|
| 253 |
if len(parts) >= 2:
|
| 254 |
content = parts[1].replace("json", "", 1).strip()
|
| 255 |
+
|
| 256 |
parsed = json.loads(content)
|
| 257 |
if isinstance(parsed, list):
|
| 258 |
return parsed
|
|
|
|
| 269 |
steps_taken = 0
|
| 270 |
score = 0.0
|
| 271 |
success = False
|
| 272 |
+
task_list = [TASK_NAME] if TASK_NAME else DEFAULT_TASKS
|
| 273 |
|
| 274 |
+
log_start(task=",".join(task_list), env=BENCHMARK, model=MODEL_NAME)
|
| 275 |
|
| 276 |
try:
|
| 277 |
+
for task_id in task_list:
|
| 278 |
+
observation = env.reset(task_id=task_id)
|
| 279 |
+
|
| 280 |
+
for _ in range(MAX_STEPS):
|
| 281 |
+
steps_taken += 1
|
| 282 |
+
error: Optional[str] = None
|
| 283 |
+
action_text = "[]"
|
| 284 |
+
|
| 285 |
+
try:
|
| 286 |
+
prompt = _build_prompt(observation["source_code"], task_id)
|
| 287 |
+
actions = _call_model(client, prompt)
|
| 288 |
+
if not actions:
|
| 289 |
+
actions = _fallback_actions(observation["source_code"], task_id)
|
| 290 |
+
|
| 291 |
+
result = env.step(actions)
|
| 292 |
+
reward = _safe_score(float(result.get("reward", 0.0)))
|
| 293 |
+
done = result.get("done", True)
|
| 294 |
+
action_text = json.dumps(
|
| 295 |
+
actions, ensure_ascii=True, separators=(",", ":")
|
| 296 |
+
)
|
| 297 |
+
except Exception as exc:
|
| 298 |
+
error = str(exc)
|
| 299 |
+
reward = 0.01
|
| 300 |
+
done = True
|
| 301 |
+
|
| 302 |
+
rewards.append(reward)
|
| 303 |
+
log_step(
|
| 304 |
+
step=steps_taken,
|
| 305 |
+
action=action_text,
|
| 306 |
+
reward=reward,
|
| 307 |
+
done=done,
|
| 308 |
+
error=error,
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
if done:
|
| 312 |
+
break
|
|
|
|
|
|
|
| 313 |
|
| 314 |
# Calculate final score
|
| 315 |
score = _safe_score(sum(rewards) / max(len(rewards), 1))
|
openenv.yaml
CHANGED
|
@@ -9,27 +9,44 @@ tasks:
|
|
| 9 |
enabled: true
|
| 10 |
description: Detect syntax and best-practice issues in Solidity contracts.
|
| 11 |
max_steps: 1
|
| 12 |
-
grader: "graders:
|
| 13 |
graders:
|
| 14 |
-
-
|
|
|
|
|
|
|
| 15 |
- id: task_2_gas_optimization
|
| 16 |
name: Gas Optimization Audit
|
| 17 |
difficulty: medium
|
| 18 |
enabled: true
|
| 19 |
description: Detect gas optimization opportunities in Solidity contracts.
|
| 20 |
max_steps: 1
|
| 21 |
-
grader: "graders:
|
| 22 |
graders:
|
| 23 |
-
-
|
|
|
|
|
|
|
| 24 |
- id: task_3_security
|
| 25 |
name: Security Vulnerability Audit
|
| 26 |
difficulty: hard
|
| 27 |
enabled: true
|
| 28 |
description: Detect security vulnerabilities in Solidity contracts.
|
| 29 |
max_steps: 1
|
| 30 |
-
grader: "graders:
|
| 31 |
graders:
|
| 32 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
schemas:
|
| 34 |
observation:
|
| 35 |
type: object
|
|
|
|
| 9 |
enabled: true
|
| 10 |
description: Detect syntax and best-practice issues in Solidity contracts.
|
| 11 |
max_steps: 1
|
| 12 |
+
grader: "graders:grade_task_1"
|
| 13 |
graders:
|
| 14 |
+
- name: task_1_grader
|
| 15 |
+
enabled: true
|
| 16 |
+
entrypoint: "graders:grade_task_1"
|
| 17 |
- id: task_2_gas_optimization
|
| 18 |
name: Gas Optimization Audit
|
| 19 |
difficulty: medium
|
| 20 |
enabled: true
|
| 21 |
description: Detect gas optimization opportunities in Solidity contracts.
|
| 22 |
max_steps: 1
|
| 23 |
+
grader: "graders:grade_task_2"
|
| 24 |
graders:
|
| 25 |
+
- name: task_2_grader
|
| 26 |
+
enabled: true
|
| 27 |
+
entrypoint: "graders:grade_task_2"
|
| 28 |
- id: task_3_security
|
| 29 |
name: Security Vulnerability Audit
|
| 30 |
difficulty: hard
|
| 31 |
enabled: true
|
| 32 |
description: Detect security vulnerabilities in Solidity contracts.
|
| 33 |
max_steps: 1
|
| 34 |
+
grader: "graders:grade_task_3"
|
| 35 |
graders:
|
| 36 |
+
- name: task_3_grader
|
| 37 |
+
enabled: true
|
| 38 |
+
entrypoint: "graders:grade_task_3"
|
| 39 |
+
- id: task_4_comprehensive_audit
|
| 40 |
+
name: Comprehensive Audit
|
| 41 |
+
difficulty: hard
|
| 42 |
+
enabled: true
|
| 43 |
+
description: Perform a complete audit covering best practices, gas, and security.
|
| 44 |
+
max_steps: 1
|
| 45 |
+
grader: "graders:grade_task_4"
|
| 46 |
+
graders:
|
| 47 |
+
- name: task_4_grader
|
| 48 |
+
enabled: true
|
| 49 |
+
entrypoint: "graders:grade_task_4"
|
| 50 |
schemas:
|
| 51 |
observation:
|
| 52 |
type: object
|
server/app.py
CHANGED
|
@@ -464,6 +464,7 @@ def root() -> str:
|
|
| 464 |
</html>
|
| 465 |
"""
|
| 466 |
|
|
|
|
| 467 |
@app.get("/health")
|
| 468 |
def health() -> Dict[str, str]:
|
| 469 |
return {"status": "ok"}
|
|
@@ -500,14 +501,14 @@ def generate_report(request: ReportRequest) -> Dict[str, Any]:
|
|
| 500 |
try:
|
| 501 |
# Reset to get the contract
|
| 502 |
observation = env.reset(task_id=request.task_id)
|
| 503 |
-
|
| 504 |
# Get contract metadata
|
| 505 |
metadata = observation["metadata"]
|
| 506 |
source_code = observation["source_code"]
|
| 507 |
-
|
| 508 |
# Calculate risk metrics
|
| 509 |
risk_metrics = _calculate_risk_metrics(source_code, request.task_id)
|
| 510 |
-
|
| 511 |
# Generate summary
|
| 512 |
report = {
|
| 513 |
"contract_info": {
|
|
@@ -515,27 +516,31 @@ def generate_report(request: ReportRequest) -> Dict[str, Any]:
|
|
| 515 |
"compiler_version": metadata["compiler_version"],
|
| 516 |
"file_path": metadata["file_path"],
|
| 517 |
"task_category": request.task_id,
|
| 518 |
-
"lines_of_code": len(source_code.split(
|
| 519 |
},
|
| 520 |
"risk_assessment": risk_metrics,
|
| 521 |
"recommendations": _generate_recommendations(risk_metrics),
|
| 522 |
"timestamp": "2026-04-06T12:00:00Z", # Mock timestamp
|
| 523 |
-
"report_version": "2.0.0"
|
| 524 |
}
|
| 525 |
-
|
| 526 |
if request.include_fixes:
|
| 527 |
-
report["suggested_fixes"] = _get_fix_suggestions(
|
| 528 |
-
|
|
|
|
|
|
|
| 529 |
if request.include_exploits:
|
| 530 |
-
report["exploit_scenarios"] = _get_exploit_scenarios(
|
| 531 |
-
|
|
|
|
|
|
|
| 532 |
return report
|
| 533 |
-
|
| 534 |
except Exception as exc:
|
| 535 |
raise HTTPException(status_code=400, detail=str(exc))
|
| 536 |
|
| 537 |
|
| 538 |
-
@app.get("/dashboard")
|
| 539 |
def get_dashboard() -> Dict[str, Any]:
|
| 540 |
"""Get dashboard overview of all contract categories."""
|
| 541 |
try:
|
|
@@ -544,159 +549,210 @@ def get_dashboard() -> Dict[str, Any]:
|
|
| 544 |
"total_samples": 18,
|
| 545 |
"categories": 3,
|
| 546 |
"avg_risk_score": 0.65,
|
| 547 |
-
"last_updated": "2026-04-06T12:00:00Z"
|
| 548 |
},
|
| 549 |
"category_breakdown": {
|
| 550 |
"task_1_best_practices": {
|
| 551 |
"sample_count": 6,
|
| 552 |
"avg_severity": "Low",
|
| 553 |
-
"common_issues": [
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
},
|
| 555 |
"task_2_gas_optimization": {
|
| 556 |
-
"sample_count": 6,
|
| 557 |
"avg_severity": "Medium",
|
| 558 |
-
"common_issues": [
|
|
|
|
|
|
|
|
|
|
|
|
|
| 559 |
},
|
| 560 |
"task_3_security": {
|
| 561 |
"sample_count": 6,
|
| 562 |
-
"avg_severity": "Critical",
|
| 563 |
-
"common_issues": [
|
| 564 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 565 |
},
|
| 566 |
"agent_stats": {
|
| 567 |
"multi_agent_enabled": True,
|
| 568 |
"analyzer_accuracy": 0.85,
|
| 569 |
"verifier_precision": 0.90,
|
| 570 |
-
"risk_scorer_coverage": 0.95
|
| 571 |
-
}
|
| 572 |
}
|
| 573 |
-
|
| 574 |
return dashboard_data
|
| 575 |
-
|
| 576 |
except Exception as exc:
|
| 577 |
raise HTTPException(status_code=400, detail=str(exc))
|
| 578 |
|
| 579 |
|
| 580 |
def _calculate_risk_metrics(source_code: str, task_id: str) -> Dict[str, Any]:
|
| 581 |
"""Calculate comprehensive risk metrics for a contract."""
|
| 582 |
-
|
| 583 |
# Basic metrics
|
| 584 |
-
lines_of_code = len(source_code.split(
|
| 585 |
-
cyclomatic_complexity =
|
| 586 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 587 |
# Security indicators
|
| 588 |
-
has_external_calls =
|
| 589 |
-
has_state_variables =
|
| 590 |
-
has_payable =
|
| 591 |
-
|
| 592 |
# Calculate overall risk score
|
| 593 |
base_risk = 0.3
|
| 594 |
if task_id == "task_3_security":
|
| 595 |
base_risk = 0.8
|
| 596 |
-
elif task_id == "
|
|
|
|
|
|
|
| 597 |
base_risk = 0.5
|
| 598 |
-
|
| 599 |
complexity_factor = min(cyclomatic_complexity * 0.1, 0.3)
|
| 600 |
external_call_factor = 0.2 if has_external_calls else 0.0
|
| 601 |
payable_factor = 0.1 if has_payable else 0.0
|
| 602 |
-
|
| 603 |
-
overall_risk = min(
|
| 604 |
-
|
|
|
|
|
|
|
| 605 |
return {
|
| 606 |
"overall_risk_score": round(overall_risk, 3),
|
| 607 |
-
"risk_category": "High"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 608 |
"complexity_score": cyclomatic_complexity,
|
| 609 |
"lines_of_code": lines_of_code,
|
| 610 |
"has_external_calls": has_external_calls,
|
| 611 |
"has_state_variables": has_state_variables,
|
| 612 |
"has_payable_functions": has_payable,
|
| 613 |
-
"recommended_review_time": f"{max(15, lines_of_code * 2)} minutes"
|
| 614 |
}
|
| 615 |
|
| 616 |
|
| 617 |
def _generate_recommendations(risk_metrics: Dict[str, Any]) -> List[str]:
|
| 618 |
"""Generate audit recommendations based on risk metrics."""
|
| 619 |
-
|
| 620 |
recommendations = []
|
| 621 |
-
|
| 622 |
if risk_metrics["overall_risk_score"] >= 0.7:
|
| 623 |
recommendations.append("🔴 HIGH RISK: Requires immediate security review")
|
| 624 |
recommendations.append("Consider formal verification for critical functions")
|
| 625 |
-
|
| 626 |
if risk_metrics["has_external_calls"]:
|
| 627 |
-
recommendations.append(
|
| 628 |
-
|
|
|
|
|
|
|
| 629 |
if risk_metrics["has_payable_functions"]:
|
| 630 |
-
recommendations.append(
|
| 631 |
-
|
|
|
|
|
|
|
| 632 |
if risk_metrics["complexity_score"] > 10:
|
| 633 |
-
recommendations.append(
|
| 634 |
-
|
|
|
|
|
|
|
| 635 |
if risk_metrics["lines_of_code"] > 100:
|
| 636 |
recommendations.append("📏 Large contract: Consider modularization")
|
| 637 |
-
|
| 638 |
recommendations.append("✅ Run static analysis tools (Slither, Mythril)")
|
| 639 |
recommendations.append("🧪 Implement comprehensive test coverage")
|
| 640 |
-
|
| 641 |
return recommendations
|
| 642 |
|
| 643 |
|
| 644 |
def _get_fix_suggestions(source_code: str, task_id: str) -> List[Dict[str, str]]:
|
| 645 |
"""Get specific fix suggestions for common issues."""
|
| 646 |
-
|
| 647 |
suggestions = []
|
| 648 |
-
|
| 649 |
if not source_code.strip().startswith("// SPDX"):
|
| 650 |
-
suggestions.append(
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
|
|
|
|
|
|
| 656 |
if "0.4." in source_code or "0.7." in source_code:
|
| 657 |
-
suggestions.append(
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
|
|
|
|
|
|
| 663 |
if "tx.origin" in source_code:
|
| 664 |
-
suggestions.append(
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
|
|
|
|
|
|
| 670 |
return suggestions
|
| 671 |
|
| 672 |
|
| 673 |
def _get_exploit_scenarios(source_code: str, task_id: str) -> List[Dict[str, str]]:
|
| 674 |
"""Get potential exploit scenarios for security issues."""
|
| 675 |
-
|
| 676 |
scenarios = []
|
| 677 |
-
|
| 678 |
if "call{" in source_code and "balances[" in source_code:
|
| 679 |
-
scenarios.append(
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
|
|
|
|
|
|
| 686 |
if "tx.origin" in source_code:
|
| 687 |
-
scenarios.append(
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
|
| 693 |
-
|
|
|
|
|
|
|
| 694 |
return scenarios
|
| 695 |
|
|
|
|
| 696 |
def main():
|
| 697 |
import uvicorn
|
|
|
|
| 698 |
uvicorn.run("server.app:app", host="0.0.0.0", port=7860)
|
| 699 |
|
|
|
|
| 700 |
if __name__ == "__main__":
|
| 701 |
main()
|
| 702 |
-
|
|
|
|
| 464 |
</html>
|
| 465 |
"""
|
| 466 |
|
| 467 |
+
|
| 468 |
@app.get("/health")
|
| 469 |
def health() -> Dict[str, str]:
|
| 470 |
return {"status": "ok"}
|
|
|
|
| 501 |
try:
|
| 502 |
# Reset to get the contract
|
| 503 |
observation = env.reset(task_id=request.task_id)
|
| 504 |
+
|
| 505 |
# Get contract metadata
|
| 506 |
metadata = observation["metadata"]
|
| 507 |
source_code = observation["source_code"]
|
| 508 |
+
|
| 509 |
# Calculate risk metrics
|
| 510 |
risk_metrics = _calculate_risk_metrics(source_code, request.task_id)
|
| 511 |
+
|
| 512 |
# Generate summary
|
| 513 |
report = {
|
| 514 |
"contract_info": {
|
|
|
|
| 516 |
"compiler_version": metadata["compiler_version"],
|
| 517 |
"file_path": metadata["file_path"],
|
| 518 |
"task_category": request.task_id,
|
| 519 |
+
"lines_of_code": len(source_code.split("\n")),
|
| 520 |
},
|
| 521 |
"risk_assessment": risk_metrics,
|
| 522 |
"recommendations": _generate_recommendations(risk_metrics),
|
| 523 |
"timestamp": "2026-04-06T12:00:00Z", # Mock timestamp
|
| 524 |
+
"report_version": "2.0.0",
|
| 525 |
}
|
| 526 |
+
|
| 527 |
if request.include_fixes:
|
| 528 |
+
report["suggested_fixes"] = _get_fix_suggestions(
|
| 529 |
+
source_code, request.task_id
|
| 530 |
+
)
|
| 531 |
+
|
| 532 |
if request.include_exploits:
|
| 533 |
+
report["exploit_scenarios"] = _get_exploit_scenarios(
|
| 534 |
+
source_code, request.task_id
|
| 535 |
+
)
|
| 536 |
+
|
| 537 |
return report
|
| 538 |
+
|
| 539 |
except Exception as exc:
|
| 540 |
raise HTTPException(status_code=400, detail=str(exc))
|
| 541 |
|
| 542 |
|
| 543 |
+
@app.get("/dashboard")
|
| 544 |
def get_dashboard() -> Dict[str, Any]:
|
| 545 |
"""Get dashboard overview of all contract categories."""
|
| 546 |
try:
|
|
|
|
| 549 |
"total_samples": 18,
|
| 550 |
"categories": 3,
|
| 551 |
"avg_risk_score": 0.65,
|
| 552 |
+
"last_updated": "2026-04-06T12:00:00Z",
|
| 553 |
},
|
| 554 |
"category_breakdown": {
|
| 555 |
"task_1_best_practices": {
|
| 556 |
"sample_count": 6,
|
| 557 |
"avg_severity": "Low",
|
| 558 |
+
"common_issues": [
|
| 559 |
+
"missing_spdx",
|
| 560 |
+
"old_compiler_version",
|
| 561 |
+
"missing_natspec",
|
| 562 |
+
],
|
| 563 |
},
|
| 564 |
"task_2_gas_optimization": {
|
| 565 |
+
"sample_count": 6,
|
| 566 |
"avg_severity": "Medium",
|
| 567 |
+
"common_issues": [
|
| 568 |
+
"unbounded_loop",
|
| 569 |
+
"redundant_storage_read",
|
| 570 |
+
"poor_struct_packing",
|
| 571 |
+
],
|
| 572 |
},
|
| 573 |
"task_3_security": {
|
| 574 |
"sample_count": 6,
|
| 575 |
+
"avg_severity": "Critical",
|
| 576 |
+
"common_issues": [
|
| 577 |
+
"reentrancy",
|
| 578 |
+
"missing_access_control",
|
| 579 |
+
"tx_origin_auth",
|
| 580 |
+
],
|
| 581 |
+
},
|
| 582 |
+
"task_4_comprehensive_audit": {
|
| 583 |
+
"sample_count": 3,
|
| 584 |
+
"avg_severity": "Critical",
|
| 585 |
+
"common_issues": [
|
| 586 |
+
"old_compiler_version",
|
| 587 |
+
"unbounded_loop",
|
| 588 |
+
"tx_origin_auth",
|
| 589 |
+
],
|
| 590 |
+
},
|
| 591 |
},
|
| 592 |
"agent_stats": {
|
| 593 |
"multi_agent_enabled": True,
|
| 594 |
"analyzer_accuracy": 0.85,
|
| 595 |
"verifier_precision": 0.90,
|
| 596 |
+
"risk_scorer_coverage": 0.95,
|
| 597 |
+
},
|
| 598 |
}
|
| 599 |
+
|
| 600 |
return dashboard_data
|
| 601 |
+
|
| 602 |
except Exception as exc:
|
| 603 |
raise HTTPException(status_code=400, detail=str(exc))
|
| 604 |
|
| 605 |
|
| 606 |
def _calculate_risk_metrics(source_code: str, task_id: str) -> Dict[str, Any]:
|
| 607 |
"""Calculate comprehensive risk metrics for a contract."""
|
| 608 |
+
|
| 609 |
# Basic metrics
|
| 610 |
+
lines_of_code = len(source_code.split("\n"))
|
| 611 |
+
cyclomatic_complexity = (
|
| 612 |
+
source_code.count("if ")
|
| 613 |
+
+ source_code.count("for ")
|
| 614 |
+
+ source_code.count("while ")
|
| 615 |
+
)
|
| 616 |
+
|
| 617 |
# Security indicators
|
| 618 |
+
has_external_calls = "call{" in source_code or ".call(" in source_code
|
| 619 |
+
has_state_variables = "mapping(" in source_code or "uint" in source_code
|
| 620 |
+
has_payable = "payable" in source_code
|
| 621 |
+
|
| 622 |
# Calculate overall risk score
|
| 623 |
base_risk = 0.3
|
| 624 |
if task_id == "task_3_security":
|
| 625 |
base_risk = 0.8
|
| 626 |
+
elif task_id == "task_4_comprehensive_audit":
|
| 627 |
+
base_risk = 0.85
|
| 628 |
+
elif task_id == "task_2_gas_optimization":
|
| 629 |
base_risk = 0.5
|
| 630 |
+
|
| 631 |
complexity_factor = min(cyclomatic_complexity * 0.1, 0.3)
|
| 632 |
external_call_factor = 0.2 if has_external_calls else 0.0
|
| 633 |
payable_factor = 0.1 if has_payable else 0.0
|
| 634 |
+
|
| 635 |
+
overall_risk = min(
|
| 636 |
+
base_risk + complexity_factor + external_call_factor + payable_factor, 1.0
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
return {
|
| 640 |
"overall_risk_score": round(overall_risk, 3),
|
| 641 |
+
"risk_category": "High"
|
| 642 |
+
if overall_risk >= 0.7
|
| 643 |
+
else "Medium"
|
| 644 |
+
if overall_risk >= 0.4
|
| 645 |
+
else "Low",
|
| 646 |
"complexity_score": cyclomatic_complexity,
|
| 647 |
"lines_of_code": lines_of_code,
|
| 648 |
"has_external_calls": has_external_calls,
|
| 649 |
"has_state_variables": has_state_variables,
|
| 650 |
"has_payable_functions": has_payable,
|
| 651 |
+
"recommended_review_time": f"{max(15, lines_of_code * 2)} minutes",
|
| 652 |
}
|
| 653 |
|
| 654 |
|
| 655 |
def _generate_recommendations(risk_metrics: Dict[str, Any]) -> List[str]:
|
| 656 |
"""Generate audit recommendations based on risk metrics."""
|
| 657 |
+
|
| 658 |
recommendations = []
|
| 659 |
+
|
| 660 |
if risk_metrics["overall_risk_score"] >= 0.7:
|
| 661 |
recommendations.append("🔴 HIGH RISK: Requires immediate security review")
|
| 662 |
recommendations.append("Consider formal verification for critical functions")
|
| 663 |
+
|
| 664 |
if risk_metrics["has_external_calls"]:
|
| 665 |
+
recommendations.append(
|
| 666 |
+
"⚠️ External calls detected: Review for reentrancy vulnerabilities"
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
if risk_metrics["has_payable_functions"]:
|
| 670 |
+
recommendations.append(
|
| 671 |
+
"💰 Payable functions detected: Ensure proper access controls"
|
| 672 |
+
)
|
| 673 |
+
|
| 674 |
if risk_metrics["complexity_score"] > 10:
|
| 675 |
+
recommendations.append(
|
| 676 |
+
"🧩 High complexity: Consider breaking into smaller functions"
|
| 677 |
+
)
|
| 678 |
+
|
| 679 |
if risk_metrics["lines_of_code"] > 100:
|
| 680 |
recommendations.append("📏 Large contract: Consider modularization")
|
| 681 |
+
|
| 682 |
recommendations.append("✅ Run static analysis tools (Slither, Mythril)")
|
| 683 |
recommendations.append("🧪 Implement comprehensive test coverage")
|
| 684 |
+
|
| 685 |
return recommendations
|
| 686 |
|
| 687 |
|
| 688 |
def _get_fix_suggestions(source_code: str, task_id: str) -> List[Dict[str, str]]:
|
| 689 |
"""Get specific fix suggestions for common issues."""
|
| 690 |
+
|
| 691 |
suggestions = []
|
| 692 |
+
|
| 693 |
if not source_code.strip().startswith("// SPDX"):
|
| 694 |
+
suggestions.append(
|
| 695 |
+
{
|
| 696 |
+
"issue": "Missing SPDX License",
|
| 697 |
+
"fix": "Add '// SPDX-License-Identifier: MIT' at the top of the file",
|
| 698 |
+
"priority": "Low",
|
| 699 |
+
}
|
| 700 |
+
)
|
| 701 |
+
|
| 702 |
if "0.4." in source_code or "0.7." in source_code:
|
| 703 |
+
suggestions.append(
|
| 704 |
+
{
|
| 705 |
+
"issue": "Outdated Solidity Version",
|
| 706 |
+
"fix": "Update to 'pragma solidity ^0.8.0;' for better security",
|
| 707 |
+
"priority": "Medium",
|
| 708 |
+
}
|
| 709 |
+
)
|
| 710 |
+
|
| 711 |
if "tx.origin" in source_code:
|
| 712 |
+
suggestions.append(
|
| 713 |
+
{
|
| 714 |
+
"issue": "tx.origin Usage",
|
| 715 |
+
"fix": "Replace 'tx.origin' with 'msg.sender' for proper authentication",
|
| 716 |
+
"priority": "High",
|
| 717 |
+
}
|
| 718 |
+
)
|
| 719 |
+
|
| 720 |
return suggestions
|
| 721 |
|
| 722 |
|
| 723 |
def _get_exploit_scenarios(source_code: str, task_id: str) -> List[Dict[str, str]]:
|
| 724 |
"""Get potential exploit scenarios for security issues."""
|
| 725 |
+
|
| 726 |
scenarios = []
|
| 727 |
+
|
| 728 |
if "call{" in source_code and "balances[" in source_code:
|
| 729 |
+
scenarios.append(
|
| 730 |
+
{
|
| 731 |
+
"vulnerability": "Reentrancy Attack",
|
| 732 |
+
"scenario": "Attacker creates malicious contract with fallback function that calls withdraw() recursively",
|
| 733 |
+
"impact": "Complete drainage of contract funds",
|
| 734 |
+
"mitigation": "Implement checks-effects-interactions pattern or ReentrancyGuard",
|
| 735 |
+
}
|
| 736 |
+
)
|
| 737 |
+
|
| 738 |
if "tx.origin" in source_code:
|
| 739 |
+
scenarios.append(
|
| 740 |
+
{
|
| 741 |
+
"vulnerability": "tx.origin Phishing",
|
| 742 |
+
"scenario": "Attacker tricks user into calling malicious contract that forwards transactions",
|
| 743 |
+
"impact": "Unauthorized access to protected functions",
|
| 744 |
+
"mitigation": "Use msg.sender instead of tx.origin for authentication",
|
| 745 |
+
}
|
| 746 |
+
)
|
| 747 |
+
|
| 748 |
return scenarios
|
| 749 |
|
| 750 |
+
|
| 751 |
def main():
|
| 752 |
import uvicorn
|
| 753 |
+
|
| 754 |
uvicorn.run("server.app:app", host="0.0.0.0", port=7860)
|
| 755 |
|
| 756 |
+
|
| 757 |
if __name__ == "__main__":
|
| 758 |
main()
|
|
|