aiptx 2.0.2__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.

Potentially problematic release.


This version of aiptx might be problematic. Click here for more details.

Files changed (165) 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 +24 -0
  6. aipt_v2/agents/base.py +520 -0
  7. aipt_v2/agents/ptt.py +406 -0
  8. aipt_v2/agents/state.py +168 -0
  9. aipt_v2/app.py +960 -0
  10. aipt_v2/browser/__init__.py +31 -0
  11. aipt_v2/browser/automation.py +458 -0
  12. aipt_v2/browser/crawler.py +453 -0
  13. aipt_v2/cli.py +321 -0
  14. aipt_v2/compliance/__init__.py +71 -0
  15. aipt_v2/compliance/compliance_report.py +449 -0
  16. aipt_v2/compliance/framework_mapper.py +424 -0
  17. aipt_v2/compliance/nist_mapping.py +345 -0
  18. aipt_v2/compliance/owasp_mapping.py +330 -0
  19. aipt_v2/compliance/pci_mapping.py +297 -0
  20. aipt_v2/config.py +288 -0
  21. aipt_v2/core/__init__.py +43 -0
  22. aipt_v2/core/agent.py +630 -0
  23. aipt_v2/core/llm.py +395 -0
  24. aipt_v2/core/memory.py +305 -0
  25. aipt_v2/core/ptt.py +329 -0
  26. aipt_v2/database/__init__.py +14 -0
  27. aipt_v2/database/models.py +232 -0
  28. aipt_v2/database/repository.py +384 -0
  29. aipt_v2/docker/__init__.py +23 -0
  30. aipt_v2/docker/builder.py +260 -0
  31. aipt_v2/docker/manager.py +222 -0
  32. aipt_v2/docker/sandbox.py +371 -0
  33. aipt_v2/evasion/__init__.py +58 -0
  34. aipt_v2/evasion/request_obfuscator.py +272 -0
  35. aipt_v2/evasion/tls_fingerprint.py +285 -0
  36. aipt_v2/evasion/ua_rotator.py +301 -0
  37. aipt_v2/evasion/waf_bypass.py +439 -0
  38. aipt_v2/execution/__init__.py +23 -0
  39. aipt_v2/execution/executor.py +302 -0
  40. aipt_v2/execution/parser.py +544 -0
  41. aipt_v2/execution/terminal.py +337 -0
  42. aipt_v2/health.py +437 -0
  43. aipt_v2/intelligence/__init__.py +85 -0
  44. aipt_v2/intelligence/auth.py +520 -0
  45. aipt_v2/intelligence/chaining.py +775 -0
  46. aipt_v2/intelligence/cve_aipt.py +334 -0
  47. aipt_v2/intelligence/cve_info.py +1111 -0
  48. aipt_v2/intelligence/rag.py +239 -0
  49. aipt_v2/intelligence/scope.py +442 -0
  50. aipt_v2/intelligence/searchers/__init__.py +5 -0
  51. aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
  52. aipt_v2/intelligence/searchers/github_searcher.py +467 -0
  53. aipt_v2/intelligence/searchers/google_searcher.py +281 -0
  54. aipt_v2/intelligence/tools.json +443 -0
  55. aipt_v2/intelligence/triage.py +670 -0
  56. aipt_v2/interface/__init__.py +5 -0
  57. aipt_v2/interface/cli.py +230 -0
  58. aipt_v2/interface/main.py +501 -0
  59. aipt_v2/interface/tui.py +1276 -0
  60. aipt_v2/interface/utils.py +583 -0
  61. aipt_v2/llm/__init__.py +39 -0
  62. aipt_v2/llm/config.py +26 -0
  63. aipt_v2/llm/llm.py +514 -0
  64. aipt_v2/llm/memory.py +214 -0
  65. aipt_v2/llm/request_queue.py +89 -0
  66. aipt_v2/llm/utils.py +89 -0
  67. aipt_v2/models/__init__.py +15 -0
  68. aipt_v2/models/findings.py +295 -0
  69. aipt_v2/models/phase_result.py +224 -0
  70. aipt_v2/models/scan_config.py +207 -0
  71. aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
  72. aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
  73. aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
  74. aipt_v2/monitoring/prometheus.yml +60 -0
  75. aipt_v2/orchestration/__init__.py +52 -0
  76. aipt_v2/orchestration/pipeline.py +398 -0
  77. aipt_v2/orchestration/progress.py +300 -0
  78. aipt_v2/orchestration/scheduler.py +296 -0
  79. aipt_v2/orchestrator.py +2284 -0
  80. aipt_v2/payloads/__init__.py +27 -0
  81. aipt_v2/payloads/cmdi.py +150 -0
  82. aipt_v2/payloads/sqli.py +263 -0
  83. aipt_v2/payloads/ssrf.py +204 -0
  84. aipt_v2/payloads/templates.py +222 -0
  85. aipt_v2/payloads/traversal.py +166 -0
  86. aipt_v2/payloads/xss.py +204 -0
  87. aipt_v2/prompts/__init__.py +60 -0
  88. aipt_v2/proxy/__init__.py +29 -0
  89. aipt_v2/proxy/history.py +352 -0
  90. aipt_v2/proxy/interceptor.py +452 -0
  91. aipt_v2/recon/__init__.py +44 -0
  92. aipt_v2/recon/dns.py +241 -0
  93. aipt_v2/recon/osint.py +367 -0
  94. aipt_v2/recon/subdomain.py +372 -0
  95. aipt_v2/recon/tech_detect.py +311 -0
  96. aipt_v2/reports/__init__.py +17 -0
  97. aipt_v2/reports/generator.py +313 -0
  98. aipt_v2/reports/html_report.py +378 -0
  99. aipt_v2/runtime/__init__.py +44 -0
  100. aipt_v2/runtime/base.py +30 -0
  101. aipt_v2/runtime/docker.py +401 -0
  102. aipt_v2/runtime/local.py +346 -0
  103. aipt_v2/runtime/tool_server.py +205 -0
  104. aipt_v2/scanners/__init__.py +28 -0
  105. aipt_v2/scanners/base.py +273 -0
  106. aipt_v2/scanners/nikto.py +244 -0
  107. aipt_v2/scanners/nmap.py +402 -0
  108. aipt_v2/scanners/nuclei.py +273 -0
  109. aipt_v2/scanners/web.py +454 -0
  110. aipt_v2/scripts/security_audit.py +366 -0
  111. aipt_v2/telemetry/__init__.py +7 -0
  112. aipt_v2/telemetry/tracer.py +347 -0
  113. aipt_v2/terminal/__init__.py +28 -0
  114. aipt_v2/terminal/executor.py +400 -0
  115. aipt_v2/terminal/sandbox.py +350 -0
  116. aipt_v2/tools/__init__.py +44 -0
  117. aipt_v2/tools/active_directory/__init__.py +78 -0
  118. aipt_v2/tools/active_directory/ad_config.py +238 -0
  119. aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
  120. aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
  121. aipt_v2/tools/active_directory/ldap_enum.py +533 -0
  122. aipt_v2/tools/active_directory/smb_attacks.py +505 -0
  123. aipt_v2/tools/agents_graph/__init__.py +19 -0
  124. aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
  125. aipt_v2/tools/api_security/__init__.py +76 -0
  126. aipt_v2/tools/api_security/api_discovery.py +608 -0
  127. aipt_v2/tools/api_security/graphql_scanner.py +622 -0
  128. aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
  129. aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
  130. aipt_v2/tools/browser/__init__.py +5 -0
  131. aipt_v2/tools/browser/browser_actions.py +238 -0
  132. aipt_v2/tools/browser/browser_instance.py +535 -0
  133. aipt_v2/tools/browser/tab_manager.py +344 -0
  134. aipt_v2/tools/cloud/__init__.py +70 -0
  135. aipt_v2/tools/cloud/cloud_config.py +273 -0
  136. aipt_v2/tools/cloud/cloud_scanner.py +639 -0
  137. aipt_v2/tools/cloud/prowler_tool.py +571 -0
  138. aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
  139. aipt_v2/tools/executor.py +307 -0
  140. aipt_v2/tools/parser.py +408 -0
  141. aipt_v2/tools/proxy/__init__.py +5 -0
  142. aipt_v2/tools/proxy/proxy_actions.py +103 -0
  143. aipt_v2/tools/proxy/proxy_manager.py +789 -0
  144. aipt_v2/tools/registry.py +196 -0
  145. aipt_v2/tools/scanners/__init__.py +343 -0
  146. aipt_v2/tools/scanners/acunetix_tool.py +712 -0
  147. aipt_v2/tools/scanners/burp_tool.py +631 -0
  148. aipt_v2/tools/scanners/config.py +156 -0
  149. aipt_v2/tools/scanners/nessus_tool.py +588 -0
  150. aipt_v2/tools/scanners/zap_tool.py +612 -0
  151. aipt_v2/tools/terminal/__init__.py +5 -0
  152. aipt_v2/tools/terminal/terminal_actions.py +37 -0
  153. aipt_v2/tools/terminal/terminal_manager.py +153 -0
  154. aipt_v2/tools/terminal/terminal_session.py +449 -0
  155. aipt_v2/tools/tool_processing.py +108 -0
  156. aipt_v2/utils/__init__.py +17 -0
  157. aipt_v2/utils/logging.py +201 -0
  158. aipt_v2/utils/model_manager.py +187 -0
  159. aipt_v2/utils/searchers/__init__.py +269 -0
  160. aiptx-2.0.2.dist-info/METADATA +324 -0
  161. aiptx-2.0.2.dist-info/RECORD +165 -0
  162. aiptx-2.0.2.dist-info/WHEEL +5 -0
  163. aiptx-2.0.2.dist-info/entry_points.txt +7 -0
  164. aiptx-2.0.2.dist-info/licenses/LICENSE +21 -0
  165. aiptx-2.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,346 @@
1
+ """
2
+ Local Runtime for AIPT v2
3
+ =========================
4
+
5
+ Non-sandboxed local execution runtime.
6
+ Implements AbstractRuntime interface for local command execution.
7
+
8
+ WARNING: This runtime provides NO isolation. Only use for trusted commands.
9
+ """
10
+
11
+ import asyncio
12
+ import uuid
13
+ import os
14
+ from dataclasses import dataclass, field
15
+ from typing import Optional, Dict, Tuple, List
16
+ from pathlib import Path
17
+ from datetime import datetime
18
+
19
+ from aipt_v2.utils.logging import logger
20
+
21
+
22
+ @dataclass
23
+ class LocalSandboxInfo:
24
+ """Information about a local 'sandbox' (execution context)."""
25
+
26
+ sandbox_id: str
27
+ created_at: datetime = field(default_factory=datetime.now)
28
+ working_dir: str = field(default_factory=lambda: os.getcwd())
29
+ is_local: bool = True
30
+ url: str = "local"
31
+
32
+
33
+ class LocalRuntime:
34
+ """
35
+ Local runtime for command execution without sandboxing.
36
+
37
+ This runtime executes commands directly on the local system.
38
+ It provides a consistent interface with sandboxed runtimes
39
+ but does NOT provide any isolation.
40
+
41
+ Usage:
42
+ runtime = LocalRuntime()
43
+ sandbox = await runtime.create_sandbox()
44
+ stdout, stderr, code = await runtime.execute(sandbox.sandbox_id, "ls -la")
45
+ await runtime.destroy_sandbox(sandbox.sandbox_id)
46
+ """
47
+
48
+ def __init__(self, working_dir: Optional[str] = None):
49
+ """
50
+ Initialize local runtime.
51
+
52
+ Args:
53
+ working_dir: Default working directory for commands
54
+ """
55
+ self.working_dir = working_dir or os.getcwd()
56
+ self._sandboxes: Dict[str, LocalSandboxInfo] = {}
57
+ self._processes: Dict[str, asyncio.subprocess.Process] = {}
58
+
59
+ async def create_sandbox(
60
+ self,
61
+ image: Optional[str] = None,
62
+ working_dir: Optional[str] = None,
63
+ **kwargs
64
+ ) -> LocalSandboxInfo:
65
+ """
66
+ Create a local execution context.
67
+
68
+ Note: For local runtime, this just creates a tracking ID.
69
+ No actual isolation is provided.
70
+
71
+ Args:
72
+ image: Ignored for local runtime
73
+ working_dir: Working directory for commands
74
+ **kwargs: Additional options (ignored)
75
+
76
+ Returns:
77
+ LocalSandboxInfo with sandbox details
78
+ """
79
+ sandbox_id = f"local-{uuid.uuid4().hex[:8]}"
80
+ work_dir = working_dir or self.working_dir
81
+
82
+ # Ensure working directory exists
83
+ Path(work_dir).mkdir(parents=True, exist_ok=True)
84
+
85
+ info = LocalSandboxInfo(
86
+ sandbox_id=sandbox_id,
87
+ working_dir=work_dir,
88
+ )
89
+
90
+ self._sandboxes[sandbox_id] = info
91
+ logger.info("Created local execution context", sandbox_id=sandbox_id, working_dir=work_dir)
92
+
93
+ return info
94
+
95
+ async def get_sandbox_url(self, sandbox_id: str) -> str:
96
+ """
97
+ Get sandbox URL.
98
+
99
+ Args:
100
+ sandbox_id: Sandbox identifier
101
+
102
+ Returns:
103
+ URL string ('local' for local runtime)
104
+
105
+ Raises:
106
+ ValueError: If sandbox not found
107
+ """
108
+ if sandbox_id not in self._sandboxes:
109
+ raise ValueError(f"Unknown sandbox: {sandbox_id}")
110
+ return "local"
111
+
112
+ async def destroy_sandbox(self, sandbox_id: str) -> None:
113
+ """
114
+ Destroy sandbox and cleanup resources.
115
+
116
+ Args:
117
+ sandbox_id: Sandbox identifier
118
+ """
119
+ if sandbox_id in self._sandboxes:
120
+ # Kill any running processes
121
+ if sandbox_id in self._processes:
122
+ try:
123
+ self._processes[sandbox_id].kill()
124
+ await self._processes[sandbox_id].wait()
125
+ except ProcessLookupError:
126
+ pass
127
+ del self._processes[sandbox_id]
128
+
129
+ del self._sandboxes[sandbox_id]
130
+ logger.info("Destroyed local sandbox", sandbox_id=sandbox_id)
131
+
132
+ async def execute(
133
+ self,
134
+ sandbox_id: str,
135
+ command: str,
136
+ timeout: int = 300,
137
+ env: Optional[Dict[str, str]] = None,
138
+ ) -> Tuple[str, str, int]:
139
+ """
140
+ Execute command in sandbox.
141
+
142
+ Args:
143
+ sandbox_id: Sandbox identifier
144
+ command: Shell command to execute
145
+ timeout: Command timeout in seconds
146
+ env: Additional environment variables
147
+
148
+ Returns:
149
+ Tuple of (stdout, stderr, exit_code)
150
+
151
+ Raises:
152
+ ValueError: If sandbox not found
153
+ """
154
+ if sandbox_id not in self._sandboxes:
155
+ raise ValueError(f"Unknown sandbox: {sandbox_id}")
156
+
157
+ sandbox = self._sandboxes[sandbox_id]
158
+
159
+ # Prepare environment
160
+ cmd_env = os.environ.copy()
161
+ if env:
162
+ cmd_env.update(env)
163
+
164
+ logger.debug(
165
+ "Executing command",
166
+ sandbox_id=sandbox_id,
167
+ command=command[:100] + "..." if len(command) > 100 else command,
168
+ timeout=timeout,
169
+ )
170
+
171
+ try:
172
+ proc = await asyncio.create_subprocess_shell(
173
+ command,
174
+ stdout=asyncio.subprocess.PIPE,
175
+ stderr=asyncio.subprocess.PIPE,
176
+ cwd=sandbox.working_dir,
177
+ env=cmd_env,
178
+ )
179
+
180
+ self._processes[sandbox_id] = proc
181
+
182
+ try:
183
+ stdout_bytes, stderr_bytes = await asyncio.wait_for(
184
+ proc.communicate(),
185
+ timeout=timeout
186
+ )
187
+
188
+ stdout = stdout_bytes.decode("utf-8", errors="replace")
189
+ stderr = stderr_bytes.decode("utf-8", errors="replace")
190
+ exit_code = proc.returncode or 0
191
+
192
+ logger.debug(
193
+ "Command completed",
194
+ sandbox_id=sandbox_id,
195
+ exit_code=exit_code,
196
+ stdout_len=len(stdout),
197
+ stderr_len=len(stderr),
198
+ )
199
+
200
+ return stdout, stderr, exit_code
201
+
202
+ except asyncio.TimeoutError:
203
+ logger.warning(
204
+ "Command timed out",
205
+ sandbox_id=sandbox_id,
206
+ command=command[:50],
207
+ timeout=timeout,
208
+ )
209
+ proc.kill()
210
+ await proc.wait()
211
+ return "", f"Command timed out after {timeout} seconds", 124
212
+
213
+ except Exception as e:
214
+ logger.error(
215
+ "Command execution failed",
216
+ sandbox_id=sandbox_id,
217
+ command=command[:50],
218
+ error=str(e),
219
+ exc_info=True,
220
+ )
221
+ return "", str(e), 1
222
+
223
+ finally:
224
+ if sandbox_id in self._processes:
225
+ del self._processes[sandbox_id]
226
+
227
+ async def execute_background(
228
+ self,
229
+ sandbox_id: str,
230
+ command: str,
231
+ env: Optional[Dict[str, str]] = None,
232
+ ) -> str:
233
+ """
234
+ Execute command in background.
235
+
236
+ Args:
237
+ sandbox_id: Sandbox identifier
238
+ command: Shell command to execute
239
+ env: Additional environment variables
240
+
241
+ Returns:
242
+ Process ID as string
243
+ """
244
+ if sandbox_id not in self._sandboxes:
245
+ raise ValueError(f"Unknown sandbox: {sandbox_id}")
246
+
247
+ sandbox = self._sandboxes[sandbox_id]
248
+
249
+ cmd_env = os.environ.copy()
250
+ if env:
251
+ cmd_env.update(env)
252
+
253
+ proc = await asyncio.create_subprocess_shell(
254
+ command,
255
+ stdout=asyncio.subprocess.PIPE,
256
+ stderr=asyncio.subprocess.PIPE,
257
+ cwd=sandbox.working_dir,
258
+ env=cmd_env,
259
+ )
260
+
261
+ proc_id = f"{sandbox_id}-{proc.pid}"
262
+ self._processes[proc_id] = proc
263
+
264
+ logger.info(
265
+ "Started background process",
266
+ sandbox_id=sandbox_id,
267
+ proc_id=proc_id,
268
+ command=command[:50],
269
+ )
270
+
271
+ return proc_id
272
+
273
+ async def check_tool_available(self, tool_name: str) -> bool:
274
+ """
275
+ Check if a tool is available locally.
276
+
277
+ Args:
278
+ tool_name: Name of the tool to check
279
+
280
+ Returns:
281
+ True if tool is available
282
+ """
283
+ try:
284
+ proc = await asyncio.create_subprocess_shell(
285
+ f"which {tool_name}",
286
+ stdout=asyncio.subprocess.PIPE,
287
+ stderr=asyncio.subprocess.PIPE,
288
+ )
289
+ stdout, _ = await proc.communicate()
290
+ return proc.returncode == 0 and len(stdout.strip()) > 0
291
+ except Exception:
292
+ return False
293
+
294
+ async def get_available_tools(self, tool_list: List[str]) -> Dict[str, bool]:
295
+ """
296
+ Check availability of multiple tools.
297
+
298
+ Args:
299
+ tool_list: List of tool names to check
300
+
301
+ Returns:
302
+ Dict mapping tool names to availability
303
+ """
304
+ results = {}
305
+ for tool in tool_list:
306
+ results[tool] = await self.check_tool_available(tool)
307
+ return results
308
+
309
+ def get_sandbox_info(self, sandbox_id: str) -> Optional[LocalSandboxInfo]:
310
+ """
311
+ Get information about a sandbox.
312
+
313
+ Args:
314
+ sandbox_id: Sandbox identifier
315
+
316
+ Returns:
317
+ LocalSandboxInfo or None if not found
318
+ """
319
+ return self._sandboxes.get(sandbox_id)
320
+
321
+ async def cleanup_all(self) -> int:
322
+ """
323
+ Cleanup all sandboxes and processes.
324
+
325
+ Returns:
326
+ Number of sandboxes cleaned up
327
+ """
328
+ count = len(self._sandboxes)
329
+
330
+ for sandbox_id in list(self._sandboxes.keys()):
331
+ await self.destroy_sandbox(sandbox_id)
332
+
333
+ logger.info("Cleaned up all local sandboxes", count=count)
334
+ return count
335
+
336
+
337
+ # Singleton instance for convenience
338
+ _default_runtime: Optional[LocalRuntime] = None
339
+
340
+
341
+ def get_local_runtime() -> LocalRuntime:
342
+ """Get or create the default local runtime instance."""
343
+ global _default_runtime
344
+ if _default_runtime is None:
345
+ _default_runtime = LocalRuntime()
346
+ return _default_runtime
@@ -0,0 +1,205 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import asyncio
5
+ import logging
6
+ import os
7
+ import signal
8
+ import sys
9
+ from multiprocessing import Process, Queue
10
+ from typing import Any
11
+
12
+ import uvicorn
13
+ from fastapi import Depends, FastAPI, HTTPException, status
14
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
15
+ from pydantic import BaseModel, ValidationError
16
+
17
+
18
+ SANDBOX_MODE = os.getenv("AIPT_SANDBOX_MODE", "false").lower() == "true"
19
+ if not SANDBOX_MODE:
20
+ raise RuntimeError("Tool server should only run in sandbox mode (AIPT_SANDBOX_MODE=true)")
21
+
22
+ parser = argparse.ArgumentParser(description="Start AIPTx tool server")
23
+ parser.add_argument("--token", required=True, help="Authentication token")
24
+ parser.add_argument("--host", default="0.0.0.0", help="Host to bind to") # nosec
25
+ parser.add_argument("--port", type=int, required=True, help="Port to bind to")
26
+
27
+ args = parser.parse_args()
28
+ EXPECTED_TOKEN = args.token
29
+
30
+ app = FastAPI()
31
+ security = HTTPBearer()
32
+
33
+ security_dependency = Depends(security)
34
+
35
+ agent_processes: dict[str, dict[str, Any]] = {}
36
+ agent_queues: dict[str, dict[str, Queue[Any]]] = {}
37
+
38
+
39
+ def verify_token(credentials: HTTPAuthorizationCredentials) -> str:
40
+ if not credentials or credentials.scheme != "Bearer":
41
+ raise HTTPException(
42
+ status_code=status.HTTP_401_UNAUTHORIZED,
43
+ detail="Invalid authentication scheme. Bearer token required.",
44
+ headers={"WWW-Authenticate": "Bearer"},
45
+ )
46
+
47
+ if credentials.credentials != EXPECTED_TOKEN:
48
+ raise HTTPException(
49
+ status_code=status.HTTP_401_UNAUTHORIZED,
50
+ detail="Invalid authentication token",
51
+ headers={"WWW-Authenticate": "Bearer"},
52
+ )
53
+
54
+ return credentials.credentials
55
+
56
+
57
+ class ToolExecutionRequest(BaseModel):
58
+ agent_id: str
59
+ tool_name: str
60
+ kwargs: dict[str, Any]
61
+
62
+
63
+ class ToolExecutionResponse(BaseModel):
64
+ result: Any | None = None
65
+ error: str | None = None
66
+
67
+
68
+ def agent_worker(_agent_id: str, request_queue: Queue[Any], response_queue: Queue[Any]) -> None:
69
+ null_handler = logging.NullHandler()
70
+
71
+ root_logger = logging.getLogger()
72
+ root_logger.handlers = [null_handler]
73
+ root_logger.setLevel(logging.CRITICAL)
74
+
75
+ from aipt_v2.tools.argument_parser import ArgumentConversionError, convert_arguments
76
+ from aipt_v2.tools.registry import get_tool_by_name
77
+
78
+ while True:
79
+ try:
80
+ request = request_queue.get()
81
+
82
+ if request is None:
83
+ break
84
+
85
+ tool_name = request["tool_name"]
86
+ kwargs = request["kwargs"]
87
+
88
+ try:
89
+ tool_func = get_tool_by_name(tool_name)
90
+ if not tool_func:
91
+ response_queue.put({"error": f"Tool '{tool_name}' not found"})
92
+ continue
93
+
94
+ converted_kwargs = convert_arguments(tool_func, kwargs)
95
+ result = tool_func(**converted_kwargs)
96
+
97
+ response_queue.put({"result": result})
98
+
99
+ except (ArgumentConversionError, ValidationError) as e:
100
+ response_queue.put({"error": f"Invalid arguments: {e}"})
101
+ except (RuntimeError, ValueError, ImportError) as e:
102
+ response_queue.put({"error": f"Tool execution error: {e}"})
103
+
104
+ except (RuntimeError, ValueError, ImportError) as e:
105
+ response_queue.put({"error": f"Worker error: {e}"})
106
+
107
+
108
+ def ensure_agent_process(agent_id: str) -> tuple[Queue[Any], Queue[Any]]:
109
+ if agent_id not in agent_processes:
110
+ request_queue: Queue[Any] = Queue()
111
+ response_queue: Queue[Any] = Queue()
112
+
113
+ process = Process(
114
+ target=agent_worker, args=(agent_id, request_queue, response_queue), daemon=True
115
+ )
116
+ process.start()
117
+
118
+ agent_processes[agent_id] = {"process": process, "pid": process.pid}
119
+ agent_queues[agent_id] = {"request": request_queue, "response": response_queue}
120
+
121
+ return agent_queues[agent_id]["request"], agent_queues[agent_id]["response"]
122
+
123
+
124
+ @app.post("/execute", response_model=ToolExecutionResponse)
125
+ async def execute_tool(
126
+ request: ToolExecutionRequest, credentials: HTTPAuthorizationCredentials = security_dependency
127
+ ) -> ToolExecutionResponse:
128
+ verify_token(credentials)
129
+
130
+ request_queue, response_queue = ensure_agent_process(request.agent_id)
131
+
132
+ request_queue.put({"tool_name": request.tool_name, "kwargs": request.kwargs})
133
+
134
+ try:
135
+ loop = asyncio.get_event_loop()
136
+ response = await loop.run_in_executor(None, response_queue.get)
137
+
138
+ if "error" in response:
139
+ return ToolExecutionResponse(error=response["error"])
140
+ return ToolExecutionResponse(result=response.get("result"))
141
+
142
+ except (RuntimeError, ValueError, OSError) as e:
143
+ return ToolExecutionResponse(error=f"Worker error: {e}")
144
+
145
+
146
+ @app.post("/register_agent")
147
+ async def register_agent(
148
+ agent_id: str, credentials: HTTPAuthorizationCredentials = security_dependency
149
+ ) -> dict[str, str]:
150
+ verify_token(credentials)
151
+
152
+ ensure_agent_process(agent_id)
153
+ return {"status": "registered", "agent_id": agent_id}
154
+
155
+
156
+ @app.get("/health")
157
+ async def health_check() -> dict[str, Any]:
158
+ return {
159
+ "status": "healthy",
160
+ "sandbox_mode": str(SANDBOX_MODE),
161
+ "environment": "sandbox" if SANDBOX_MODE else "main",
162
+ "auth_configured": "true" if EXPECTED_TOKEN else "false",
163
+ "active_agents": len(agent_processes),
164
+ "agents": list(agent_processes.keys()),
165
+ }
166
+
167
+
168
+ def cleanup_all_agents() -> None:
169
+ for agent_id in list(agent_processes.keys()):
170
+ try:
171
+ agent_queues[agent_id]["request"].put(None)
172
+ process = agent_processes[agent_id]["process"]
173
+
174
+ process.join(timeout=1)
175
+
176
+ if process.is_alive():
177
+ process.terminate()
178
+ process.join(timeout=1)
179
+
180
+ if process.is_alive():
181
+ process.kill()
182
+
183
+ except (BrokenPipeError, EOFError, OSError) as e:
184
+ logging.getLogger(__name__).debug(f"Expected error during process cleanup: {e}")
185
+ except (RuntimeError, ValueError) as e:
186
+ logging.getLogger(__name__).debug(f"Error during agent cleanup: {e}")
187
+
188
+
189
+ def signal_handler(_signum: int, _frame: Any) -> None:
190
+ signal.signal(signal.SIGPIPE, signal.SIG_IGN) if hasattr(signal, "SIGPIPE") else None
191
+ cleanup_all_agents()
192
+ sys.exit(0)
193
+
194
+
195
+ if hasattr(signal, "SIGPIPE"):
196
+ signal.signal(signal.SIGPIPE, signal.SIG_IGN)
197
+
198
+ signal.signal(signal.SIGTERM, signal_handler)
199
+ signal.signal(signal.SIGINT, signal_handler)
200
+
201
+ if __name__ == "__main__":
202
+ try:
203
+ uvicorn.run(app, host=args.host, port=args.port, log_level="info")
204
+ finally:
205
+ cleanup_all_agents()
@@ -0,0 +1,28 @@
1
+ """
2
+ AIPT Scanners Module
3
+
4
+ Integrations with popular security scanning tools:
5
+ - Nuclei - Template-based vulnerability scanner
6
+ - Nmap - Network scanner
7
+ - Nikto - Web server scanner
8
+ - SQLMap - SQL injection scanner
9
+ - Dirb/Gobuster - Directory enumeration
10
+ """
11
+
12
+ from .base import BaseScanner, ScanResult
13
+ from .nuclei import NucleiScanner, NucleiConfig
14
+ from .nmap import NmapScanner, NmapConfig
15
+ from .nikto import NiktoScanner
16
+ from .web import WebScanner, WebScanConfig
17
+
18
+ __all__ = [
19
+ "BaseScanner",
20
+ "ScanResult",
21
+ "NucleiScanner",
22
+ "NucleiConfig",
23
+ "NmapScanner",
24
+ "NmapConfig",
25
+ "NiktoScanner",
26
+ "WebScanner",
27
+ "WebScanConfig",
28
+ ]