aiptx 2.0.7__py3-none-any.whl

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 (187) hide show
  1. aipt_v2/__init__.py +110 -0
  2. aipt_v2/__main__.py +24 -0
  3. aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
  4. aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
  5. aipt_v2/agents/__init__.py +46 -0
  6. aipt_v2/agents/base.py +520 -0
  7. aipt_v2/agents/exploit_agent.py +688 -0
  8. aipt_v2/agents/ptt.py +406 -0
  9. aipt_v2/agents/state.py +168 -0
  10. aipt_v2/app.py +957 -0
  11. aipt_v2/browser/__init__.py +31 -0
  12. aipt_v2/browser/automation.py +458 -0
  13. aipt_v2/browser/crawler.py +453 -0
  14. aipt_v2/cli.py +2933 -0
  15. aipt_v2/compliance/__init__.py +71 -0
  16. aipt_v2/compliance/compliance_report.py +449 -0
  17. aipt_v2/compliance/framework_mapper.py +424 -0
  18. aipt_v2/compliance/nist_mapping.py +345 -0
  19. aipt_v2/compliance/owasp_mapping.py +330 -0
  20. aipt_v2/compliance/pci_mapping.py +297 -0
  21. aipt_v2/config.py +341 -0
  22. aipt_v2/core/__init__.py +43 -0
  23. aipt_v2/core/agent.py +630 -0
  24. aipt_v2/core/llm.py +395 -0
  25. aipt_v2/core/memory.py +305 -0
  26. aipt_v2/core/ptt.py +329 -0
  27. aipt_v2/database/__init__.py +14 -0
  28. aipt_v2/database/models.py +232 -0
  29. aipt_v2/database/repository.py +384 -0
  30. aipt_v2/docker/__init__.py +23 -0
  31. aipt_v2/docker/builder.py +260 -0
  32. aipt_v2/docker/manager.py +222 -0
  33. aipt_v2/docker/sandbox.py +371 -0
  34. aipt_v2/evasion/__init__.py +58 -0
  35. aipt_v2/evasion/request_obfuscator.py +272 -0
  36. aipt_v2/evasion/tls_fingerprint.py +285 -0
  37. aipt_v2/evasion/ua_rotator.py +301 -0
  38. aipt_v2/evasion/waf_bypass.py +439 -0
  39. aipt_v2/execution/__init__.py +23 -0
  40. aipt_v2/execution/executor.py +302 -0
  41. aipt_v2/execution/parser.py +544 -0
  42. aipt_v2/execution/terminal.py +337 -0
  43. aipt_v2/health.py +437 -0
  44. aipt_v2/intelligence/__init__.py +194 -0
  45. aipt_v2/intelligence/adaptation.py +474 -0
  46. aipt_v2/intelligence/auth.py +520 -0
  47. aipt_v2/intelligence/chaining.py +775 -0
  48. aipt_v2/intelligence/correlation.py +536 -0
  49. aipt_v2/intelligence/cve_aipt.py +334 -0
  50. aipt_v2/intelligence/cve_info.py +1111 -0
  51. aipt_v2/intelligence/knowledge_graph.py +590 -0
  52. aipt_v2/intelligence/learning.py +626 -0
  53. aipt_v2/intelligence/llm_analyzer.py +502 -0
  54. aipt_v2/intelligence/llm_tool_selector.py +518 -0
  55. aipt_v2/intelligence/payload_generator.py +562 -0
  56. aipt_v2/intelligence/rag.py +239 -0
  57. aipt_v2/intelligence/scope.py +442 -0
  58. aipt_v2/intelligence/searchers/__init__.py +5 -0
  59. aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
  60. aipt_v2/intelligence/searchers/github_searcher.py +467 -0
  61. aipt_v2/intelligence/searchers/google_searcher.py +281 -0
  62. aipt_v2/intelligence/tools.json +443 -0
  63. aipt_v2/intelligence/triage.py +670 -0
  64. aipt_v2/interactive_shell.py +559 -0
  65. aipt_v2/interface/__init__.py +5 -0
  66. aipt_v2/interface/cli.py +230 -0
  67. aipt_v2/interface/main.py +501 -0
  68. aipt_v2/interface/tui.py +1276 -0
  69. aipt_v2/interface/utils.py +583 -0
  70. aipt_v2/llm/__init__.py +39 -0
  71. aipt_v2/llm/config.py +26 -0
  72. aipt_v2/llm/llm.py +514 -0
  73. aipt_v2/llm/memory.py +214 -0
  74. aipt_v2/llm/request_queue.py +89 -0
  75. aipt_v2/llm/utils.py +89 -0
  76. aipt_v2/local_tool_installer.py +1467 -0
  77. aipt_v2/models/__init__.py +15 -0
  78. aipt_v2/models/findings.py +295 -0
  79. aipt_v2/models/phase_result.py +224 -0
  80. aipt_v2/models/scan_config.py +207 -0
  81. aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
  82. aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
  83. aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
  84. aipt_v2/monitoring/prometheus.yml +60 -0
  85. aipt_v2/orchestration/__init__.py +52 -0
  86. aipt_v2/orchestration/pipeline.py +398 -0
  87. aipt_v2/orchestration/progress.py +300 -0
  88. aipt_v2/orchestration/scheduler.py +296 -0
  89. aipt_v2/orchestrator.py +2427 -0
  90. aipt_v2/payloads/__init__.py +27 -0
  91. aipt_v2/payloads/cmdi.py +150 -0
  92. aipt_v2/payloads/sqli.py +263 -0
  93. aipt_v2/payloads/ssrf.py +204 -0
  94. aipt_v2/payloads/templates.py +222 -0
  95. aipt_v2/payloads/traversal.py +166 -0
  96. aipt_v2/payloads/xss.py +204 -0
  97. aipt_v2/prompts/__init__.py +60 -0
  98. aipt_v2/proxy/__init__.py +29 -0
  99. aipt_v2/proxy/history.py +352 -0
  100. aipt_v2/proxy/interceptor.py +452 -0
  101. aipt_v2/recon/__init__.py +44 -0
  102. aipt_v2/recon/dns.py +241 -0
  103. aipt_v2/recon/osint.py +367 -0
  104. aipt_v2/recon/subdomain.py +372 -0
  105. aipt_v2/recon/tech_detect.py +311 -0
  106. aipt_v2/reports/__init__.py +17 -0
  107. aipt_v2/reports/generator.py +313 -0
  108. aipt_v2/reports/html_report.py +378 -0
  109. aipt_v2/runtime/__init__.py +53 -0
  110. aipt_v2/runtime/base.py +30 -0
  111. aipt_v2/runtime/docker.py +401 -0
  112. aipt_v2/runtime/local.py +346 -0
  113. aipt_v2/runtime/tool_server.py +205 -0
  114. aipt_v2/runtime/vps.py +830 -0
  115. aipt_v2/scanners/__init__.py +28 -0
  116. aipt_v2/scanners/base.py +273 -0
  117. aipt_v2/scanners/nikto.py +244 -0
  118. aipt_v2/scanners/nmap.py +402 -0
  119. aipt_v2/scanners/nuclei.py +273 -0
  120. aipt_v2/scanners/web.py +454 -0
  121. aipt_v2/scripts/security_audit.py +366 -0
  122. aipt_v2/setup_wizard.py +941 -0
  123. aipt_v2/skills/__init__.py +80 -0
  124. aipt_v2/skills/agents/__init__.py +14 -0
  125. aipt_v2/skills/agents/api_tester.py +706 -0
  126. aipt_v2/skills/agents/base.py +477 -0
  127. aipt_v2/skills/agents/code_review.py +459 -0
  128. aipt_v2/skills/agents/security_agent.py +336 -0
  129. aipt_v2/skills/agents/web_pentest.py +818 -0
  130. aipt_v2/skills/prompts/__init__.py +647 -0
  131. aipt_v2/system_detector.py +539 -0
  132. aipt_v2/telemetry/__init__.py +7 -0
  133. aipt_v2/telemetry/tracer.py +347 -0
  134. aipt_v2/terminal/__init__.py +28 -0
  135. aipt_v2/terminal/executor.py +400 -0
  136. aipt_v2/terminal/sandbox.py +350 -0
  137. aipt_v2/tools/__init__.py +44 -0
  138. aipt_v2/tools/active_directory/__init__.py +78 -0
  139. aipt_v2/tools/active_directory/ad_config.py +238 -0
  140. aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
  141. aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
  142. aipt_v2/tools/active_directory/ldap_enum.py +533 -0
  143. aipt_v2/tools/active_directory/smb_attacks.py +505 -0
  144. aipt_v2/tools/agents_graph/__init__.py +19 -0
  145. aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
  146. aipt_v2/tools/api_security/__init__.py +76 -0
  147. aipt_v2/tools/api_security/api_discovery.py +608 -0
  148. aipt_v2/tools/api_security/graphql_scanner.py +622 -0
  149. aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
  150. aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
  151. aipt_v2/tools/browser/__init__.py +5 -0
  152. aipt_v2/tools/browser/browser_actions.py +238 -0
  153. aipt_v2/tools/browser/browser_instance.py +535 -0
  154. aipt_v2/tools/browser/tab_manager.py +344 -0
  155. aipt_v2/tools/cloud/__init__.py +70 -0
  156. aipt_v2/tools/cloud/cloud_config.py +273 -0
  157. aipt_v2/tools/cloud/cloud_scanner.py +639 -0
  158. aipt_v2/tools/cloud/prowler_tool.py +571 -0
  159. aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
  160. aipt_v2/tools/executor.py +307 -0
  161. aipt_v2/tools/parser.py +408 -0
  162. aipt_v2/tools/proxy/__init__.py +5 -0
  163. aipt_v2/tools/proxy/proxy_actions.py +103 -0
  164. aipt_v2/tools/proxy/proxy_manager.py +789 -0
  165. aipt_v2/tools/registry.py +196 -0
  166. aipt_v2/tools/scanners/__init__.py +343 -0
  167. aipt_v2/tools/scanners/acunetix_tool.py +712 -0
  168. aipt_v2/tools/scanners/burp_tool.py +631 -0
  169. aipt_v2/tools/scanners/config.py +156 -0
  170. aipt_v2/tools/scanners/nessus_tool.py +588 -0
  171. aipt_v2/tools/scanners/zap_tool.py +612 -0
  172. aipt_v2/tools/terminal/__init__.py +5 -0
  173. aipt_v2/tools/terminal/terminal_actions.py +37 -0
  174. aipt_v2/tools/terminal/terminal_manager.py +153 -0
  175. aipt_v2/tools/terminal/terminal_session.py +449 -0
  176. aipt_v2/tools/tool_processing.py +108 -0
  177. aipt_v2/utils/__init__.py +17 -0
  178. aipt_v2/utils/logging.py +202 -0
  179. aipt_v2/utils/model_manager.py +187 -0
  180. aipt_v2/utils/searchers/__init__.py +269 -0
  181. aipt_v2/verify_install.py +793 -0
  182. aiptx-2.0.7.dist-info/METADATA +345 -0
  183. aiptx-2.0.7.dist-info/RECORD +187 -0
  184. aiptx-2.0.7.dist-info/WHEEL +5 -0
  185. aiptx-2.0.7.dist-info/entry_points.txt +7 -0
  186. aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
  187. aiptx-2.0.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,706 @@
1
+ """
2
+ API Security Testing Agent - AI-powered REST API security assessment.
3
+
4
+ Tests APIs for:
5
+ - Injection vulnerabilities (SQLi, NoSQLi, Command Injection)
6
+ - Broken authentication and authorization (BOLA, BFLA)
7
+ - Data exposure
8
+ - Rate limiting bypass
9
+ - Mass assignment
10
+ - SSRF
11
+ """
12
+
13
+ import json
14
+ import time
15
+ from typing import Any, Dict, List, Optional
16
+ from urllib.parse import urljoin, urlparse
17
+
18
+ import structlog
19
+
20
+ from aipt_v2.skills.agents.base import (
21
+ AgentConfig,
22
+ AgentResult,
23
+ BaseSecurityAgent,
24
+ Finding,
25
+ Severity,
26
+ VulnCategory,
27
+ register_tool,
28
+ )
29
+ from aipt_v2.skills.prompts import SkillPrompts
30
+
31
+ logger = structlog.get_logger()
32
+
33
+ # HTTP client for API testing
34
+ _http_client = None
35
+
36
+
37
+ def get_http_client():
38
+ """Get or create HTTP client."""
39
+ global _http_client
40
+ if _http_client is None:
41
+ import httpx
42
+ _http_client = httpx.AsyncClient(
43
+ timeout=30.0,
44
+ follow_redirects=True,
45
+ verify=False # Allow self-signed certs for testing
46
+ )
47
+ return _http_client
48
+
49
+
50
+ # Register API testing tools
51
+ @register_tool(
52
+ name="http_request",
53
+ description="Send an HTTP request to test an API endpoint",
54
+ parameters={
55
+ "method": {"type": "string", "description": "HTTP method (GET, POST, PUT, DELETE, PATCH)"},
56
+ "url": {"type": "string", "description": "Full URL to request"},
57
+ "headers": {"type": "object", "description": "Optional headers dict"},
58
+ "body": {"type": "string", "description": "Optional request body (JSON string)"},
59
+ "params": {"type": "object", "description": "Optional query parameters"}
60
+ },
61
+ category="api_test"
62
+ )
63
+ async def http_request(
64
+ method: str,
65
+ url: str,
66
+ headers: Optional[Dict[str, str]] = None,
67
+ body: Optional[str] = None,
68
+ params: Optional[Dict[str, str]] = None
69
+ ) -> str:
70
+ """Send an HTTP request."""
71
+ try:
72
+ client = get_http_client()
73
+
74
+ # Parse body if JSON string
75
+ json_body = None
76
+ if body:
77
+ try:
78
+ json_body = json.loads(body)
79
+ except json.JSONDecodeError:
80
+ pass
81
+
82
+ response = await client.request(
83
+ method=method.upper(),
84
+ url=url,
85
+ headers=headers,
86
+ json=json_body if json_body else None,
87
+ content=body if body and not json_body else None,
88
+ params=params
89
+ )
90
+
91
+ # Build response summary
92
+ result = f"""HTTP {method.upper()} {url}
93
+ Status: {response.status_code}
94
+ Headers: {dict(response.headers)}
95
+
96
+ Response Body:
97
+ {response.text[:5000]}"""
98
+
99
+ return result
100
+
101
+ except Exception as e:
102
+ return f"Request failed: {str(e)}"
103
+
104
+
105
+ @register_tool(
106
+ name="parse_openapi",
107
+ description="Parse an OpenAPI/Swagger specification to discover endpoints",
108
+ parameters={
109
+ "spec_url_or_content": {"type": "string", "description": "URL to OpenAPI spec or the spec content itself"}
110
+ },
111
+ category="api_test"
112
+ )
113
+ async def parse_openapi(spec_url_or_content: str) -> str:
114
+ """Parse OpenAPI specification."""
115
+ try:
116
+ import yaml
117
+
118
+ # Try to fetch if URL
119
+ if spec_url_or_content.startswith(('http://', 'https://')):
120
+ client = get_http_client()
121
+ response = await client.get(spec_url_or_content)
122
+ content = response.text
123
+ else:
124
+ content = spec_url_or_content
125
+
126
+ # Parse YAML or JSON
127
+ try:
128
+ spec = yaml.safe_load(content)
129
+ except yaml.YAMLError:
130
+ spec = json.loads(content)
131
+
132
+ # Extract endpoints
133
+ endpoints = []
134
+ paths = spec.get('paths', {})
135
+
136
+ for path, methods in paths.items():
137
+ for method, details in methods.items():
138
+ if method in ['get', 'post', 'put', 'delete', 'patch']:
139
+ params = []
140
+ for param in details.get('parameters', []):
141
+ params.append(f"{param.get('name')} ({param.get('in', 'query')})")
142
+
143
+ endpoint = {
144
+ 'path': path,
145
+ 'method': method.upper(),
146
+ 'summary': details.get('summary', ''),
147
+ 'parameters': params,
148
+ 'security': details.get('security', [])
149
+ }
150
+ endpoints.append(endpoint)
151
+
152
+ # Format output
153
+ output = f"OpenAPI Spec: {spec.get('info', {}).get('title', 'Unknown')}\n"
154
+ output += f"Version: {spec.get('info', {}).get('version', 'Unknown')}\n"
155
+ output += f"Base URL: {spec.get('servers', [{}])[0].get('url', 'Not specified')}\n\n"
156
+ output += f"Endpoints ({len(endpoints)}):\n"
157
+
158
+ for ep in endpoints:
159
+ output += f"\n{ep['method']} {ep['path']}"
160
+ if ep['summary']:
161
+ output += f" - {ep['summary']}"
162
+ if ep['parameters']:
163
+ output += f"\n Parameters: {', '.join(ep['parameters'])}"
164
+ if ep['security']:
165
+ output += f"\n Security: {ep['security']}"
166
+
167
+ return output
168
+
169
+ except Exception as e:
170
+ return f"Failed to parse OpenAPI spec: {str(e)}"
171
+
172
+
173
+ @register_tool(
174
+ name="fuzz_parameter",
175
+ description="Fuzz a parameter with various payloads",
176
+ parameters={
177
+ "base_url": {"type": "string", "description": "Base URL of the endpoint"},
178
+ "method": {"type": "string", "description": "HTTP method"},
179
+ "param_name": {"type": "string", "description": "Parameter name to fuzz"},
180
+ "param_location": {"type": "string", "description": "Location: query, body, header, path"},
181
+ "payloads": {"type": "array", "description": "List of payloads to test"},
182
+ "headers": {"type": "object", "description": "Optional headers"}
183
+ },
184
+ category="api_test"
185
+ )
186
+ async def fuzz_parameter(
187
+ base_url: str,
188
+ method: str,
189
+ param_name: str,
190
+ param_location: str,
191
+ payloads: List[str],
192
+ headers: Optional[Dict[str, str]] = None
193
+ ) -> str:
194
+ """Fuzz a parameter with various payloads."""
195
+ try:
196
+ client = get_http_client()
197
+ results = []
198
+
199
+ for payload in payloads[:20]: # Limit to 20 payloads
200
+ try:
201
+ url = base_url
202
+ req_headers = headers.copy() if headers else {}
203
+ body = None
204
+ params = None
205
+
206
+ if param_location == "query":
207
+ params = {param_name: payload}
208
+ elif param_location == "body":
209
+ body = json.dumps({param_name: payload})
210
+ req_headers["Content-Type"] = "application/json"
211
+ elif param_location == "header":
212
+ req_headers[param_name] = payload
213
+ elif param_location == "path":
214
+ url = base_url.replace(f"{{{param_name}}}", payload)
215
+
216
+ response = await client.request(
217
+ method=method.upper(),
218
+ url=url,
219
+ headers=req_headers,
220
+ content=body,
221
+ params=params
222
+ )
223
+
224
+ # Check for interesting responses
225
+ interesting = False
226
+ indicators = []
227
+
228
+ # Error indicators
229
+ if response.status_code >= 500:
230
+ interesting = True
231
+ indicators.append("Server Error")
232
+ if "error" in response.text.lower():
233
+ interesting = True
234
+ indicators.append("Error in response")
235
+ if "exception" in response.text.lower():
236
+ interesting = True
237
+ indicators.append("Exception disclosed")
238
+ if "sql" in response.text.lower():
239
+ interesting = True
240
+ indicators.append("SQL-related")
241
+
242
+ results.append({
243
+ "payload": payload,
244
+ "status": response.status_code,
245
+ "length": len(response.text),
246
+ "interesting": interesting,
247
+ "indicators": indicators,
248
+ "response_preview": response.text[:200] if interesting else ""
249
+ })
250
+
251
+ # Small delay between requests
252
+ await asyncio.sleep(0.1)
253
+
254
+ except Exception as e:
255
+ results.append({
256
+ "payload": payload,
257
+ "error": str(e)
258
+ })
259
+
260
+ # Format results
261
+ output = f"Fuzzing {param_name} ({param_location}) on {method} {base_url}\n\n"
262
+
263
+ interesting_results = [r for r in results if r.get("interesting")]
264
+ if interesting_results:
265
+ output += "=== INTERESTING RESULTS ===\n"
266
+ for r in interesting_results:
267
+ output += f"\nPayload: {r['payload']}\n"
268
+ output += f"Status: {r['status']}, Length: {r['length']}\n"
269
+ output += f"Indicators: {', '.join(r['indicators'])}\n"
270
+ if r.get('response_preview'):
271
+ output += f"Preview: {r['response_preview']}\n"
272
+
273
+ output += f"\n=== ALL RESULTS ({len(results)} payloads) ===\n"
274
+ for r in results:
275
+ if 'error' in r:
276
+ output += f"Payload: {r['payload']} - Error: {r['error']}\n"
277
+ else:
278
+ output += f"Payload: {r['payload']} - Status: {r['status']}, Length: {r['length']}\n"
279
+
280
+ return output
281
+
282
+ except Exception as e:
283
+ return f"Fuzzing failed: {str(e)}"
284
+
285
+
286
+ import asyncio
287
+
288
+
289
+ @register_tool(
290
+ name="test_authentication",
291
+ description="Test API authentication mechanisms",
292
+ parameters={
293
+ "base_url": {"type": "string", "description": "API base URL"},
294
+ "auth_endpoint": {"type": "string", "description": "Authentication endpoint (e.g., /auth/login)"},
295
+ "test_credentials": {"type": "array", "description": "List of username:password pairs to test"}
296
+ },
297
+ category="api_test"
298
+ )
299
+ async def test_authentication(
300
+ base_url: str,
301
+ auth_endpoint: str,
302
+ test_credentials: List[str]
303
+ ) -> str:
304
+ """Test authentication mechanisms."""
305
+ try:
306
+ client = get_http_client()
307
+ url = urljoin(base_url, auth_endpoint)
308
+ results = []
309
+
310
+ for cred in test_credentials[:10]: # Limit to 10 attempts
311
+ try:
312
+ username, password = cred.split(":", 1)
313
+
314
+ # Try common auth payload formats
315
+ payloads = [
316
+ {"username": username, "password": password},
317
+ {"email": username, "password": password},
318
+ {"user": username, "pass": password},
319
+ {"login": username, "password": password},
320
+ ]
321
+
322
+ for payload in payloads:
323
+ response = await client.post(url, json=payload)
324
+
325
+ # Check if authentication succeeded
326
+ success_indicators = ["token", "jwt", "session", "access_token", "auth"]
327
+ is_success = any(ind in response.text.lower() for ind in success_indicators)
328
+ is_success = is_success or response.status_code == 200
329
+
330
+ results.append({
331
+ "credential": cred,
332
+ "payload_format": list(payload.keys()),
333
+ "status": response.status_code,
334
+ "success": is_success,
335
+ "response_preview": response.text[:300]
336
+ })
337
+
338
+ if is_success:
339
+ break
340
+
341
+ await asyncio.sleep(0.2) # Rate limiting
342
+
343
+ except Exception as e:
344
+ results.append({"credential": cred, "error": str(e)})
345
+
346
+ # Format output
347
+ output = f"Authentication Testing: {url}\n\n"
348
+
349
+ successful = [r for r in results if r.get("success")]
350
+ if successful:
351
+ output += "=== SUCCESSFUL AUTHENTICATIONS ===\n"
352
+ for r in successful:
353
+ output += f"Credential: {r['credential']}\n"
354
+ output += f"Response: {r['response_preview']}\n\n"
355
+
356
+ output += f"\n=== ALL RESULTS ({len(results)} attempts) ===\n"
357
+ for r in results:
358
+ if 'error' in r:
359
+ output += f"{r['credential']}: Error - {r['error']}\n"
360
+ else:
361
+ status = "SUCCESS" if r['success'] else "FAILED"
362
+ output += f"{r['credential']}: {status} (HTTP {r['status']})\n"
363
+
364
+ return output
365
+
366
+ except Exception as e:
367
+ return f"Auth testing failed: {str(e)}"
368
+
369
+
370
+ @register_tool(
371
+ name="test_authorization",
372
+ description="Test for authorization bypass (IDOR/BOLA)",
373
+ parameters={
374
+ "url": {"type": "string", "description": "URL with object ID to test"},
375
+ "auth_header": {"type": "string", "description": "Authorization header value"},
376
+ "test_ids": {"type": "array", "description": "List of IDs to test access to"}
377
+ },
378
+ category="api_test"
379
+ )
380
+ async def test_authorization(
381
+ url: str,
382
+ auth_header: str,
383
+ test_ids: List[str]
384
+ ) -> str:
385
+ """Test for authorization bypass."""
386
+ try:
387
+ client = get_http_client()
388
+ results = []
389
+
390
+ headers = {"Authorization": auth_header}
391
+
392
+ for test_id in test_ids[:20]: # Limit to 20 IDs
393
+ try:
394
+ # Replace ID placeholder in URL
395
+ test_url = url.replace("{id}", test_id).replace(":id", test_id)
396
+
397
+ response = await client.get(test_url, headers=headers)
398
+
399
+ results.append({
400
+ "id": test_id,
401
+ "url": test_url,
402
+ "status": response.status_code,
403
+ "length": len(response.text),
404
+ "accessible": response.status_code == 200,
405
+ "response_preview": response.text[:200] if response.status_code == 200 else ""
406
+ })
407
+
408
+ await asyncio.sleep(0.1)
409
+
410
+ except Exception as e:
411
+ results.append({"id": test_id, "error": str(e)})
412
+
413
+ # Analyze for IDOR
414
+ accessible = [r for r in results if r.get("accessible")]
415
+
416
+ output = f"Authorization Testing: {url}\n\n"
417
+
418
+ if len(accessible) > 1:
419
+ output += "⚠️ POTENTIAL IDOR DETECTED - Multiple resources accessible\n\n"
420
+
421
+ output += f"=== ACCESSIBLE RESOURCES ({len(accessible)}) ===\n"
422
+ for r in accessible:
423
+ output += f"ID: {r['id']} - Length: {r['length']}\n"
424
+ if r.get('response_preview'):
425
+ output += f"Preview: {r['response_preview']}\n\n"
426
+
427
+ output += f"\n=== ALL RESULTS ({len(results)} IDs tested) ===\n"
428
+ for r in results:
429
+ if 'error' in r:
430
+ output += f"ID {r['id']}: Error - {r['error']}\n"
431
+ else:
432
+ status = "ACCESSIBLE" if r['accessible'] else "DENIED"
433
+ output += f"ID {r['id']}: {status} (HTTP {r['status']})\n"
434
+
435
+ return output
436
+
437
+ except Exception as e:
438
+ return f"Authorization testing failed: {str(e)}"
439
+
440
+
441
+ @register_tool(
442
+ name="report_api_finding",
443
+ description="Report an API security vulnerability",
444
+ parameters={
445
+ "title": {"type": "string", "description": "Title of the vulnerability"},
446
+ "severity": {"type": "string", "description": "Severity level"},
447
+ "endpoint": {"type": "string", "description": "Affected endpoint"},
448
+ "method": {"type": "string", "description": "HTTP method"},
449
+ "description": {"type": "string", "description": "Detailed description"},
450
+ "request": {"type": "string", "description": "Example malicious request"},
451
+ "response": {"type": "string", "description": "Response showing vulnerability"},
452
+ "remediation": {"type": "string", "description": "How to fix"}
453
+ },
454
+ category="api_test"
455
+ )
456
+ async def report_api_finding(
457
+ title: str,
458
+ severity: str,
459
+ endpoint: str,
460
+ method: str,
461
+ description: str,
462
+ request: str,
463
+ response: str,
464
+ remediation: str
465
+ ) -> str:
466
+ """Report an API security finding."""
467
+ return f"""API Security Finding Recorded:
468
+ Title: {title}
469
+ Severity: {severity}
470
+ Endpoint: {method} {endpoint}
471
+ Description: {description}
472
+ Request: {request[:500]}
473
+ Response: {response[:500]}
474
+ Remediation: {remediation}
475
+ """
476
+
477
+
478
+ API_TEST_SYSTEM_PROMPT = """You are an expert API security tester specializing in REST API penetration testing.
479
+
480
+ ## EXPERTISE AREAS
481
+ - OWASP API Security Top 10
482
+ - Authentication/Authorization attacks (BOLA, BFLA)
483
+ - Injection attacks (SQLi, NoSQLi, Command Injection)
484
+ - Mass assignment vulnerabilities
485
+ - Rate limiting bypass
486
+ - Information disclosure
487
+
488
+ ## TESTING METHODOLOGY
489
+
490
+ ### 1. Reconnaissance
491
+ - Parse OpenAPI/Swagger specs if available
492
+ - Enumerate endpoints and parameters
493
+ - Identify authentication mechanisms
494
+ - Map data models and relationships
495
+
496
+ ### 2. Authentication Testing
497
+ - Test for weak credentials
498
+ - Check token handling (JWT vulnerabilities, session issues)
499
+ - Test password reset flows
500
+ - Check for authentication bypass
501
+
502
+ ### 3. Authorization Testing (BOLA/BFLA)
503
+ - Test accessing other users' resources (horizontal escalation)
504
+ - Test accessing admin functions (vertical escalation)
505
+ - Check for IDOR vulnerabilities
506
+ - Test function-level access control
507
+
508
+ ### 4. Injection Testing
509
+ - SQL injection in all parameters
510
+ - NoSQL injection (MongoDB operators)
511
+ - Command injection
512
+ - Template injection
513
+
514
+ ### 5. Data Handling
515
+ - Test for mass assignment
516
+ - Check for sensitive data in responses
517
+ - Test file upload functionality
518
+ - Check for rate limiting
519
+
520
+ ## PAYLOADS BY VULNERABILITY
521
+
522
+ ### SQL Injection
523
+ - ' OR '1'='1
524
+ - 1; DROP TABLE--
525
+ - ' UNION SELECT NULL--
526
+
527
+ ### NoSQL Injection
528
+ - {"$gt": ""}
529
+ - {"$ne": null}
530
+ - {"$where": "sleep(5000)"}
531
+
532
+ ### Command Injection
533
+ - ; id
534
+ - | cat /etc/passwd
535
+ - `whoami`
536
+
537
+ ### Mass Assignment
538
+ - Add admin: true to requests
539
+ - Add role: admin to user creation
540
+ - Include hidden fields
541
+
542
+ ## OUTPUT FORMAT
543
+ Use report_api_finding for each vulnerability discovered with:
544
+ - Clear title and severity
545
+ - Affected endpoint and method
546
+ - Full request/response evidence
547
+ - Specific remediation steps
548
+
549
+ Be aggressive and thorough. Test every endpoint. Check every parameter."""
550
+
551
+
552
+ class APITestAgent(BaseSecurityAgent):
553
+ """
554
+ AI-powered API security testing agent.
555
+
556
+ Performs comprehensive security testing of REST APIs including:
557
+ - Authentication and authorization testing
558
+ - Injection vulnerability testing
559
+ - Business logic testing
560
+ - Rate limiting and DoS testing
561
+
562
+ Usage:
563
+ agent = APITestAgent(base_url="https://api.example.com")
564
+ result = await agent.run()
565
+ """
566
+
567
+ def __init__(
568
+ self,
569
+ base_url: str,
570
+ config: Optional[AgentConfig] = None,
571
+ openapi_spec: Optional[str] = None,
572
+ auth_token: Optional[str] = None,
573
+ headers: Optional[Dict[str, str]] = None
574
+ ):
575
+ """
576
+ Initialize the API testing agent.
577
+
578
+ Args:
579
+ base_url: Base URL of the API to test
580
+ config: Agent configuration
581
+ openapi_spec: Path or URL to OpenAPI/Swagger spec
582
+ auth_token: Authentication token for API access
583
+ headers: Additional headers to include in requests
584
+ """
585
+ super().__init__(config)
586
+ self.base_url = base_url.rstrip('/')
587
+ self.openapi_spec = openapi_spec
588
+ self.auth_token = auth_token
589
+ self.headers = headers or {}
590
+
591
+ if auth_token:
592
+ self.headers["Authorization"] = f"Bearer {auth_token}"
593
+
594
+ def get_system_prompt(self) -> str:
595
+ """Get the API testing system prompt."""
596
+ return API_TEST_SYSTEM_PROMPT
597
+
598
+ def get_tools(self) -> List[Dict[str, Any]]:
599
+ """Get tools available for API testing."""
600
+ return [
601
+ {
602
+ "name": "http_request",
603
+ "description": "Send an HTTP request to test an API endpoint",
604
+ "parameters": {
605
+ "method": {"type": "string", "description": "HTTP method"},
606
+ "url": {"type": "string", "description": "Full URL"},
607
+ "headers": {"type": "object", "description": "Optional headers"},
608
+ "body": {"type": "string", "description": "Optional JSON body"},
609
+ "params": {"type": "object", "description": "Query parameters"}
610
+ },
611
+ "required": ["method", "url"]
612
+ },
613
+ {
614
+ "name": "parse_openapi",
615
+ "description": "Parse OpenAPI/Swagger specification",
616
+ "parameters": {
617
+ "spec_url_or_content": {"type": "string", "description": "URL or content of spec"}
618
+ },
619
+ "required": ["spec_url_or_content"]
620
+ },
621
+ {
622
+ "name": "fuzz_parameter",
623
+ "description": "Fuzz a parameter with payloads",
624
+ "parameters": {
625
+ "base_url": {"type": "string"},
626
+ "method": {"type": "string"},
627
+ "param_name": {"type": "string"},
628
+ "param_location": {"type": "string"},
629
+ "payloads": {"type": "array"},
630
+ "headers": {"type": "object"}
631
+ },
632
+ "required": ["base_url", "method", "param_name", "param_location", "payloads"]
633
+ },
634
+ {
635
+ "name": "test_authentication",
636
+ "description": "Test authentication mechanisms",
637
+ "parameters": {
638
+ "base_url": {"type": "string"},
639
+ "auth_endpoint": {"type": "string"},
640
+ "test_credentials": {"type": "array"}
641
+ },
642
+ "required": ["base_url", "auth_endpoint", "test_credentials"]
643
+ },
644
+ {
645
+ "name": "test_authorization",
646
+ "description": "Test for IDOR/BOLA vulnerabilities",
647
+ "parameters": {
648
+ "url": {"type": "string"},
649
+ "auth_header": {"type": "string"},
650
+ "test_ids": {"type": "array"}
651
+ },
652
+ "required": ["url", "auth_header", "test_ids"]
653
+ },
654
+ {
655
+ "name": "report_api_finding",
656
+ "description": "Report an API security finding",
657
+ "parameters": {
658
+ "title": {"type": "string"},
659
+ "severity": {"type": "string"},
660
+ "endpoint": {"type": "string"},
661
+ "method": {"type": "string"},
662
+ "description": {"type": "string"},
663
+ "request": {"type": "string"},
664
+ "response": {"type": "string"},
665
+ "remediation": {"type": "string"}
666
+ },
667
+ "required": ["title", "severity", "endpoint", "method", "description", "request", "response", "remediation"]
668
+ }
669
+ ]
670
+
671
+ async def run(self, initial_message: Optional[str] = None) -> AgentResult:
672
+ """
673
+ Run the API security test.
674
+
675
+ Args:
676
+ initial_message: Optional additional instructions
677
+
678
+ Returns:
679
+ AgentResult with all security findings
680
+ """
681
+ message = f"""Perform comprehensive API security testing on: {self.base_url}
682
+
683
+ """
684
+ if self.openapi_spec:
685
+ message += f"OpenAPI Specification available at: {self.openapi_spec}\nStart by parsing the OpenAPI spec to discover all endpoints.\n\n"
686
+ else:
687
+ message += "No OpenAPI spec provided. Start by discovering endpoints through common paths and responses.\n\n"
688
+
689
+ if self.headers:
690
+ message += f"Use these headers for authenticated requests: {json.dumps(self.headers)}\n\n"
691
+
692
+ message += """Testing priorities:
693
+ 1. Map all endpoints and parameters
694
+ 2. Test authentication mechanisms
695
+ 3. Check for authorization bypass (BOLA/IDOR)
696
+ 4. Test injection vulnerabilities
697
+ 5. Check for information disclosure
698
+ 6. Test rate limiting
699
+
700
+ """
701
+ if initial_message:
702
+ message += initial_message
703
+
704
+ message += "\n\nBegin testing now."
705
+
706
+ return await super().run(message)