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,344 @@
1
+ from __future__ import annotations
2
+
3
+ import atexit
4
+ import contextlib
5
+ import signal
6
+ import sys
7
+ import threading
8
+ from typing import Any
9
+
10
+ from .browser_instance import BrowserInstance
11
+
12
+
13
+ class BrowserTabManager:
14
+ def __init__(self) -> None:
15
+ self.browser_instance: BrowserInstance | None = None
16
+ self._lock = threading.Lock()
17
+
18
+ self._register_cleanup_handlers()
19
+
20
+ def launch_browser(self, url: str | None = None) -> dict[str, Any]:
21
+ with self._lock:
22
+ if self.browser_instance is not None:
23
+ raise ValueError("Browser is already launched")
24
+
25
+ try:
26
+ self.browser_instance = BrowserInstance()
27
+ result = self.browser_instance.launch(url)
28
+ result["message"] = "Browser launched successfully"
29
+ except (OSError, ValueError, RuntimeError) as e:
30
+ if self.browser_instance:
31
+ self.browser_instance = None
32
+ raise RuntimeError(f"Failed to launch browser: {e}") from e
33
+ else:
34
+ return result
35
+
36
+ def goto_url(self, url: str, tab_id: str | None = None) -> dict[str, Any]:
37
+ with self._lock:
38
+ if self.browser_instance is None:
39
+ raise ValueError("Browser not launched")
40
+
41
+ try:
42
+ result = self.browser_instance.goto(url, tab_id)
43
+ result["message"] = f"Navigated to {url}"
44
+ except (OSError, ValueError, RuntimeError) as e:
45
+ raise RuntimeError(f"Failed to navigate to URL: {e}") from e
46
+ else:
47
+ return result
48
+
49
+ def click(self, coordinate: str, tab_id: str | None = None) -> dict[str, Any]:
50
+ with self._lock:
51
+ if self.browser_instance is None:
52
+ raise ValueError("Browser not launched")
53
+
54
+ try:
55
+ result = self.browser_instance.click(coordinate, tab_id)
56
+ result["message"] = f"Clicked at {coordinate}"
57
+ except (OSError, ValueError, RuntimeError) as e:
58
+ raise RuntimeError(f"Failed to click: {e}") from e
59
+ else:
60
+ return result
61
+
62
+ def type_text(self, text: str, tab_id: str | None = None) -> dict[str, Any]:
63
+ with self._lock:
64
+ if self.browser_instance is None:
65
+ raise ValueError("Browser not launched")
66
+
67
+ try:
68
+ result = self.browser_instance.type_text(text, tab_id)
69
+ result["message"] = f"Typed text: {text[:50]}{'...' if len(text) > 50 else ''}"
70
+ except (OSError, ValueError, RuntimeError) as e:
71
+ raise RuntimeError(f"Failed to type text: {e}") from e
72
+ else:
73
+ return result
74
+
75
+ def scroll(self, direction: str, tab_id: str | None = None) -> dict[str, Any]:
76
+ with self._lock:
77
+ if self.browser_instance is None:
78
+ raise ValueError("Browser not launched")
79
+
80
+ try:
81
+ result = self.browser_instance.scroll(direction, tab_id)
82
+ result["message"] = f"Scrolled {direction}"
83
+ except (OSError, ValueError, RuntimeError) as e:
84
+ raise RuntimeError(f"Failed to scroll: {e}") from e
85
+ else:
86
+ return result
87
+
88
+ def back(self, tab_id: str | None = None) -> dict[str, Any]:
89
+ with self._lock:
90
+ if self.browser_instance is None:
91
+ raise ValueError("Browser not launched")
92
+
93
+ try:
94
+ result = self.browser_instance.back(tab_id)
95
+ result["message"] = "Navigated back"
96
+ except (OSError, ValueError, RuntimeError) as e:
97
+ raise RuntimeError(f"Failed to go back: {e}") from e
98
+ else:
99
+ return result
100
+
101
+ def forward(self, tab_id: str | None = None) -> dict[str, Any]:
102
+ with self._lock:
103
+ if self.browser_instance is None:
104
+ raise ValueError("Browser not launched")
105
+
106
+ try:
107
+ result = self.browser_instance.forward(tab_id)
108
+ result["message"] = "Navigated forward"
109
+ except (OSError, ValueError, RuntimeError) as e:
110
+ raise RuntimeError(f"Failed to go forward: {e}") from e
111
+ else:
112
+ return result
113
+
114
+ def new_tab(self, url: str | None = None) -> dict[str, Any]:
115
+ with self._lock:
116
+ if self.browser_instance is None:
117
+ raise ValueError("Browser not launched")
118
+
119
+ try:
120
+ result = self.browser_instance.new_tab(url)
121
+ result["message"] = f"Created new tab {result.get('tab_id', '')}"
122
+ except (OSError, ValueError, RuntimeError) as e:
123
+ raise RuntimeError(f"Failed to create new tab: {e}") from e
124
+ else:
125
+ return result
126
+
127
+ def switch_tab(self, tab_id: str) -> dict[str, Any]:
128
+ with self._lock:
129
+ if self.browser_instance is None:
130
+ raise ValueError("Browser not launched")
131
+
132
+ try:
133
+ result = self.browser_instance.switch_tab(tab_id)
134
+ result["message"] = f"Switched to tab {tab_id}"
135
+ except (OSError, ValueError, RuntimeError) as e:
136
+ raise RuntimeError(f"Failed to switch tab: {e}") from e
137
+ else:
138
+ return result
139
+
140
+ def close_tab(self, tab_id: str) -> dict[str, Any]:
141
+ with self._lock:
142
+ if self.browser_instance is None:
143
+ raise ValueError("Browser not launched")
144
+
145
+ try:
146
+ result = self.browser_instance.close_tab(tab_id)
147
+ result["message"] = f"Closed tab {tab_id}"
148
+ except (OSError, ValueError, RuntimeError) as e:
149
+ raise RuntimeError(f"Failed to close tab: {e}") from e
150
+ else:
151
+ return result
152
+
153
+ def wait_browser(self, duration: float, tab_id: str | None = None) -> dict[str, Any]:
154
+ with self._lock:
155
+ if self.browser_instance is None:
156
+ raise ValueError("Browser not launched")
157
+
158
+ try:
159
+ result = self.browser_instance.wait(duration, tab_id)
160
+ result["message"] = f"Waited {duration}s"
161
+ except (OSError, ValueError, RuntimeError) as e:
162
+ raise RuntimeError(f"Failed to wait: {e}") from e
163
+ else:
164
+ return result
165
+
166
+ def execute_js(self, js_code: str, tab_id: str | None = None) -> dict[str, Any]:
167
+ with self._lock:
168
+ if self.browser_instance is None:
169
+ raise ValueError("Browser not launched")
170
+
171
+ try:
172
+ result = self.browser_instance.execute_js(js_code, tab_id)
173
+ result["message"] = "JavaScript executed successfully"
174
+ except (OSError, ValueError, RuntimeError) as e:
175
+ raise RuntimeError(f"Failed to execute JavaScript: {e}") from e
176
+ else:
177
+ return result
178
+
179
+ def double_click(self, coordinate: str, tab_id: str | None = None) -> dict[str, Any]:
180
+ with self._lock:
181
+ if self.browser_instance is None:
182
+ raise ValueError("Browser not launched")
183
+
184
+ try:
185
+ result = self.browser_instance.double_click(coordinate, tab_id)
186
+ result["message"] = f"Double clicked at {coordinate}"
187
+ except (OSError, ValueError, RuntimeError) as e:
188
+ raise RuntimeError(f"Failed to double click: {e}") from e
189
+ else:
190
+ return result
191
+
192
+ def hover(self, coordinate: str, tab_id: str | None = None) -> dict[str, Any]:
193
+ with self._lock:
194
+ if self.browser_instance is None:
195
+ raise ValueError("Browser not launched")
196
+
197
+ try:
198
+ result = self.browser_instance.hover(coordinate, tab_id)
199
+ result["message"] = f"Hovered at {coordinate}"
200
+ except (OSError, ValueError, RuntimeError) as e:
201
+ raise RuntimeError(f"Failed to hover: {e}") from e
202
+ else:
203
+ return result
204
+
205
+ def press_key(self, key: str, tab_id: str | None = None) -> dict[str, Any]:
206
+ with self._lock:
207
+ if self.browser_instance is None:
208
+ raise ValueError("Browser not launched")
209
+
210
+ try:
211
+ result = self.browser_instance.press_key(key, tab_id)
212
+ result["message"] = f"Pressed key {key}"
213
+ except (OSError, ValueError, RuntimeError) as e:
214
+ raise RuntimeError(f"Failed to press key: {e}") from e
215
+ else:
216
+ return result
217
+
218
+ def save_pdf(self, file_path: str, tab_id: str | None = None) -> dict[str, Any]:
219
+ with self._lock:
220
+ if self.browser_instance is None:
221
+ raise ValueError("Browser not launched")
222
+
223
+ try:
224
+ result = self.browser_instance.save_pdf(file_path, tab_id)
225
+ result["message"] = f"Page saved as PDF: {file_path}"
226
+ except (OSError, ValueError, RuntimeError) as e:
227
+ raise RuntimeError(f"Failed to save PDF: {e}") from e
228
+ else:
229
+ return result
230
+
231
+ def get_console_logs(self, tab_id: str | None = None, clear: bool = False) -> dict[str, Any]:
232
+ with self._lock:
233
+ if self.browser_instance is None:
234
+ raise ValueError("Browser not launched")
235
+
236
+ try:
237
+ result = self.browser_instance.get_console_logs(tab_id, clear)
238
+ action_text = "cleared and retrieved" if clear else "retrieved"
239
+
240
+ logs = result.get("console_logs", [])
241
+ truncated = any(log.get("text", "").startswith("[TRUNCATED:") for log in logs)
242
+ truncated_text = " (truncated)" if truncated else ""
243
+
244
+ result["message"] = (
245
+ f"Console logs {action_text} for tab "
246
+ f"{result.get('tab_id', 'current')}{truncated_text}"
247
+ )
248
+ except (OSError, ValueError, RuntimeError) as e:
249
+ raise RuntimeError(f"Failed to get console logs: {e}") from e
250
+ else:
251
+ return result
252
+
253
+ def view_source(self, tab_id: str | None = None) -> dict[str, Any]:
254
+ with self._lock:
255
+ if self.browser_instance is None:
256
+ raise ValueError("Browser not launched")
257
+
258
+ try:
259
+ result = self.browser_instance.view_source(tab_id)
260
+ result["message"] = "Page source retrieved"
261
+ except (OSError, ValueError, RuntimeError) as e:
262
+ raise RuntimeError(f"Failed to get page source: {e}") from e
263
+ else:
264
+ return result
265
+
266
+ def list_tabs(self) -> dict[str, Any]:
267
+ with self._lock:
268
+ if self.browser_instance is None:
269
+ return {"tabs": {}, "total_count": 0, "current_tab": None}
270
+
271
+ try:
272
+ tab_info = {}
273
+ for tid, tab_page in self.browser_instance.pages.items():
274
+ try:
275
+ tab_info[tid] = {
276
+ "url": tab_page.url,
277
+ "title": "Unknown" if tab_page.is_closed() else "Active",
278
+ "is_current": tid == self.browser_instance.current_page_id,
279
+ }
280
+ except (AttributeError, RuntimeError):
281
+ tab_info[tid] = {
282
+ "url": "Unknown",
283
+ "title": "Closed",
284
+ "is_current": False,
285
+ }
286
+
287
+ return {
288
+ "tabs": tab_info,
289
+ "total_count": len(tab_info),
290
+ "current_tab": self.browser_instance.current_page_id,
291
+ }
292
+ except (OSError, ValueError, RuntimeError) as e:
293
+ raise RuntimeError(f"Failed to list tabs: {e}") from e
294
+
295
+ def close_browser(self) -> dict[str, Any]:
296
+ with self._lock:
297
+ if self.browser_instance is None:
298
+ raise ValueError("Browser not launched")
299
+
300
+ try:
301
+ self.browser_instance.close()
302
+ self.browser_instance = None
303
+ except (OSError, ValueError, RuntimeError) as e:
304
+ raise RuntimeError(f"Failed to close browser: {e}") from e
305
+ else:
306
+ return {
307
+ "message": "Browser closed successfully",
308
+ "screenshot": "",
309
+ "is_running": False,
310
+ }
311
+
312
+ def cleanup_dead_browser(self) -> None:
313
+ with self._lock:
314
+ if self.browser_instance and not self.browser_instance.is_alive():
315
+ with contextlib.suppress(Exception):
316
+ self.browser_instance.close()
317
+ self.browser_instance = None
318
+
319
+ def close_all(self) -> None:
320
+ with self._lock:
321
+ if self.browser_instance:
322
+ with contextlib.suppress(Exception):
323
+ self.browser_instance.close()
324
+ self.browser_instance = None
325
+
326
+ def _register_cleanup_handlers(self) -> None:
327
+ atexit.register(self.close_all)
328
+
329
+ signal.signal(signal.SIGTERM, self._signal_handler)
330
+ signal.signal(signal.SIGINT, self._signal_handler)
331
+
332
+ if hasattr(signal, "SIGHUP"):
333
+ signal.signal(signal.SIGHUP, self._signal_handler)
334
+
335
+ def _signal_handler(self, _signum: int, _frame: Any) -> None:
336
+ self.close_all()
337
+ sys.exit(0)
338
+
339
+
340
+ _browser_tab_manager = BrowserTabManager()
341
+
342
+
343
+ def get_browser_tab_manager() -> BrowserTabManager:
344
+ return _browser_tab_manager
@@ -0,0 +1,70 @@
1
+ """
2
+ AIPT Cloud Security Module - Multi-Cloud Vulnerability Scanning
3
+
4
+ Provides comprehensive cloud security assessment for:
5
+ - AWS (Amazon Web Services)
6
+ - Azure (Microsoft Azure)
7
+ - GCP (Google Cloud Platform)
8
+
9
+ Tools integrated:
10
+ - ScoutSuite: Multi-cloud security auditing
11
+ - Prowler: AWS security best practices
12
+ - Custom checks: IAM, S3, Security Groups, etc.
13
+
14
+ Usage:
15
+ from aipt_v2.tools.cloud import CloudScanner, get_cloud_scanner
16
+
17
+ scanner = get_cloud_scanner(provider="aws", profile="default")
18
+ findings = await scanner.scan()
19
+ """
20
+
21
+ from aipt_v2.tools.cloud.cloud_config import (
22
+ CloudConfig,
23
+ AWSConfig,
24
+ AzureConfig,
25
+ GCPConfig,
26
+ get_cloud_config,
27
+ )
28
+
29
+ from aipt_v2.tools.cloud.cloud_scanner import (
30
+ CloudScanner,
31
+ CloudFinding,
32
+ CloudSeverity,
33
+ get_cloud_scanner,
34
+ scan_cloud,
35
+ )
36
+
37
+ from aipt_v2.tools.cloud.scoutsuite_tool import (
38
+ ScoutSuiteTool,
39
+ ScoutSuiteConfig,
40
+ run_scoutsuite,
41
+ )
42
+
43
+ from aipt_v2.tools.cloud.prowler_tool import (
44
+ ProwlerTool,
45
+ ProwlerConfig,
46
+ run_prowler,
47
+ )
48
+
49
+ __all__ = [
50
+ # Configuration
51
+ "CloudConfig",
52
+ "AWSConfig",
53
+ "AzureConfig",
54
+ "GCPConfig",
55
+ "get_cloud_config",
56
+ # Scanner
57
+ "CloudScanner",
58
+ "CloudFinding",
59
+ "CloudSeverity",
60
+ "get_cloud_scanner",
61
+ "scan_cloud",
62
+ # ScoutSuite
63
+ "ScoutSuiteTool",
64
+ "ScoutSuiteConfig",
65
+ "run_scoutsuite",
66
+ # Prowler
67
+ "ProwlerTool",
68
+ "ProwlerConfig",
69
+ "run_prowler",
70
+ ]
@@ -0,0 +1,273 @@
1
+ """
2
+ Cloud Configuration Management
3
+
4
+ Handles credentials and configuration for multi-cloud security scanning.
5
+ Supports AWS profiles, Azure subscriptions, and GCP projects.
6
+ """
7
+
8
+ import os
9
+ from dataclasses import dataclass, field
10
+ from typing import Optional, List, Dict, Any
11
+ from pathlib import Path
12
+
13
+
14
+ @dataclass
15
+ class AWSConfig:
16
+ """AWS-specific configuration."""
17
+ profile: str = "default"
18
+ region: str = "us-east-1"
19
+ access_key_id: Optional[str] = None
20
+ secret_access_key: Optional[str] = None
21
+ session_token: Optional[str] = None
22
+
23
+ # Scanning options
24
+ services: List[str] = field(default_factory=lambda: [
25
+ "iam", "s3", "ec2", "rds", "lambda", "cloudtrail",
26
+ "cloudwatch", "kms", "sns", "sqs", "vpc", "elb"
27
+ ])
28
+ skip_services: List[str] = field(default_factory=list)
29
+
30
+ def __post_init__(self):
31
+ # Load from environment if not provided
32
+ if not self.access_key_id:
33
+ self.access_key_id = os.getenv("AWS_ACCESS_KEY_ID")
34
+ if not self.secret_access_key:
35
+ self.secret_access_key = os.getenv("AWS_SECRET_ACCESS_KEY")
36
+ if not self.session_token:
37
+ self.session_token = os.getenv("AWS_SESSION_TOKEN")
38
+ if self.region == "us-east-1":
39
+ self.region = os.getenv("AWS_DEFAULT_REGION", "us-east-1")
40
+
41
+ def is_configured(self) -> bool:
42
+ """Check if AWS credentials are available."""
43
+ # Either profile-based or key-based auth
44
+ if self.profile:
45
+ # Check if credentials file exists
46
+ creds_file = Path.home() / ".aws" / "credentials"
47
+ return creds_file.exists()
48
+ return bool(self.access_key_id and self.secret_access_key)
49
+
50
+ def to_env_dict(self) -> Dict[str, str]:
51
+ """Convert to environment variables dict."""
52
+ env = {"AWS_DEFAULT_REGION": self.region}
53
+ if self.profile:
54
+ env["AWS_PROFILE"] = self.profile
55
+ if self.access_key_id:
56
+ env["AWS_ACCESS_KEY_ID"] = self.access_key_id
57
+ if self.secret_access_key:
58
+ env["AWS_SECRET_ACCESS_KEY"] = self.secret_access_key
59
+ if self.session_token:
60
+ env["AWS_SESSION_TOKEN"] = self.session_token
61
+ return env
62
+
63
+
64
+ @dataclass
65
+ class AzureConfig:
66
+ """Azure-specific configuration."""
67
+ subscription_id: Optional[str] = None
68
+ tenant_id: Optional[str] = None
69
+ client_id: Optional[str] = None
70
+ client_secret: Optional[str] = None
71
+
72
+ # Use Azure CLI auth by default
73
+ use_cli_auth: bool = True
74
+
75
+ # Scanning options
76
+ resource_groups: List[str] = field(default_factory=list) # Empty = all
77
+
78
+ def __post_init__(self):
79
+ # Load from environment if not provided
80
+ if not self.subscription_id:
81
+ self.subscription_id = os.getenv("AZURE_SUBSCRIPTION_ID")
82
+ if not self.tenant_id:
83
+ self.tenant_id = os.getenv("AZURE_TENANT_ID")
84
+ if not self.client_id:
85
+ self.client_id = os.getenv("AZURE_CLIENT_ID")
86
+ if not self.client_secret:
87
+ self.client_secret = os.getenv("AZURE_CLIENT_SECRET")
88
+
89
+ def is_configured(self) -> bool:
90
+ """Check if Azure credentials are available."""
91
+ if self.use_cli_auth:
92
+ # Check for Azure CLI
93
+ return Path.home().joinpath(".azure").exists()
94
+ return bool(self.subscription_id and self.tenant_id and
95
+ self.client_id and self.client_secret)
96
+
97
+ def to_env_dict(self) -> Dict[str, str]:
98
+ """Convert to environment variables dict."""
99
+ env = {}
100
+ if self.subscription_id:
101
+ env["AZURE_SUBSCRIPTION_ID"] = self.subscription_id
102
+ if self.tenant_id:
103
+ env["AZURE_TENANT_ID"] = self.tenant_id
104
+ if self.client_id:
105
+ env["AZURE_CLIENT_ID"] = self.client_id
106
+ if self.client_secret:
107
+ env["AZURE_CLIENT_SECRET"] = self.client_secret
108
+ return env
109
+
110
+
111
+ @dataclass
112
+ class GCPConfig:
113
+ """GCP-specific configuration."""
114
+ project_id: Optional[str] = None
115
+ credentials_file: Optional[str] = None
116
+
117
+ # Use application default credentials
118
+ use_adc: bool = True
119
+
120
+ # Scanning options
121
+ regions: List[str] = field(default_factory=list) # Empty = all
122
+
123
+ def __post_init__(self):
124
+ # Load from environment if not provided
125
+ if not self.project_id:
126
+ self.project_id = os.getenv("GOOGLE_CLOUD_PROJECT") or os.getenv("GCP_PROJECT")
127
+ if not self.credentials_file:
128
+ self.credentials_file = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
129
+
130
+ def is_configured(self) -> bool:
131
+ """Check if GCP credentials are available."""
132
+ if self.use_adc:
133
+ # Check for application default credentials
134
+ adc_path = Path.home() / ".config" / "gcloud" / "application_default_credentials.json"
135
+ return adc_path.exists() or bool(self.credentials_file)
136
+ return bool(self.project_id and self.credentials_file)
137
+
138
+ def to_env_dict(self) -> Dict[str, str]:
139
+ """Convert to environment variables dict."""
140
+ env = {}
141
+ if self.project_id:
142
+ env["GOOGLE_CLOUD_PROJECT"] = self.project_id
143
+ if self.credentials_file:
144
+ env["GOOGLE_APPLICATION_CREDENTIALS"] = self.credentials_file
145
+ return env
146
+
147
+
148
+ @dataclass
149
+ class CloudConfig:
150
+ """Unified cloud configuration."""
151
+
152
+ # Provider configs
153
+ aws: AWSConfig = field(default_factory=AWSConfig)
154
+ azure: AzureConfig = field(default_factory=AzureConfig)
155
+ gcp: GCPConfig = field(default_factory=GCPConfig)
156
+
157
+ # General settings
158
+ providers: List[str] = field(default_factory=lambda: ["aws"])
159
+ output_dir: str = "./cloud_scan_results"
160
+ severity_threshold: str = "low" # low, medium, high, critical
161
+
162
+ # Scanning options
163
+ parallel_scans: bool = True
164
+ max_workers: int = 5
165
+ timeout: int = 3600 # 1 hour default
166
+
167
+ def get_configured_providers(self) -> List[str]:
168
+ """Get list of providers with valid credentials."""
169
+ configured = []
170
+ if "aws" in self.providers and self.aws.is_configured():
171
+ configured.append("aws")
172
+ if "azure" in self.providers and self.azure.is_configured():
173
+ configured.append("azure")
174
+ if "gcp" in self.providers and self.gcp.is_configured():
175
+ configured.append("gcp")
176
+ return configured
177
+
178
+ def get_provider_config(self, provider: str) -> Any:
179
+ """Get configuration for a specific provider."""
180
+ provider_map = {
181
+ "aws": self.aws,
182
+ "azure": self.azure,
183
+ "gcp": self.gcp
184
+ }
185
+ return provider_map.get(provider.lower())
186
+
187
+
188
+ def get_cloud_config(
189
+ providers: Optional[List[str]] = None,
190
+ aws_profile: Optional[str] = None,
191
+ azure_subscription: Optional[str] = None,
192
+ gcp_project: Optional[str] = None,
193
+ **kwargs
194
+ ) -> CloudConfig:
195
+ """
196
+ Create CloudConfig from parameters and environment.
197
+
198
+ Args:
199
+ providers: List of cloud providers to scan ("aws", "azure", "gcp")
200
+ aws_profile: AWS CLI profile name
201
+ azure_subscription: Azure subscription ID
202
+ gcp_project: GCP project ID
203
+ **kwargs: Additional configuration options
204
+
205
+ Returns:
206
+ CloudConfig instance
207
+ """
208
+ # Build AWS config
209
+ aws_config = AWSConfig(
210
+ profile=aws_profile or os.getenv("AWS_PROFILE", "default"),
211
+ region=kwargs.get("aws_region", os.getenv("AWS_DEFAULT_REGION", "us-east-1"))
212
+ )
213
+
214
+ # Build Azure config
215
+ azure_config = AzureConfig(
216
+ subscription_id=azure_subscription or os.getenv("AZURE_SUBSCRIPTION_ID")
217
+ )
218
+
219
+ # Build GCP config
220
+ gcp_config = GCPConfig(
221
+ project_id=gcp_project or os.getenv("GOOGLE_CLOUD_PROJECT")
222
+ )
223
+
224
+ # Determine providers
225
+ if providers is None:
226
+ providers = ["aws"] # Default to AWS only
227
+
228
+ return CloudConfig(
229
+ aws=aws_config,
230
+ azure=azure_config,
231
+ gcp=gcp_config,
232
+ providers=providers,
233
+ output_dir=kwargs.get("output_dir", "./cloud_scan_results"),
234
+ severity_threshold=kwargs.get("severity_threshold", "low"),
235
+ timeout=kwargs.get("timeout", 3600)
236
+ )
237
+
238
+
239
+ def validate_cloud_credentials() -> Dict[str, Dict[str, Any]]:
240
+ """
241
+ Validate credentials for all cloud providers.
242
+
243
+ Returns:
244
+ Dict with validation results per provider
245
+ """
246
+ results = {}
247
+
248
+ # Check AWS
249
+ aws_config = AWSConfig()
250
+ results["aws"] = {
251
+ "configured": aws_config.is_configured(),
252
+ "profile": aws_config.profile,
253
+ "region": aws_config.region,
254
+ "has_keys": bool(aws_config.access_key_id)
255
+ }
256
+
257
+ # Check Azure
258
+ azure_config = AzureConfig()
259
+ results["azure"] = {
260
+ "configured": azure_config.is_configured(),
261
+ "subscription": azure_config.subscription_id,
262
+ "use_cli": azure_config.use_cli_auth
263
+ }
264
+
265
+ # Check GCP
266
+ gcp_config = GCPConfig()
267
+ results["gcp"] = {
268
+ "configured": gcp_config.is_configured(),
269
+ "project": gcp_config.project_id,
270
+ "use_adc": gcp_config.use_adc
271
+ }
272
+
273
+ return results