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,518 @@
1
+ """
2
+ AIPT LLM-Powered Tool Selection
3
+
4
+ Uses LLM intelligence to dynamically select the most appropriate security tools
5
+ based on:
6
+ - Current scan phase
7
+ - Findings discovered so far
8
+ - Target characteristics (tech stack, WAF, etc.)
9
+ - Time/resource constraints
10
+
11
+ This replaces static tool lists with intelligent, context-aware selection.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import logging
17
+ import os
18
+ from dataclasses import dataclass, field
19
+ from datetime import datetime
20
+ from typing import Any, Optional
21
+
22
+ from aipt_v2.models.findings import Finding, VulnerabilityType
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ # Available tools by category
28
+ AVAILABLE_TOOLS = {
29
+ "recon": {
30
+ "subfinder": {
31
+ "description": "Fast subdomain discovery using passive sources",
32
+ "best_for": ["subdomain_enumeration", "asset_discovery"],
33
+ "speed": "fast",
34
+ "noise": "low",
35
+ },
36
+ "amass": {
37
+ "description": "Comprehensive subdomain enumeration with active/passive modes",
38
+ "best_for": ["deep_subdomain_enum", "asset_discovery"],
39
+ "speed": "slow",
40
+ "noise": "medium",
41
+ },
42
+ "httpx": {
43
+ "description": "HTTP probing to find live hosts and tech fingerprinting",
44
+ "best_for": ["live_host_detection", "tech_detection"],
45
+ "speed": "fast",
46
+ "noise": "low",
47
+ },
48
+ "nmap": {
49
+ "description": "Port scanning and service detection",
50
+ "best_for": ["port_scan", "service_detection", "os_detection"],
51
+ "speed": "medium",
52
+ "noise": "high",
53
+ },
54
+ "whatweb": {
55
+ "description": "Web technology fingerprinting",
56
+ "best_for": ["tech_detection", "cms_detection"],
57
+ "speed": "fast",
58
+ "noise": "low",
59
+ },
60
+ "wafw00f": {
61
+ "description": "Web application firewall detection",
62
+ "best_for": ["waf_detection", "security_posture"],
63
+ "speed": "fast",
64
+ "noise": "low",
65
+ },
66
+ "waybackurls": {
67
+ "description": "Fetch URLs from Wayback Machine archives",
68
+ "best_for": ["url_discovery", "historical_endpoints"],
69
+ "speed": "fast",
70
+ "noise": "none",
71
+ },
72
+ "gau": {
73
+ "description": "Fetch known URLs from AlienVault, Wayback, Common Crawl",
74
+ "best_for": ["url_discovery", "parameter_discovery"],
75
+ "speed": "fast",
76
+ "noise": "none",
77
+ },
78
+ },
79
+ "scan": {
80
+ "nuclei": {
81
+ "description": "Template-based vulnerability scanner",
82
+ "best_for": ["known_vulns", "cve_detection", "misconfigs"],
83
+ "speed": "fast",
84
+ "noise": "medium",
85
+ },
86
+ "nikto": {
87
+ "description": "Web server scanner for dangerous files/CGIs",
88
+ "best_for": ["web_server_vulns", "misconfigs", "default_files"],
89
+ "speed": "slow",
90
+ "noise": "high",
91
+ },
92
+ "wpscan": {
93
+ "description": "WordPress vulnerability scanner",
94
+ "best_for": ["wordpress_vulns", "plugin_vulns", "theme_vulns"],
95
+ "speed": "medium",
96
+ "noise": "medium",
97
+ "requires": ["wordpress"],
98
+ },
99
+ "sqlmap": {
100
+ "description": "Automatic SQL injection detection and exploitation",
101
+ "best_for": ["sqli_detection", "sqli_exploitation", "db_dump"],
102
+ "speed": "slow",
103
+ "noise": "high",
104
+ },
105
+ "ffuf": {
106
+ "description": "Fast web fuzzer for content discovery",
107
+ "best_for": ["dir_bruteforce", "parameter_fuzzing", "vhost_discovery"],
108
+ "speed": "fast",
109
+ "noise": "high",
110
+ },
111
+ "gobuster": {
112
+ "description": "Directory/file & DNS busting tool",
113
+ "best_for": ["dir_bruteforce", "dns_bruteforce"],
114
+ "speed": "fast",
115
+ "noise": "high",
116
+ },
117
+ "dirsearch": {
118
+ "description": "Web path scanner",
119
+ "best_for": ["dir_bruteforce", "backup_files"],
120
+ "speed": "medium",
121
+ "noise": "high",
122
+ },
123
+ "sslscan": {
124
+ "description": "SSL/TLS configuration scanner",
125
+ "best_for": ["ssl_vulns", "cipher_analysis", "cert_issues"],
126
+ "speed": "fast",
127
+ "noise": "low",
128
+ },
129
+ "testssl": {
130
+ "description": "Comprehensive SSL/TLS testing",
131
+ "best_for": ["ssl_vulns", "protocol_analysis"],
132
+ "speed": "medium",
133
+ "noise": "low",
134
+ },
135
+ },
136
+ "exploit": {
137
+ "sqlmap": {
138
+ "description": "SQL injection exploitation and data extraction",
139
+ "best_for": ["sqli_exploitation", "db_dump", "os_shell"],
140
+ "speed": "slow",
141
+ "noise": "high",
142
+ },
143
+ "commix": {
144
+ "description": "Command injection exploitation",
145
+ "best_for": ["command_injection", "os_shell"],
146
+ "speed": "medium",
147
+ "noise": "high",
148
+ },
149
+ "xsstrike": {
150
+ "description": "Advanced XSS detection and exploitation",
151
+ "best_for": ["xss_detection", "xss_exploitation", "waf_bypass"],
152
+ "speed": "medium",
153
+ "noise": "medium",
154
+ },
155
+ },
156
+ }
157
+
158
+
159
+ TOOL_SELECTION_PROMPT = """You are an expert penetration tester selecting tools for the next phase of testing.
160
+
161
+ ## Current Scan State
162
+ - **Target**: {target}
163
+ - **Phase**: {phase}
164
+ - **Time Budget**: {time_budget} minutes remaining
165
+ - **Stealth Mode**: {stealth_mode}
166
+
167
+ ## Findings So Far
168
+ {findings_summary}
169
+
170
+ ## Detected Technologies
171
+ {tech_stack}
172
+
173
+ ## WAF/Security Controls
174
+ {security_controls}
175
+
176
+ ## Tools Already Run
177
+ {tools_run}
178
+
179
+ ## Available Tools for {phase} Phase
180
+ {available_tools}
181
+
182
+ ## Your Task
183
+ Select the 3-5 most valuable tools to run next. Consider:
184
+
185
+ 1. **Gap Analysis**: What information are we missing?
186
+ 2. **Finding Follow-up**: Which findings need deeper investigation?
187
+ 3. **Attack Surface**: What haven't we explored yet?
188
+ 4. **Efficiency**: Prioritize fast tools if time-limited
189
+ 5. **Stealth**: Avoid noisy tools if stealth_mode is True
190
+ 6. **Tech-Specific**: Use specialized tools for detected technologies
191
+
192
+ ## Output Format (JSON)
193
+ ```json
194
+ {{
195
+ "selected_tools": [
196
+ {{
197
+ "name": "tool_name",
198
+ "priority": 1,
199
+ "reasoning": "Why this tool now",
200
+ "expected_findings": "What we hope to discover",
201
+ "custom_args": "Any specific arguments or targets"
202
+ }}
203
+ ],
204
+ "skip_tools": [
205
+ {{
206
+ "name": "tool_name",
207
+ "reason": "Why skip this tool"
208
+ }}
209
+ ],
210
+ "overall_strategy": "Brief description of testing strategy"
211
+ }}
212
+ ```"""
213
+
214
+
215
+ @dataclass
216
+ class ToolSelection:
217
+ """Result of LLM tool selection."""
218
+ name: str
219
+ priority: int
220
+ reasoning: str
221
+ expected_findings: str
222
+ custom_args: Optional[str] = None
223
+
224
+
225
+ @dataclass
226
+ class ToolSelectionResult:
227
+ """Complete tool selection result."""
228
+ selected_tools: list[ToolSelection]
229
+ skip_tools: list[dict[str, str]]
230
+ overall_strategy: str
231
+ confidence: float = 0.9
232
+ selected_at: datetime = field(default_factory=datetime.utcnow)
233
+
234
+ def get_tool_names(self) -> list[str]:
235
+ """Get just the tool names in priority order."""
236
+ return [t.name for t in sorted(self.selected_tools, key=lambda x: x.priority)]
237
+
238
+
239
+ class LLMToolSelector:
240
+ """
241
+ LLM-powered intelligent tool selection.
242
+
243
+ Uses an LLM to analyze the current scan state and select the most
244
+ appropriate tools for the next phase of testing.
245
+
246
+ Example:
247
+ selector = LLMToolSelector()
248
+ result = await selector.select_tools(
249
+ target="https://example.com",
250
+ phase="scan",
251
+ findings=[...],
252
+ tech_stack=["WordPress", "PHP", "MySQL"]
253
+ )
254
+ for tool in result.get_tool_names():
255
+ print(f"Run: {tool}")
256
+ """
257
+
258
+ def __init__(
259
+ self,
260
+ llm_provider: str = "anthropic",
261
+ llm_model: str = "claude-3-haiku-20240307",
262
+ ):
263
+ self.llm_provider = llm_provider
264
+ self.llm_model = llm_model
265
+ self._llm = None
266
+
267
+ async def _get_llm(self):
268
+ """Get or create LLM client."""
269
+ if self._llm is None:
270
+ try:
271
+ import litellm
272
+ self._llm = litellm
273
+ except ImportError:
274
+ logger.warning("litellm not installed, falling back to heuristics")
275
+ return None
276
+ return self._llm
277
+
278
+ async def select_tools(
279
+ self,
280
+ target: str,
281
+ phase: str,
282
+ findings: list[Finding],
283
+ tech_stack: list[str] = None,
284
+ waf_detected: str = None,
285
+ tools_already_run: list[str] = None,
286
+ time_budget_minutes: int = 60,
287
+ stealth_mode: bool = False,
288
+ ) -> ToolSelectionResult:
289
+ """
290
+ Select the best tools for the current phase.
291
+
292
+ Args:
293
+ target: Target URL or domain
294
+ phase: Current phase (recon, scan, exploit)
295
+ findings: Findings discovered so far
296
+ tech_stack: Detected technologies
297
+ waf_detected: Detected WAF name
298
+ tools_already_run: Tools that have already been executed
299
+ time_budget_minutes: Remaining time budget
300
+ stealth_mode: Whether to prioritize stealth over thoroughness
301
+
302
+ Returns:
303
+ ToolSelectionResult with prioritized tool list
304
+ """
305
+ llm = await self._get_llm()
306
+
307
+ if llm is None or not self._has_api_key():
308
+ # Fall back to heuristic selection
309
+ return self._heuristic_selection(
310
+ target, phase, findings, tech_stack,
311
+ waf_detected, tools_already_run, stealth_mode
312
+ )
313
+
314
+ # Prepare context for LLM
315
+ findings_summary = self._summarize_findings(findings)
316
+ available = AVAILABLE_TOOLS.get(phase, {})
317
+ available_str = json.dumps(available, indent=2)
318
+
319
+ prompt = TOOL_SELECTION_PROMPT.format(
320
+ target=target,
321
+ phase=phase,
322
+ time_budget=time_budget_minutes,
323
+ stealth_mode=stealth_mode,
324
+ findings_summary=findings_summary,
325
+ tech_stack=", ".join(tech_stack) if tech_stack else "Not yet determined",
326
+ security_controls=waf_detected or "None detected",
327
+ tools_run=", ".join(tools_already_run) if tools_already_run else "None yet",
328
+ available_tools=available_str,
329
+ )
330
+
331
+ try:
332
+ response = await self._call_llm(prompt)
333
+ return self._parse_llm_response(response)
334
+ except Exception as e:
335
+ logger.warning(f"LLM tool selection failed: {e}, falling back to heuristics")
336
+ return self._heuristic_selection(
337
+ target, phase, findings, tech_stack,
338
+ waf_detected, tools_already_run, stealth_mode
339
+ )
340
+
341
+ async def _call_llm(self, prompt: str) -> str:
342
+ """Call LLM and get response."""
343
+ llm = await self._get_llm()
344
+
345
+ model_str = f"{self.llm_provider}/{self.llm_model}"
346
+ if self.llm_provider == "anthropic" and not self.llm_model.startswith("anthropic/"):
347
+ model_str = f"anthropic/{self.llm_model}"
348
+ elif self.llm_provider == "openai" and not self.llm_model.startswith("openai/"):
349
+ model_str = f"openai/{self.llm_model}"
350
+
351
+ response = await llm.acompletion(
352
+ model=model_str,
353
+ messages=[{"role": "user", "content": prompt}],
354
+ max_tokens=2000,
355
+ temperature=0.3,
356
+ )
357
+
358
+ return response.choices[0].message.content
359
+
360
+ def _parse_llm_response(self, response: str) -> ToolSelectionResult:
361
+ """Parse LLM response into ToolSelectionResult."""
362
+ try:
363
+ # Extract JSON from response
364
+ json_start = response.find("{")
365
+ json_end = response.rfind("}") + 1
366
+ if json_start >= 0 and json_end > json_start:
367
+ json_str = response[json_start:json_end]
368
+ data = json.loads(json_str)
369
+ else:
370
+ raise ValueError("No JSON found in response")
371
+
372
+ selected = []
373
+ for tool_data in data.get("selected_tools", []):
374
+ selected.append(ToolSelection(
375
+ name=tool_data["name"],
376
+ priority=tool_data.get("priority", 1),
377
+ reasoning=tool_data.get("reasoning", ""),
378
+ expected_findings=tool_data.get("expected_findings", ""),
379
+ custom_args=tool_data.get("custom_args"),
380
+ ))
381
+
382
+ return ToolSelectionResult(
383
+ selected_tools=selected,
384
+ skip_tools=data.get("skip_tools", []),
385
+ overall_strategy=data.get("overall_strategy", ""),
386
+ )
387
+
388
+ except (json.JSONDecodeError, KeyError, ValueError) as e:
389
+ logger.warning(f"Failed to parse LLM response: {e}")
390
+ # Return empty result, will fall back to heuristics
391
+ raise
392
+
393
+ def _heuristic_selection(
394
+ self,
395
+ target: str,
396
+ phase: str,
397
+ findings: list[Finding],
398
+ tech_stack: list[str] = None,
399
+ waf_detected: str = None,
400
+ tools_already_run: list[str] = None,
401
+ stealth_mode: bool = False,
402
+ ) -> ToolSelectionResult:
403
+ """
404
+ Heuristic-based tool selection when LLM is unavailable.
405
+
406
+ Uses predefined rules based on phase and context.
407
+ """
408
+ tools_already_run = tools_already_run or []
409
+ tech_stack = tech_stack or []
410
+ selected = []
411
+
412
+ available = AVAILABLE_TOOLS.get(phase, {})
413
+
414
+ if phase == "recon":
415
+ # Standard recon order
416
+ priority_order = ["subfinder", "httpx", "whatweb", "wafw00f", "waybackurls"]
417
+ if not stealth_mode:
418
+ priority_order.extend(["nmap", "amass"])
419
+
420
+ elif phase == "scan":
421
+ priority_order = ["nuclei", "ffuf"]
422
+
423
+ # Add tech-specific tools
424
+ if any("wordpress" in t.lower() for t in tech_stack):
425
+ priority_order.insert(0, "wpscan")
426
+
427
+ # Add SQLi tools if we found potential injection points
428
+ sqli_findings = [f for f in findings if f.vuln_type == VulnerabilityType.SQL_INJECTION]
429
+ if sqli_findings:
430
+ priority_order.insert(0, "sqlmap")
431
+
432
+ if not stealth_mode:
433
+ priority_order.extend(["nikto", "gobuster", "sslscan"])
434
+
435
+ elif phase == "exploit":
436
+ priority_order = []
437
+
438
+ # Select exploit tools based on findings
439
+ for finding in findings:
440
+ if finding.vuln_type == VulnerabilityType.SQL_INJECTION:
441
+ if "sqlmap" not in priority_order:
442
+ priority_order.append("sqlmap")
443
+ elif finding.vuln_type == VulnerabilityType.COMMAND_INJECTION:
444
+ if "commix" not in priority_order:
445
+ priority_order.append("commix")
446
+ elif finding.vuln_type in [VulnerabilityType.XSS_REFLECTED, VulnerabilityType.XSS_STORED]:
447
+ if "xsstrike" not in priority_order:
448
+ priority_order.append("xsstrike")
449
+
450
+ else:
451
+ priority_order = list(available.keys())[:5]
452
+
453
+ # Build selection result
454
+ priority = 1
455
+ for tool_name in priority_order:
456
+ if tool_name in tools_already_run:
457
+ continue
458
+ if tool_name not in available:
459
+ continue
460
+
461
+ tool_info = available[tool_name]
462
+ selected.append(ToolSelection(
463
+ name=tool_name,
464
+ priority=priority,
465
+ reasoning=f"Standard {phase} phase tool: {tool_info['description']}",
466
+ expected_findings=f"Discover {', '.join(tool_info['best_for'][:2])}",
467
+ ))
468
+ priority += 1
469
+
470
+ if len(selected) >= 5:
471
+ break
472
+
473
+ return ToolSelectionResult(
474
+ selected_tools=selected,
475
+ skip_tools=[],
476
+ overall_strategy=f"Heuristic {phase} phase tool selection",
477
+ confidence=0.7,
478
+ )
479
+
480
+ def _summarize_findings(self, findings: list[Finding]) -> str:
481
+ """Summarize findings for LLM context."""
482
+ if not findings:
483
+ return "No findings discovered yet."
484
+
485
+ # Count by severity
486
+ by_severity = {}
487
+ for f in findings:
488
+ sev = f.severity.value
489
+ by_severity[sev] = by_severity.get(sev, 0) + 1
490
+
491
+ # Count by type
492
+ by_type = {}
493
+ for f in findings:
494
+ vtype = f.vuln_type.value
495
+ by_type[vtype] = by_type.get(vtype, 0) + 1
496
+
497
+ lines = [
498
+ f"Total findings: {len(findings)}",
499
+ f"By severity: {by_severity}",
500
+ f"By type: {by_type}",
501
+ "",
502
+ "Notable findings:",
503
+ ]
504
+
505
+ # Add top 5 most severe
506
+ sorted_findings = sorted(findings, key=lambda f: f.severity, reverse=True)
507
+ for f in sorted_findings[:5]:
508
+ lines.append(f"- [{f.severity.value}] {f.vuln_type.value}: {f.title} at {f.url}")
509
+
510
+ return "\n".join(lines)
511
+
512
+ def _has_api_key(self) -> bool:
513
+ """Check if API key is available."""
514
+ if self.llm_provider == "anthropic":
515
+ return bool(os.getenv("ANTHROPIC_API_KEY"))
516
+ if self.llm_provider == "openai":
517
+ return bool(os.getenv("OPENAI_API_KEY"))
518
+ return False