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,474 @@
1
+ """
2
+ AIPT Real-Time Adaptation Engine
3
+
4
+ Adapts scanning strategy in real-time based on target responses:
5
+ - Detects WAF/rate limiting and adjusts accordingly
6
+ - Modifies payloads when blocked
7
+ - Adjusts timing to avoid detection
8
+ - Switches techniques based on feedback
9
+
10
+ This provides intelligent, adaptive scanning that responds to defenses.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ import logging
16
+ import time
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime
19
+ from typing import Any, Callable, Optional
20
+ from enum import Enum
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class DefenseType(Enum):
26
+ """Types of defenses that can be detected."""
27
+ WAF = "waf"
28
+ RATE_LIMIT = "rate_limit"
29
+ IP_BLOCK = "ip_block"
30
+ GEO_BLOCK = "geo_block"
31
+ CAPTCHA = "captcha"
32
+ HONEYPOT = "honeypot"
33
+ TARPIT = "tarpit"
34
+
35
+
36
+ class AdaptationAction(Enum):
37
+ """Actions that can be taken in response to defenses."""
38
+ SLOW_DOWN = "slow_down"
39
+ CHANGE_PAYLOAD = "change_payload"
40
+ USE_PROXY = "use_proxy"
41
+ WAIT_AND_RETRY = "wait_and_retry"
42
+ SKIP_ENDPOINT = "skip_endpoint"
43
+ SWITCH_TECHNIQUE = "switch_technique"
44
+ ABORT = "abort"
45
+
46
+
47
+ @dataclass
48
+ class DefenseDetection:
49
+ """Detection of a defensive measure."""
50
+ defense_type: DefenseType
51
+ confidence: float # 0.0 to 1.0
52
+ evidence: str
53
+ detected_at: datetime = field(default_factory=datetime.utcnow)
54
+ metadata: dict[str, Any] = field(default_factory=dict)
55
+
56
+
57
+ @dataclass
58
+ class AdaptationStrategy:
59
+ """Strategy for adapting to detected defenses."""
60
+ action: AdaptationAction
61
+ parameters: dict[str, Any]
62
+ reason: str
63
+ expected_outcome: str
64
+
65
+
66
+ @dataclass
67
+ class RequestResult:
68
+ """Result of a request for adaptation analysis."""
69
+ url: str
70
+ status_code: int
71
+ response_time_ms: int
72
+ response_size: int
73
+ blocked: bool = False
74
+ error: Optional[str] = None
75
+ headers: dict[str, str] = field(default_factory=dict)
76
+
77
+
78
+ @dataclass
79
+ class AdaptationState:
80
+ """Current state of the adaptation engine."""
81
+ request_count: int = 0
82
+ blocked_count: int = 0
83
+ success_count: int = 0
84
+ current_delay_ms: int = 100
85
+ detected_defenses: list[DefenseDetection] = field(default_factory=list)
86
+ payload_failures: dict[str, int] = field(default_factory=dict)
87
+ last_request_time: Optional[datetime] = None
88
+ is_rate_limited: bool = False
89
+ is_blocked: bool = False
90
+
91
+
92
+ class RealTimeAdapter:
93
+ """
94
+ Real-time adaptation engine for security scanning.
95
+
96
+ Monitors request/response patterns and adapts scanning strategy
97
+ to avoid detection and maximize effectiveness.
98
+
99
+ Example:
100
+ adapter = RealTimeAdapter()
101
+
102
+ # Register defense handlers
103
+ adapter.on_waf_detected(lambda d: print(f"WAF: {d.evidence}"))
104
+
105
+ # Process results and adapt
106
+ for url in urls:
107
+ result = await send_request(url)
108
+ strategy = adapter.analyze_result(result)
109
+
110
+ if strategy.action == AdaptationAction.SLOW_DOWN:
111
+ await asyncio.sleep(strategy.parameters["delay_ms"] / 1000)
112
+ elif strategy.action == AdaptationAction.CHANGE_PAYLOAD:
113
+ payload = strategy.parameters["new_payload"]
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ base_delay_ms: int = 100,
119
+ max_delay_ms: int = 5000,
120
+ block_threshold: int = 5,
121
+ rate_limit_threshold: int = 10,
122
+ ):
123
+ self.base_delay_ms = base_delay_ms
124
+ self.max_delay_ms = max_delay_ms
125
+ self.block_threshold = block_threshold
126
+ self.rate_limit_threshold = rate_limit_threshold
127
+
128
+ self.state = AdaptationState(current_delay_ms=base_delay_ms)
129
+ self._handlers: dict[DefenseType, list[Callable]] = {}
130
+ self._recent_responses: list[RequestResult] = []
131
+ self._max_recent = 50
132
+
133
+ def analyze_result(self, result: RequestResult) -> AdaptationStrategy:
134
+ """
135
+ Analyze a request result and determine adaptation strategy.
136
+
137
+ Args:
138
+ result: The request result to analyze
139
+
140
+ Returns:
141
+ AdaptationStrategy with recommended action
142
+ """
143
+ self._record_result(result)
144
+
145
+ # Check for various defense indicators
146
+ defenses = self._detect_defenses(result)
147
+ for defense in defenses:
148
+ self._record_defense(defense)
149
+ self._trigger_handlers(defense)
150
+
151
+ # Determine strategy based on detected defenses
152
+ return self._determine_strategy(result, defenses)
153
+
154
+ def _detect_defenses(self, result: RequestResult) -> list[DefenseDetection]:
155
+ """Detect defensive measures from a response."""
156
+ detections = []
157
+
158
+ # Check for WAF signatures
159
+ waf_detection = self._detect_waf(result)
160
+ if waf_detection:
161
+ detections.append(waf_detection)
162
+
163
+ # Check for rate limiting
164
+ rate_limit = self._detect_rate_limit(result)
165
+ if rate_limit:
166
+ detections.append(rate_limit)
167
+
168
+ # Check for IP blocking
169
+ ip_block = self._detect_ip_block(result)
170
+ if ip_block:
171
+ detections.append(ip_block)
172
+
173
+ # Check for captcha
174
+ captcha = self._detect_captcha(result)
175
+ if captcha:
176
+ detections.append(captcha)
177
+
178
+ return detections
179
+
180
+ def _detect_waf(self, result: RequestResult) -> Optional[DefenseDetection]:
181
+ """Detect Web Application Firewall signatures."""
182
+ waf_indicators = {
183
+ # Status codes
184
+ "status_403": result.status_code == 403,
185
+ "status_406": result.status_code == 406,
186
+ "status_429": result.status_code == 429,
187
+ "status_503": result.status_code == 503,
188
+
189
+ # Headers
190
+ "cloudflare": any("cloudflare" in v.lower() for v in result.headers.values()),
191
+ "akamai": any("akamai" in v.lower() for v in result.headers.values()),
192
+ "aws_waf": "x-amzn-requestid" in result.headers,
193
+ "mod_security": "mod_security" in str(result.headers).lower(),
194
+
195
+ # Response patterns
196
+ "blocked_keyword": result.blocked,
197
+ }
198
+
199
+ active_indicators = [k for k, v in waf_indicators.items() if v]
200
+
201
+ if active_indicators:
202
+ # Determine WAF name from indicators
203
+ waf_name = "Unknown WAF"
204
+ if "cloudflare" in active_indicators:
205
+ waf_name = "Cloudflare"
206
+ elif "akamai" in active_indicators:
207
+ waf_name = "Akamai"
208
+ elif "aws_waf" in active_indicators:
209
+ waf_name = "AWS WAF"
210
+ elif "mod_security" in active_indicators:
211
+ waf_name = "ModSecurity"
212
+
213
+ return DefenseDetection(
214
+ defense_type=DefenseType.WAF,
215
+ confidence=min(len(active_indicators) * 0.3, 1.0),
216
+ evidence=f"{waf_name} detected via: {', '.join(active_indicators)}",
217
+ metadata={"waf_name": waf_name, "indicators": active_indicators},
218
+ )
219
+
220
+ return None
221
+
222
+ def _detect_rate_limit(self, result: RequestResult) -> Optional[DefenseDetection]:
223
+ """Detect rate limiting."""
224
+ # Check status code
225
+ if result.status_code == 429:
226
+ retry_after = result.headers.get("retry-after", "unknown")
227
+ return DefenseDetection(
228
+ defense_type=DefenseType.RATE_LIMIT,
229
+ confidence=0.95,
230
+ evidence=f"HTTP 429 received, Retry-After: {retry_after}",
231
+ metadata={"retry_after": retry_after},
232
+ )
233
+
234
+ # Check for consistent slow responses indicating throttling
235
+ recent_times = [r.response_time_ms for r in self._recent_responses[-10:]]
236
+ if len(recent_times) >= 5:
237
+ avg_time = sum(recent_times) / len(recent_times)
238
+ if avg_time > 2000: # > 2 seconds average
239
+ return DefenseDetection(
240
+ defense_type=DefenseType.RATE_LIMIT,
241
+ confidence=0.6,
242
+ evidence=f"Throttling suspected: avg response time {avg_time:.0f}ms",
243
+ metadata={"avg_response_time": avg_time},
244
+ )
245
+
246
+ # Check for repeated blocks
247
+ recent_blocks = sum(1 for r in self._recent_responses[-10:] if r.blocked)
248
+ if recent_blocks >= self.rate_limit_threshold:
249
+ return DefenseDetection(
250
+ defense_type=DefenseType.RATE_LIMIT,
251
+ confidence=0.7,
252
+ evidence=f"{recent_blocks} blocks in last 10 requests",
253
+ metadata={"block_count": recent_blocks},
254
+ )
255
+
256
+ return None
257
+
258
+ def _detect_ip_block(self, result: RequestResult) -> Optional[DefenseDetection]:
259
+ """Detect IP-based blocking."""
260
+ # Consistent 403s or connection resets suggest IP block
261
+ recent_403s = sum(1 for r in self._recent_responses[-10:] if r.status_code == 403)
262
+
263
+ if recent_403s >= 8:
264
+ return DefenseDetection(
265
+ defense_type=DefenseType.IP_BLOCK,
266
+ confidence=0.8,
267
+ evidence=f"{recent_403s}/10 recent requests returned 403",
268
+ metadata={"consecutive_403s": recent_403s},
269
+ )
270
+
271
+ return None
272
+
273
+ def _detect_captcha(self, result: RequestResult) -> Optional[DefenseDetection]:
274
+ """Detect CAPTCHA challenges."""
275
+ captcha_indicators = [
276
+ "captcha" in str(result.headers).lower(),
277
+ result.status_code == 503, # Often used with challenges
278
+ ]
279
+
280
+ if any(captcha_indicators):
281
+ return DefenseDetection(
282
+ defense_type=DefenseType.CAPTCHA,
283
+ confidence=0.7,
284
+ evidence="CAPTCHA challenge detected",
285
+ )
286
+
287
+ return None
288
+
289
+ def _determine_strategy(
290
+ self,
291
+ result: RequestResult,
292
+ defenses: list[DefenseDetection],
293
+ ) -> AdaptationStrategy:
294
+ """Determine the best adaptation strategy."""
295
+ # Priority: IP Block > Rate Limit > WAF > Continue
296
+
297
+ # Check for IP block - most severe
298
+ ip_blocks = [d for d in defenses if d.defense_type == DefenseType.IP_BLOCK]
299
+ if ip_blocks and ip_blocks[0].confidence > 0.7:
300
+ return AdaptationStrategy(
301
+ action=AdaptationAction.USE_PROXY,
302
+ parameters={"reason": "IP appears blocked"},
303
+ reason="IP blocking detected with high confidence",
304
+ expected_outcome="Requests should succeed from new IP",
305
+ )
306
+
307
+ # Check for rate limiting
308
+ rate_limits = [d for d in defenses if d.defense_type == DefenseType.RATE_LIMIT]
309
+ if rate_limits:
310
+ rl = rate_limits[0]
311
+ new_delay = min(self.state.current_delay_ms * 2, self.max_delay_ms)
312
+
313
+ # Check for Retry-After header
314
+ retry_after = rl.metadata.get("retry_after")
315
+ if retry_after and retry_after != "unknown":
316
+ try:
317
+ new_delay = int(retry_after) * 1000
318
+ except ValueError:
319
+ pass
320
+
321
+ self.state.current_delay_ms = new_delay
322
+ self.state.is_rate_limited = True
323
+
324
+ return AdaptationStrategy(
325
+ action=AdaptationAction.SLOW_DOWN,
326
+ parameters={"delay_ms": new_delay},
327
+ reason=f"Rate limiting detected: {rl.evidence}",
328
+ expected_outcome=f"Delay increased to {new_delay}ms between requests",
329
+ )
330
+
331
+ # Check for WAF blocking
332
+ wafs = [d for d in defenses if d.defense_type == DefenseType.WAF]
333
+ if wafs and result.blocked:
334
+ waf = wafs[0]
335
+ waf_name = waf.metadata.get("waf_name", "Unknown")
336
+
337
+ return AdaptationStrategy(
338
+ action=AdaptationAction.CHANGE_PAYLOAD,
339
+ parameters={
340
+ "waf_name": waf_name,
341
+ "bypass_techniques": self._get_waf_bypass_techniques(waf_name),
342
+ },
343
+ reason=f"WAF blocking detected: {waf_name}",
344
+ expected_outcome="Payload modified to bypass WAF",
345
+ )
346
+
347
+ # Check for CAPTCHA
348
+ captchas = [d for d in defenses if d.defense_type == DefenseType.CAPTCHA]
349
+ if captchas:
350
+ return AdaptationStrategy(
351
+ action=AdaptationAction.SKIP_ENDPOINT,
352
+ parameters={"reason": "CAPTCHA required"},
353
+ reason="CAPTCHA challenge cannot be bypassed automatically",
354
+ expected_outcome="Endpoint skipped, manual testing recommended",
355
+ )
356
+
357
+ # No defenses - check if we can speed up
358
+ if (self.state.is_rate_limited and
359
+ self.state.blocked_count == 0 and
360
+ len(self._recent_responses) >= 10):
361
+
362
+ # No blocks in recent requests, try reducing delay
363
+ new_delay = max(self.base_delay_ms, self.state.current_delay_ms // 2)
364
+ self.state.current_delay_ms = new_delay
365
+
366
+ if new_delay == self.base_delay_ms:
367
+ self.state.is_rate_limited = False
368
+
369
+ # Default: continue with current settings
370
+ return AdaptationStrategy(
371
+ action=AdaptationAction.SLOW_DOWN,
372
+ parameters={"delay_ms": self.state.current_delay_ms},
373
+ reason="Maintaining current pace",
374
+ expected_outcome="Continue scanning",
375
+ )
376
+
377
+ def _get_waf_bypass_techniques(self, waf_name: str) -> list[str]:
378
+ """Get recommended WAF bypass techniques."""
379
+ techniques = {
380
+ "Cloudflare": [
381
+ "double_url_encode",
382
+ "unicode_normalization",
383
+ "case_variation",
384
+ "comment_injection",
385
+ ],
386
+ "Akamai": [
387
+ "unicode_normalization",
388
+ "parameter_pollution",
389
+ "json_payload",
390
+ ],
391
+ "AWS WAF": [
392
+ "double_url_encode",
393
+ "null_byte_injection",
394
+ ],
395
+ "ModSecurity": [
396
+ "comment_injection",
397
+ "case_variation",
398
+ "chunked_encoding",
399
+ ],
400
+ }
401
+ return techniques.get(waf_name, ["encoding_variation", "case_variation"])
402
+
403
+ def _record_result(self, result: RequestResult):
404
+ """Record a request result for analysis."""
405
+ self._recent_responses.append(result)
406
+ if len(self._recent_responses) > self._max_recent:
407
+ self._recent_responses.pop(0)
408
+
409
+ self.state.request_count += 1
410
+ if result.blocked:
411
+ self.state.blocked_count += 1
412
+ elif result.status_code == 200:
413
+ self.state.success_count += 1
414
+
415
+ self.state.last_request_time = datetime.utcnow()
416
+
417
+ def _record_defense(self, defense: DefenseDetection):
418
+ """Record a detected defense."""
419
+ self.state.detected_defenses.append(defense)
420
+
421
+ def on_waf_detected(self, handler: Callable[[DefenseDetection], None]):
422
+ """Register handler for WAF detection."""
423
+ self._register_handler(DefenseType.WAF, handler)
424
+
425
+ def on_rate_limit(self, handler: Callable[[DefenseDetection], None]):
426
+ """Register handler for rate limit detection."""
427
+ self._register_handler(DefenseType.RATE_LIMIT, handler)
428
+
429
+ def on_ip_block(self, handler: Callable[[DefenseDetection], None]):
430
+ """Register handler for IP block detection."""
431
+ self._register_handler(DefenseType.IP_BLOCK, handler)
432
+
433
+ def _register_handler(self, defense_type: DefenseType, handler: Callable):
434
+ """Register a handler for a defense type."""
435
+ if defense_type not in self._handlers:
436
+ self._handlers[defense_type] = []
437
+ self._handlers[defense_type].append(handler)
438
+
439
+ def _trigger_handlers(self, defense: DefenseDetection):
440
+ """Trigger handlers for a detected defense."""
441
+ handlers = self._handlers.get(defense.defense_type, [])
442
+ for handler in handlers:
443
+ try:
444
+ handler(defense)
445
+ except Exception as e:
446
+ logger.warning(f"Handler error: {e}")
447
+
448
+ def get_current_delay(self) -> int:
449
+ """Get the current recommended delay in milliseconds."""
450
+ return self.state.current_delay_ms
451
+
452
+ def get_statistics(self) -> dict[str, Any]:
453
+ """Get adaptation statistics."""
454
+ return {
455
+ "total_requests": self.state.request_count,
456
+ "blocked_requests": self.state.blocked_count,
457
+ "successful_requests": self.state.success_count,
458
+ "block_rate": self.state.blocked_count / max(1, self.state.request_count),
459
+ "current_delay_ms": self.state.current_delay_ms,
460
+ "is_rate_limited": self.state.is_rate_limited,
461
+ "detected_defenses": [
462
+ {
463
+ "type": d.defense_type.value,
464
+ "confidence": d.confidence,
465
+ "evidence": d.evidence,
466
+ }
467
+ for d in self.state.detected_defenses[-10:]
468
+ ],
469
+ }
470
+
471
+ def reset(self):
472
+ """Reset adaptation state."""
473
+ self.state = AdaptationState(current_delay_ms=self.base_delay_ms)
474
+ self._recent_responses.clear()