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
aipt_v2/cli.py ADDED
@@ -0,0 +1,2933 @@
1
+ """
2
+ AIPTX Command Line Interface
3
+ ============================
4
+
5
+ Entry point for the AIPTX command-line tool.
6
+ Zero-click installation: pipx install aiptx
7
+
8
+ Usage:
9
+ aiptx setup # Run setup wizard (first-time)
10
+ aiptx scan example.com # Run security scan
11
+ aiptx scan example.com --full # Comprehensive scan
12
+ aiptx api # Start REST API
13
+ aiptx status # Check configuration
14
+ """
15
+
16
+ import argparse
17
+ import asyncio
18
+ import sys
19
+ import os
20
+ import warnings
21
+ from pathlib import Path
22
+
23
+ # Suppress noisy warnings for cleaner user experience
24
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
25
+ warnings.filterwarnings("ignore", message=".*urllib3.*OpenSSL.*")
26
+ warnings.filterwarnings("ignore", message=".*NotOpenSSLWarning.*")
27
+
28
+ # Set default log level to WARNING before any imports that might log
29
+ os.environ.setdefault("AIPT_LOG_LEVEL", "WARNING")
30
+
31
+ # Handle imports for both installed package and local development
32
+ try:
33
+ from . import __version__
34
+ from .config import get_config, validate_config_for_features, reload_config
35
+ from .utils.logging import setup_logging, logger
36
+ from .setup_wizard import is_configured, prompt_first_run_setup, run_setup_wizard
37
+ except ImportError:
38
+ # Local development fallback
39
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
40
+ from __init__ import __version__
41
+ from config import get_config, validate_config_for_features, reload_config
42
+ from utils.logging import setup_logging, logger
43
+ from setup_wizard import is_configured, prompt_first_run_setup, run_setup_wizard
44
+
45
+
46
+ def main():
47
+ """Main CLI entry point."""
48
+ # Handle keyboard interrupts gracefully at the top level
49
+ import signal
50
+
51
+ def signal_handler(signum, frame):
52
+ """Handle interrupt signals gracefully."""
53
+ from rich.console import Console
54
+ Console().print("\n[yellow]Operation cancelled.[/yellow]")
55
+ sys.exit(130) # Standard exit code for Ctrl+C
56
+
57
+ # Register signal handlers for graceful shutdown
58
+ signal.signal(signal.SIGINT, signal_handler)
59
+ signal.signal(signal.SIGTERM, signal_handler)
60
+
61
+ parser = argparse.ArgumentParser(
62
+ prog="aiptx",
63
+ description="AIPTX - AI-Powered Penetration Testing Framework",
64
+ formatter_class=argparse.RawDescriptionHelpFormatter,
65
+ epilog="""
66
+ Examples:
67
+ aiptx scan example.com Run basic scan
68
+ aiptx scan example.com --full Run comprehensive scan
69
+ aiptx scan example.com --ai AI-guided scanning
70
+ aiptx api Start REST API server
71
+ aiptx status Check configuration status
72
+ aiptx version Show version information
73
+
74
+ First-time setup:
75
+ aiptx setup Interactive configuration wizard
76
+
77
+ Installation:
78
+ pipx install aiptx Zero-click install
79
+ pip install aiptx[full] Install with all features
80
+ """,
81
+ )
82
+
83
+ parser.add_argument(
84
+ "--version", "-V",
85
+ action="version",
86
+ version=f"AIPTX v{__version__}",
87
+ )
88
+
89
+ parser.add_argument(
90
+ "--verbose", "-v",
91
+ action="count",
92
+ default=0,
93
+ help="Increase verbosity (use -vv for debug)",
94
+ )
95
+
96
+ parser.add_argument(
97
+ "--json",
98
+ action="store_true",
99
+ help="Output in JSON format",
100
+ )
101
+
102
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
103
+
104
+ # Scan command
105
+ scan_parser = subparsers.add_parser("scan", help="Run security scan")
106
+ scan_parser.add_argument("target", help="Target URL or domain")
107
+ scan_parser.add_argument("--client", "-c", help="Client name")
108
+ scan_parser.add_argument("--output", "-o", help="Output directory")
109
+ scan_parser.add_argument(
110
+ "--mode", "-m",
111
+ choices=["quick", "standard", "full", "ai"],
112
+ default="standard",
113
+ help="Scan mode (default: standard)",
114
+ )
115
+ scan_parser.add_argument("--full", action="store_true", help="Run full comprehensive scan")
116
+ scan_parser.add_argument("--ai", action="store_true", help="Enable AI-guided scanning")
117
+ scan_parser.add_argument("--use-vps", action="store_true", help="Use VPS for tool execution")
118
+ scan_parser.add_argument("--use-acunetix", action="store_true", help="Include Acunetix scan")
119
+ scan_parser.add_argument("--use-burp", action="store_true", help="Include Burp Suite scan")
120
+ scan_parser.add_argument("--skip-recon", action="store_true", help="Skip reconnaissance phase")
121
+ scan_parser.add_argument("--quiet", "-q", action="store_true", help="Quiet mode - minimal output")
122
+ scan_parser.add_argument("--no-stream", action="store_true", help="Don't stream command output (show progress only)")
123
+ scan_parser.add_argument("--check", action="store_true", help="Run pre-flight checks to validate config/connections before scan")
124
+
125
+ # API command
126
+ api_parser = subparsers.add_parser("api", help="Start REST API server")
127
+ # Security: Default to localhost to prevent accidental network exposure
128
+ api_parser.add_argument("--host", default="127.0.0.1", help="API host (default: 127.0.0.1, use 0.0.0.0 for network access)")
129
+ api_parser.add_argument("--port", "-p", type=int, default=8000, help="API port (default: 8000)")
130
+ api_parser.add_argument("--reload", action="store_true", help="Enable auto-reload for development")
131
+
132
+ # Status command
133
+ subparsers.add_parser("status", help="Check configuration and dependencies")
134
+
135
+ # Test command - validate all configurations
136
+ test_parser = subparsers.add_parser("test", help="Test and validate all configurations")
137
+ test_parser.add_argument("--llm", action="store_true", help="Test LLM API key only")
138
+ test_parser.add_argument("--vps", action="store_true", help="Test VPS connection only")
139
+ test_parser.add_argument("--scanners", action="store_true", help="Test scanner integrations only")
140
+ test_parser.add_argument("--tools", action="store_true", help="Test local tool availability")
141
+ test_parser.add_argument("--all", "-a", action="store_true", help="Test everything (default)")
142
+
143
+ # Version command
144
+ subparsers.add_parser("version", help="Show detailed version information")
145
+
146
+ # Setup command
147
+ setup_parser = subparsers.add_parser("setup", help="Run interactive setup wizard")
148
+ setup_parser.add_argument(
149
+ "--force", "-f",
150
+ action="store_true",
151
+ help="Force reconfiguration even if already configured"
152
+ )
153
+
154
+ # VPS command with subcommands
155
+ vps_parser = subparsers.add_parser("vps", help="VPS remote execution management")
156
+ vps_subparsers = vps_parser.add_subparsers(dest="vps_command", help="VPS commands")
157
+
158
+ # vps setup - Install tools on VPS
159
+ vps_setup = vps_subparsers.add_parser("setup", help="Install security tools on VPS")
160
+ vps_setup.add_argument(
161
+ "--categories", "-c",
162
+ nargs="+",
163
+ choices=["recon", "scan", "exploit", "post_exploit", "api", "network"],
164
+ help="Tool categories to install (default: all)"
165
+ )
166
+ vps_setup.add_argument(
167
+ "--tools", "-t",
168
+ nargs="+",
169
+ help="Specific tools to install"
170
+ )
171
+
172
+ # vps status - Check VPS connection and tools
173
+ vps_subparsers.add_parser("status", help="Check VPS connection and installed tools")
174
+
175
+ # vps scan - Run scan from VPS
176
+ vps_scan = vps_subparsers.add_parser("scan", help="Run security scan from VPS")
177
+ vps_scan.add_argument("target", help="Target URL or domain")
178
+ vps_scan.add_argument(
179
+ "--mode", "-m",
180
+ choices=["quick", "standard", "full"],
181
+ default="standard",
182
+ help="Scan mode"
183
+ )
184
+ vps_scan.add_argument(
185
+ "--tools", "-t",
186
+ nargs="+",
187
+ help="Specific tools to run"
188
+ )
189
+
190
+ # vps script - Generate setup script
191
+ vps_script = vps_subparsers.add_parser("script", help="Generate VPS setup script")
192
+ vps_script.add_argument(
193
+ "--output", "-o",
194
+ help="Output file (default: stdout)"
195
+ )
196
+ vps_script.add_argument(
197
+ "--categories", "-c",
198
+ nargs="+",
199
+ help="Tool categories to include"
200
+ )
201
+
202
+ # Verify command - Installation verification
203
+ verify_parser = subparsers.add_parser("verify", help="Verify installation and configuration")
204
+ verify_parser.add_argument(
205
+ "--quick", "-q",
206
+ action="store_true",
207
+ help="Run quick checks only"
208
+ )
209
+ verify_parser.add_argument(
210
+ "--fix",
211
+ action="store_true",
212
+ help="Attempt to auto-fix issues"
213
+ )
214
+ verify_parser.add_argument(
215
+ "--report", "-r",
216
+ help="Save markdown report to file"
217
+ )
218
+
219
+ # Shell command - Interactive shell
220
+ shell_parser = subparsers.add_parser("shell", help="Start interactive security shell")
221
+ shell_parser.add_argument(
222
+ "--log", "-l",
223
+ help="Log session to file"
224
+ )
225
+ shell_parser.add_argument(
226
+ "--dir", "-d",
227
+ help="Working directory"
228
+ )
229
+
230
+ # Tools command with subcommands
231
+ tools_parser = subparsers.add_parser("tools", help="Manage local security tools")
232
+ tools_subparsers = tools_parser.add_subparsers(dest="tools_command", help="Tools commands")
233
+
234
+ # tools install - Install security tools
235
+ tools_install = tools_subparsers.add_parser("install", help="Install security tools on local system")
236
+ tools_install.add_argument(
237
+ "--categories", "-c",
238
+ nargs="+",
239
+ choices=[
240
+ "recon", "scan", "exploit", "post_exploit", "api", "network",
241
+ "prerequisite", "active_directory", "cloud", "container",
242
+ "osint", "wireless", "web", "secrets", "mobile"
243
+ ],
244
+ help="Tool categories to install (default: core tools)"
245
+ )
246
+ tools_install.add_argument(
247
+ "--tools", "-t",
248
+ nargs="+",
249
+ help="Specific tools to install"
250
+ )
251
+ tools_install.add_argument(
252
+ "--all", "-a",
253
+ action="store_true",
254
+ help="Install all available tools"
255
+ )
256
+ tools_install.add_argument(
257
+ "--core",
258
+ action="store_true",
259
+ help="Install only core essential tools (default)"
260
+ )
261
+ tools_install.add_argument(
262
+ "--no-sudo",
263
+ action="store_true",
264
+ help="Don't use sudo for installation"
265
+ )
266
+
267
+ # tools list - List available/installed tools
268
+ tools_list = tools_subparsers.add_parser("list", help="List available and installed tools")
269
+ tools_list.add_argument(
270
+ "--category", "-c",
271
+ choices=[
272
+ "recon", "scan", "exploit", "post_exploit", "api", "network",
273
+ "prerequisite", "active_directory", "cloud", "container",
274
+ "osint", "wireless", "web", "secrets", "mobile", "all"
275
+ ],
276
+ default="all",
277
+ help="Filter by category"
278
+ )
279
+ tools_list.add_argument(
280
+ "--installed-only",
281
+ action="store_true",
282
+ help="Show only installed tools"
283
+ )
284
+
285
+ # tools check - Check tool availability
286
+ tools_subparsers.add_parser("check", help="Check which tools are installed")
287
+
288
+ # AI Skills command with subcommands
289
+ ai_parser = subparsers.add_parser("ai", help="AI-powered security testing (code review, API testing, web pentesting)")
290
+ ai_subparsers = ai_parser.add_subparsers(dest="ai_command", help="AI testing commands")
291
+
292
+ # ai code-review - AI source code security review
293
+ ai_code = ai_subparsers.add_parser("code-review", help="AI-powered source code security review")
294
+ ai_code.add_argument("target", help="Path to code directory to review")
295
+ ai_code.add_argument(
296
+ "--focus", "-f",
297
+ nargs="+",
298
+ choices=["sqli", "xss", "auth", "crypto", "secrets", "injection"],
299
+ help="Focus areas for review"
300
+ )
301
+ ai_code.add_argument(
302
+ "--model", "-m",
303
+ default="claude-sonnet-4-20250514",
304
+ help="LLM model to use (default: claude-sonnet-4-20250514)"
305
+ )
306
+ ai_code.add_argument(
307
+ "--max-steps",
308
+ type=int,
309
+ default=100,
310
+ help="Maximum agent steps (default: 100)"
311
+ )
312
+ ai_code.add_argument(
313
+ "--quick", "-q",
314
+ action="store_true",
315
+ help="Quick scan focusing on high-priority patterns"
316
+ )
317
+ ai_code.add_argument(
318
+ "--output", "-o",
319
+ help="Output file for results (JSON)"
320
+ )
321
+
322
+ # ai api-test - AI API security testing
323
+ ai_api = ai_subparsers.add_parser("api-test", help="AI-powered REST API security testing")
324
+ ai_api.add_argument("target", help="Base URL of the API to test")
325
+ ai_api.add_argument(
326
+ "--openapi", "-s",
327
+ help="Path or URL to OpenAPI/Swagger spec"
328
+ )
329
+ ai_api.add_argument(
330
+ "--auth-token", "-t",
331
+ help="Bearer token for API authentication"
332
+ )
333
+ ai_api.add_argument(
334
+ "--model", "-m",
335
+ default="claude-sonnet-4-20250514",
336
+ help="LLM model to use"
337
+ )
338
+ ai_api.add_argument(
339
+ "--max-steps",
340
+ type=int,
341
+ default=100,
342
+ help="Maximum agent steps"
343
+ )
344
+ ai_api.add_argument(
345
+ "--output", "-o",
346
+ help="Output file for results (JSON)"
347
+ )
348
+
349
+ # ai web-pentest - AI web penetration testing
350
+ ai_web = ai_subparsers.add_parser("web-pentest", help="AI-powered web application penetration testing")
351
+ ai_web.add_argument("target", help="Target URL to test")
352
+ ai_web.add_argument(
353
+ "--auth-token", "-t",
354
+ help="Bearer token for authentication"
355
+ )
356
+ ai_web.add_argument(
357
+ "--cookie", "-c",
358
+ action="append",
359
+ help="Cookies for authenticated testing (key=value)"
360
+ )
361
+ ai_web.add_argument(
362
+ "--model", "-m",
363
+ default="claude-sonnet-4-20250514",
364
+ help="LLM model to use"
365
+ )
366
+ ai_web.add_argument(
367
+ "--max-steps",
368
+ type=int,
369
+ default=100,
370
+ help="Maximum agent steps"
371
+ )
372
+ ai_web.add_argument(
373
+ "--quick", "-q",
374
+ action="store_true",
375
+ help="Quick scan focusing on critical vulnerabilities"
376
+ )
377
+ ai_web.add_argument(
378
+ "--output", "-o",
379
+ help="Output file for results (JSON)"
380
+ )
381
+
382
+ # ai full - Full AI-driven security assessment
383
+ ai_full = ai_subparsers.add_parser("full", help="Full AI-driven security assessment")
384
+ ai_full.add_argument("target", help="Target URL or code path")
385
+ ai_full.add_argument(
386
+ "--types", "-t",
387
+ nargs="+",
388
+ choices=["web", "api", "code"],
389
+ default=["web"],
390
+ help="Types of testing to perform"
391
+ )
392
+ ai_full.add_argument(
393
+ "--model", "-m",
394
+ default="claude-sonnet-4-20250514",
395
+ help="LLM model to use"
396
+ )
397
+ ai_full.add_argument(
398
+ "--output", "-o",
399
+ help="Output file for results (JSON)"
400
+ )
401
+
402
+ args = parser.parse_args()
403
+
404
+ # Setup logging based on verbosity
405
+ log_level = "DEBUG" if args.verbose >= 2 else "INFO" if args.verbose == 1 else "WARNING"
406
+ setup_logging(level=log_level, json_format=args.json)
407
+
408
+ # Handle commands - wrap in try/except for graceful interrupt handling
409
+ try:
410
+ if args.command == "setup":
411
+ return run_setup(args)
412
+ elif args.command == "scan":
413
+ return run_scan(args)
414
+ elif args.command == "api":
415
+ return run_api(args)
416
+ elif args.command == "status":
417
+ return show_status(args)
418
+ elif args.command == "test":
419
+ return run_config_test(args)
420
+ elif args.command == "version":
421
+ return show_version()
422
+ elif args.command == "vps":
423
+ return run_vps_command(args)
424
+ elif args.command == "tools":
425
+ return run_tools_command(args)
426
+ elif args.command == "shell":
427
+ return run_shell(args)
428
+ elif args.command == "verify":
429
+ return run_verify(args)
430
+ elif args.command == "ai":
431
+ return run_ai_command(args)
432
+ else:
433
+ # No command given - start interactive mode
434
+ return run_interactive_mode()
435
+ except KeyboardInterrupt:
436
+ # Gracefully handle Ctrl+C
437
+ from rich.console import Console
438
+ Console().print("\n[yellow]Operation cancelled.[/yellow]")
439
+ return 130
440
+
441
+
442
+ def show_first_run_help():
443
+ """Show helpful guidance for first-time users."""
444
+ from rich.console import Console
445
+ from rich.panel import Panel
446
+
447
+ console = Console()
448
+
449
+ console.print()
450
+ console.print(Panel(
451
+ "[bold cyan]Welcome to AIPTX![/bold cyan]\n\n"
452
+ "[bold yellow]First-time setup required[/bold yellow]\n\n"
453
+ "AIPTX needs an LLM API key to power AI-guided security testing.\n\n"
454
+ "[bold]Quick Start:[/bold]\n"
455
+ " 1. Run [bold green]aiptx setup[/bold green] to configure interactively\n"
456
+ " 2. Or set environment variable:\n"
457
+ " [dim]export ANTHROPIC_API_KEY=your-key-here[/dim]\n\n"
458
+ "[bold]Then run:[/bold]\n"
459
+ " [bold green]aiptx scan example.com[/bold green]",
460
+ title="🚀 AIPTX - AI-Powered Penetration Testing",
461
+ border_style="cyan",
462
+ padding=(1, 2),
463
+ ))
464
+ console.print()
465
+
466
+ return 0
467
+
468
+
469
+ def run_interactive_mode():
470
+ """Run AIPTX in interactive shell mode."""
471
+ from rich.console import Console
472
+ from rich.panel import Panel
473
+ from rich.prompt import Prompt
474
+ from rich.table import Table
475
+ from rich import box
476
+
477
+ console = Console()
478
+
479
+ # ASCII Art Logo
480
+ logo = """
481
+ [bold cyan] █████╗ ██╗██████╗ ████████╗██╗ ██╗[/bold cyan]
482
+ [bold cyan] ██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗██╔╝[/bold cyan]
483
+ [bold blue] ███████║██║██████╔╝ ██║ ╚███╔╝ [/bold blue]
484
+ [bold blue] ██╔══██║██║██╔═══╝ ██║ ██╔██╗ [/bold blue]
485
+ [bold magenta] ██║ ██║██║██║ ██║ ██╔╝ ██╗[/bold magenta]
486
+ [bold magenta] ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝[/bold magenta]
487
+ """
488
+
489
+ # Show welcome banner
490
+ console.print()
491
+ console.print(logo)
492
+ console.print("[bold white] AI-Powered Penetration Testing Framework[/bold white]")
493
+ console.print(f"[dim] v{__version__} • [link=https://aiptx.io]aiptx.io[/link][/dim]")
494
+ console.print()
495
+
496
+ # Stylish separator
497
+ console.print("[dim cyan]" + "━" * 60 + "[/dim cyan]")
498
+ console.print()
499
+
500
+ # Quick commands in a nice table format
501
+ commands_table = Table(
502
+ show_header=False,
503
+ box=box.SIMPLE,
504
+ padding=(0, 2),
505
+ collapse_padding=True,
506
+ )
507
+ commands_table.add_column("Command", style="bold green", width=18)
508
+ commands_table.add_column("Description", style="white")
509
+
510
+ commands_table.add_row("scan <target>", "🔍 Run security scan")
511
+ commands_table.add_row("scan <target> --ai", "🤖 AI-guided intelligent scanning")
512
+ commands_table.add_row("scan <target> --full", "🎯 Comprehensive assessment")
513
+ commands_table.add_row("ai <command>", "🧠 AI security testing")
514
+ commands_table.add_row("vps <command>", "☁️ Remote VPS execution")
515
+ commands_table.add_row("setup", "⚙️ Configure AIPTX")
516
+ commands_table.add_row("status", "📊 Show configuration")
517
+ commands_table.add_row("help", "❓ Show all commands")
518
+ commands_table.add_row("exit", "👋 Exit AIPTX")
519
+
520
+ console.print(commands_table)
521
+ console.print()
522
+ console.print("[dim cyan]" + "━" * 60 + "[/dim cyan]")
523
+ console.print()
524
+ console.print("[dim]💡 Tip: Type [bold green]scan example.com --ai[/bold green] to start AI-guided scanning[/dim]")
525
+ console.print("[dim]🌐 Docs: [link=https://aiptx.io/docs]https://aiptx.io/docs[/link][/dim]")
526
+ console.print()
527
+
528
+ # Check configuration status
529
+ if not is_configured():
530
+ console.print(Panel(
531
+ "[yellow]⚠️ AIPTX is not configured yet![/yellow]\n\n"
532
+ "Run [bold green]setup[/bold green] to configure your API keys and scanners.\n"
533
+ "Or set environment variable: [dim]export ANTHROPIC_API_KEY=your-key[/dim]",
534
+ border_style="yellow",
535
+ title="[bold yellow]Setup Required[/bold yellow]",
536
+ title_align="left",
537
+ ))
538
+ console.print()
539
+
540
+ # Interactive loop
541
+ while True:
542
+ try:
543
+ # Flush stdin to avoid stale input from previous commands
544
+ import sys
545
+ import platform
546
+ if sys.stdin.isatty():
547
+ # Clear any buffered input (platform-specific)
548
+ if platform.system() == "Windows":
549
+ # Windows: use msvcrt for non-blocking input check
550
+ import msvcrt
551
+ while msvcrt.kbhit():
552
+ msvcrt.getch()
553
+ else:
554
+ # Unix/Linux/macOS: use select
555
+ import select
556
+ while select.select([sys.stdin], [], [], 0)[0]:
557
+ sys.stdin.read(1)
558
+
559
+ # Get user input with stylish prompt
560
+ user_input = Prompt.ask("[bold cyan]aiptx[/bold cyan] [dim]→[/dim]", default="").strip()
561
+
562
+ if not user_input:
563
+ continue
564
+
565
+ # Parse the input
566
+ parts = user_input.split()
567
+ cmd = parts[0].lower()
568
+ args_list = parts[1:] if len(parts) > 1 else []
569
+
570
+ # Handle commands
571
+ if cmd in ("exit", "quit", "q"):
572
+ console.print()
573
+ console.print("[bold cyan]Thanks for using AIPTX![/bold cyan] 🚀")
574
+ console.print("[dim]Visit [link=https://aiptx.io]aiptx.io[/link] for docs & updates[/dim]")
575
+ console.print()
576
+ break
577
+
578
+ elif cmd == "help":
579
+ show_interactive_help(console)
580
+
581
+ elif cmd == "clear":
582
+ console.clear()
583
+
584
+ elif cmd == "setup":
585
+ run_setup_wrapper()
586
+
587
+ elif cmd == "status":
588
+ show_status_wrapper()
589
+
590
+ elif cmd == "test":
591
+ run_test_wrapper(parts[1:] if len(parts) > 1 else None)
592
+
593
+ elif cmd == "version":
594
+ console.print(f"[cyan]AIPTX v{__version__}[/cyan]")
595
+
596
+ elif cmd == "scan":
597
+ if not args_list:
598
+ console.print("[red]Usage:[/red] scan <target> [--mode quick|standard|full]")
599
+ else:
600
+ run_scan_wrapper(args_list)
601
+
602
+ elif cmd == "vps":
603
+ run_vps_wrapper(args_list)
604
+
605
+ elif cmd == "ai":
606
+ run_ai_wrapper(args_list)
607
+
608
+ else:
609
+ console.print(f"[red]Unknown command:[/red] {cmd}")
610
+ console.print("[dim]Type 'help' for available commands[/dim]")
611
+
612
+ except KeyboardInterrupt:
613
+ console.print("\n[dim]Press Ctrl+C again to exit, or type 'exit'[/dim]")
614
+ try:
615
+ # Give user a chance to continue
616
+ continue
617
+ except KeyboardInterrupt:
618
+ console.print("\n[dim]Goodbye![/dim]")
619
+ break
620
+ except EOFError:
621
+ # Handle Ctrl+D
622
+ console.print("\n[dim]Goodbye![/dim]")
623
+ break
624
+
625
+ return 0
626
+
627
+
628
+ def show_interactive_help(console):
629
+ """Show help for interactive mode."""
630
+ from rich.table import Table
631
+ from rich import box
632
+
633
+ table = Table(title="AIPTX Commands", box=box.ROUNDED)
634
+ table.add_column("Command", style="green")
635
+ table.add_column("Description")
636
+ table.add_column("Example", style="dim")
637
+
638
+ table.add_row("scan <target>", "Run security scan", "scan example.com --check --full")
639
+ table.add_row("ai code-review <path>", "AI code security review", "ai code-review ./src")
640
+ table.add_row("ai api-test <url>", "AI API security testing", "ai api-test https://api.example.com")
641
+ table.add_row("ai web-pentest <url>", "AI web penetration test", "ai web-pentest https://example.com")
642
+ table.add_row("vps setup", "Install tools on VPS", "vps setup")
643
+ table.add_row("vps status", "Check VPS status", "vps status")
644
+ table.add_row("vps scan <target>", "Run scan from VPS", "vps scan example.com")
645
+ table.add_row("setup", "Configure AIPTX", "setup")
646
+ table.add_row("status", "Show configuration", "status")
647
+ table.add_row("test", "Validate all configs", "test")
648
+ table.add_row("clear", "Clear screen", "clear")
649
+ table.add_row("exit", "Exit AIPTX", "exit")
650
+
651
+ console.print(table)
652
+
653
+
654
+ def run_setup_wrapper():
655
+ """Wrapper to run setup from interactive mode."""
656
+ from rich.console import Console
657
+ console = Console()
658
+ try:
659
+ success = run_setup_wizard(force=True)
660
+ # Reload configuration after successful setup
661
+ if success:
662
+ reload_config()
663
+ except Exception as e:
664
+ console.print(f"[red]Setup error:[/red] {e}")
665
+
666
+
667
+ def show_status_wrapper():
668
+ """Wrapper to show status from interactive mode."""
669
+ import argparse
670
+ args = argparse.Namespace(verbose=0, json=False)
671
+ show_status(args)
672
+
673
+
674
+ def run_test_wrapper(args_list=None):
675
+ """Wrapper to run config test from interactive mode."""
676
+ import argparse
677
+ from rich.console import Console
678
+ console = Console()
679
+
680
+ # Parse test arguments
681
+ test_llm = False
682
+ test_vps = False
683
+ test_scanners = False
684
+ test_tools = False
685
+ test_all = True
686
+
687
+ if args_list:
688
+ for arg in args_list:
689
+ if arg == "--llm":
690
+ test_llm = True
691
+ test_all = False
692
+ elif arg == "--vps":
693
+ test_vps = True
694
+ test_all = False
695
+ elif arg == "--scanners":
696
+ test_scanners = True
697
+ test_all = False
698
+ elif arg == "--tools":
699
+ test_tools = True
700
+ test_all = False
701
+
702
+ args = argparse.Namespace(
703
+ llm=test_llm,
704
+ vps=test_vps,
705
+ scanners=test_scanners,
706
+ tools=test_tools,
707
+ all=test_all,
708
+ )
709
+
710
+ try:
711
+ run_config_test(args)
712
+ except Exception as e:
713
+ console.print(f"[red]Test error:[/red] {e}")
714
+
715
+
716
+ def run_scan_wrapper(args_list):
717
+ """Wrapper to run scan from interactive mode."""
718
+ import argparse
719
+ from rich.console import Console
720
+ console = Console()
721
+
722
+ # Parse scan arguments
723
+ target = args_list[0]
724
+ mode = "standard"
725
+ full = False
726
+ ai = False
727
+ check = False
728
+ use_vps = False
729
+
730
+ for i, arg in enumerate(args_list[1:]):
731
+ if arg == "--full":
732
+ full = True
733
+ elif arg == "--ai":
734
+ ai = True
735
+ elif arg == "--check":
736
+ check = True
737
+ elif arg == "--use-vps":
738
+ use_vps = True
739
+ elif arg in ("--mode", "-m") and i + 2 < len(args_list):
740
+ mode = args_list[i + 2]
741
+
742
+ args = argparse.Namespace(
743
+ target=target,
744
+ client=None,
745
+ output=None,
746
+ mode=mode,
747
+ full=full,
748
+ ai=ai,
749
+ use_vps=use_vps,
750
+ use_acunetix=False,
751
+ use_burp=False,
752
+ skip_recon=False,
753
+ verbose=0,
754
+ check=check,
755
+ quiet=False,
756
+ no_stream=False,
757
+ )
758
+
759
+ try:
760
+ run_scan(args)
761
+ except Exception as e:
762
+ console.print(f"[red]Scan error:[/red] {e}")
763
+
764
+
765
+ def run_vps_wrapper(args_list):
766
+ """Wrapper to run VPS commands from interactive mode."""
767
+ import argparse
768
+ from rich.console import Console
769
+ console = Console()
770
+
771
+ if not args_list:
772
+ console.print("[yellow]VPS Commands:[/yellow]")
773
+ console.print(" vps setup - Install security tools")
774
+ console.print(" vps status - Check VPS status")
775
+ console.print(" vps scan - Run scan from VPS")
776
+ return
777
+
778
+ vps_cmd = args_list[0]
779
+ args = argparse.Namespace(
780
+ vps_command=vps_cmd,
781
+ categories=None,
782
+ tools=None,
783
+ target=args_list[1] if len(args_list) > 1 else None,
784
+ mode="standard",
785
+ output=None,
786
+ )
787
+
788
+ try:
789
+ run_vps_command(args)
790
+ except Exception as e:
791
+ console.print(f"[red]VPS error:[/red] {e}")
792
+
793
+
794
+ def run_ai_wrapper(args_list):
795
+ """Wrapper to run AI commands from interactive mode."""
796
+ import argparse
797
+ from rich.console import Console
798
+ console = Console()
799
+
800
+ if not args_list:
801
+ console.print("[yellow]AI Commands:[/yellow]")
802
+ console.print(" ai code-review <path> - AI code security review")
803
+ console.print(" ai api-test <url> - AI API testing")
804
+ console.print(" ai web-pentest <url> - AI web pentesting")
805
+ console.print(" ai full <target> - Full AI assessment")
806
+ return
807
+
808
+ ai_cmd = args_list[0]
809
+ target = args_list[1] if len(args_list) > 1 else None
810
+
811
+ if not target and ai_cmd != "help":
812
+ console.print(f"[red]Usage:[/red] ai {ai_cmd} <target>")
813
+ return
814
+
815
+ args = argparse.Namespace(
816
+ ai_command=ai_cmd,
817
+ target=target,
818
+ focus=None,
819
+ model="claude-sonnet-4-20250514",
820
+ max_steps=100,
821
+ quick="--quick" in args_list or "-q" in args_list,
822
+ output=None,
823
+ openapi=None,
824
+ auth_token=None,
825
+ cookie=None,
826
+ types=["web"],
827
+ )
828
+
829
+ try:
830
+ run_ai_command(args)
831
+ except Exception as e:
832
+ console.print(f"[red]AI error:[/red] {e}")
833
+
834
+
835
+ def run_setup(args):
836
+ """Run the interactive setup wizard."""
837
+ force = getattr(args, 'force', False)
838
+ success = run_setup_wizard(force=force)
839
+
840
+ # Reload configuration after successful setup so it's immediately available
841
+ if success:
842
+ reload_config()
843
+
844
+ return 0 if success else 1
845
+
846
+
847
+ def run_scan(args):
848
+ """Run security scan."""
849
+ from rich.console import Console
850
+ from rich.panel import Panel
851
+
852
+ console = Console()
853
+
854
+ try:
855
+ from .orchestrator import Orchestrator, OrchestratorConfig
856
+ except ImportError as e:
857
+ # Provide helpful error message for missing dependencies
858
+ error_msg = str(e)
859
+ console.print()
860
+ console.print(Panel(
861
+ "[bold red]Missing Dependencies[/bold red]\n\n"
862
+ f"[dim]Import error: {error_msg}[/dim]\n\n"
863
+ "The scan module requires additional dependencies.\n\n"
864
+ "[bold]To fix, install the full package:[/bold]\n"
865
+ " [bold green]pip install aiptx[full][/bold green]\n\n"
866
+ "[bold]Or install specific dependencies:[/bold]\n"
867
+ " [dim]pip install sentence-transformers torch langchain-core[/dim]",
868
+ title="⚠️ Scan Module Not Available",
869
+ border_style="yellow",
870
+ padding=(1, 2),
871
+ ))
872
+ console.print()
873
+ return 1
874
+
875
+ # Check if configured - prompt for setup if not
876
+ if not is_configured():
877
+ # Interactive setup for first-time users
878
+ if not prompt_first_run_setup():
879
+ return 1 # User declined setup or setup failed
880
+
881
+ # Validate configuration for requested features
882
+ features = ["llm"]
883
+ if args.use_acunetix:
884
+ features.append("acunetix")
885
+ if args.use_burp:
886
+ features.append("burp")
887
+ if args.use_vps:
888
+ features.append("vps")
889
+
890
+ errors = validate_config_for_features(features)
891
+ if errors:
892
+ console.print()
893
+ console.print(Panel(
894
+ "[bold red]Configuration Error[/bold red]\n\n"
895
+ "The following issues need to be resolved:\n\n" +
896
+ "\n".join(f" [yellow]•[/yellow] {error}" for error in errors) +
897
+ "\n\n[bold]To fix:[/bold]\n"
898
+ " Run [bold green]aiptx setup[/bold green] to configure interactively\n\n"
899
+ "[bold]Or set environment variables:[/bold]\n"
900
+ " [dim]export ANTHROPIC_API_KEY=your-key-here[/dim]",
901
+ title="⚠️ Setup Required",
902
+ border_style="yellow",
903
+ padding=(1, 2),
904
+ ))
905
+ console.print()
906
+ return 1
907
+
908
+ # Run pre-flight checks if requested
909
+ if getattr(args, 'check', False):
910
+ ai_mode = args.ai or args.mode == "ai"
911
+ checks_passed = run_preflight_check(
912
+ console=console,
913
+ use_vps=args.use_vps,
914
+ use_acunetix=args.use_acunetix,
915
+ use_burp=args.use_burp,
916
+ ai_mode=ai_mode,
917
+ )
918
+
919
+ if not checks_passed:
920
+ console.print("[yellow]Scan aborted due to failed pre-flight checks.[/yellow]")
921
+ console.print("[dim]Fix the issues above and try again, or run without --check to skip validation.[/dim]")
922
+ return 1
923
+
924
+ console.print("[dim]Pre-flight checks passed. Starting scan...[/dim]")
925
+ console.print()
926
+
927
+ # Create config
928
+ # Verbose mode is default (True), quiet mode disables it
929
+ verbose = not getattr(args, 'quiet', False)
930
+ # Show command output is default (True), --no-stream disables it
931
+ show_command_output = not getattr(args, 'no_stream', False)
932
+
933
+ config = OrchestratorConfig(
934
+ target=args.target,
935
+ output_dir=Path(args.output) if args.output else Path("./results"),
936
+ skip_recon=args.skip_recon,
937
+ use_acunetix=args.use_acunetix,
938
+ use_burp=args.use_burp,
939
+ verbose=verbose,
940
+ show_command_output=show_command_output,
941
+ )
942
+
943
+ # Determine mode
944
+ if args.ai or args.mode == "ai":
945
+ mode = "ai"
946
+ elif args.full or args.mode == "full":
947
+ mode = "full"
948
+ elif args.mode == "quick":
949
+ mode = "quick"
950
+ else:
951
+ mode = "standard"
952
+
953
+ # Show scan starting message
954
+ console.print()
955
+ console.print(f"[bold cyan]Starting {mode} scan on[/bold cyan] [bold]{args.target}[/bold]")
956
+ console.print()
957
+
958
+ # Run orchestrator
959
+ orchestrator = Orchestrator(args.target, config)
960
+
961
+ try:
962
+ # Use custom event loop handling to avoid cleanup warnings
963
+ loop = asyncio.new_event_loop()
964
+ asyncio.set_event_loop(loop)
965
+ try:
966
+ loop.run_until_complete(orchestrator.run())
967
+ finally:
968
+ # Clean up pending tasks before closing the loop
969
+ try:
970
+ pending = asyncio.all_tasks(loop)
971
+ for task in pending:
972
+ task.cancel()
973
+ # Give tasks a chance to respond to cancellation
974
+ if pending:
975
+ loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
976
+ except Exception:
977
+ pass # Ignore cleanup errors
978
+ loop.close()
979
+
980
+ console.print()
981
+ console.print("[bold green]✓ Scan completed successfully[/bold green]")
982
+ return 0
983
+ except KeyboardInterrupt:
984
+ console.print()
985
+ console.print("[yellow]Scan interrupted by user[/yellow]")
986
+ return 130
987
+ except Exception as e:
988
+ console.print()
989
+ console.print(f"[bold red]✗ Scan failed:[/bold red] {e}")
990
+ if args.verbose:
991
+ import traceback
992
+ console.print(f"[dim]{traceback.format_exc()}[/dim]")
993
+ return 1
994
+
995
+
996
+ def run_api(args):
997
+ """Start REST API server."""
998
+ import uvicorn
999
+
1000
+ logger.info(f"Starting API server on {args.host}:{args.port}")
1001
+
1002
+ # Try package import first, then local
1003
+ try:
1004
+ uvicorn.run(
1005
+ "app:app",
1006
+ host=args.host,
1007
+ port=args.port,
1008
+ reload=args.reload,
1009
+ log_level="info",
1010
+ )
1011
+ except Exception:
1012
+ # Fallback for installed package
1013
+ uvicorn.run(
1014
+ "aiptx.app:app",
1015
+ host=args.host,
1016
+ port=args.port,
1017
+ reload=args.reload,
1018
+ log_level="info",
1019
+ )
1020
+
1021
+ return 0
1022
+
1023
+
1024
+ def show_status(args):
1025
+ """Show configuration status."""
1026
+ from rich.console import Console
1027
+ from rich.table import Table
1028
+
1029
+ console = Console()
1030
+ config = get_config()
1031
+
1032
+ console.print("\n[bold cyan]AIPT v2 Configuration Status[/bold cyan]\n")
1033
+
1034
+ # LLM Status
1035
+ table = Table(title="LLM Configuration")
1036
+ table.add_column("Setting", style="cyan")
1037
+ table.add_column("Value", style="green")
1038
+ table.add_column("Status", style="yellow")
1039
+
1040
+ table.add_row("Provider", config.llm.provider, "✓" if config.llm.provider else "✗")
1041
+ table.add_row("Model", config.llm.model, "✓" if config.llm.model else "✗")
1042
+ table.add_row("API Key", "****" if config.llm.api_key else "Not set", "✓" if config.llm.api_key else "✗")
1043
+
1044
+ console.print(table)
1045
+
1046
+ # Scanner Status
1047
+ table = Table(title="Scanner Configuration")
1048
+ table.add_column("Scanner", style="cyan")
1049
+ table.add_column("URL", style="green")
1050
+ table.add_column("API Key", style="yellow")
1051
+
1052
+ table.add_row(
1053
+ "Acunetix",
1054
+ config.scanners.acunetix_url or "Not configured",
1055
+ "✓" if config.scanners.acunetix_api_key else "✗",
1056
+ )
1057
+ table.add_row(
1058
+ "Burp Suite",
1059
+ config.scanners.burp_url or "Not configured",
1060
+ "✓" if config.scanners.burp_api_key else "✗",
1061
+ )
1062
+ table.add_row(
1063
+ "Nessus",
1064
+ config.scanners.nessus_url or "Not configured",
1065
+ "✓" if config.scanners.nessus_access_key else "✗",
1066
+ )
1067
+ table.add_row(
1068
+ "OWASP ZAP",
1069
+ config.scanners.zap_url or "Not configured",
1070
+ "✓" if config.scanners.zap_api_key else "✗",
1071
+ )
1072
+
1073
+ console.print(table)
1074
+
1075
+ # VPS Status
1076
+ table = Table(title="VPS Configuration")
1077
+ table.add_column("Setting", style="cyan")
1078
+ table.add_column("Value", style="green")
1079
+
1080
+ table.add_row("Host", config.vps.host or "Not configured")
1081
+ table.add_row("User", config.vps.user)
1082
+ table.add_row("SSH Key", config.vps.key_path or "Not configured")
1083
+
1084
+ console.print(table)
1085
+
1086
+ # Check for issues
1087
+ console.print("\n[bold]Configuration Validation:[/bold]")
1088
+
1089
+ all_features = ["llm", "acunetix", "burp", "nessus", "vps"]
1090
+ for feature in all_features:
1091
+ errors = validate_config_for_features([feature])
1092
+ if errors:
1093
+ console.print(f" [yellow]⚠[/yellow] {feature}: {errors[0]}")
1094
+ else:
1095
+ console.print(f" [green]✓[/green] {feature}: Ready")
1096
+
1097
+ return 0
1098
+
1099
+
1100
+ def run_config_test(args):
1101
+ """
1102
+ Test and validate all configurations by making real connections.
1103
+
1104
+ Unlike 'status' which just shows config values, 'test' actually
1105
+ validates that services are reachable and credentials work.
1106
+ """
1107
+ import asyncio
1108
+ import shutil
1109
+ import time
1110
+ from rich.console import Console
1111
+ from rich.panel import Panel
1112
+ from rich.table import Table
1113
+ from rich.progress import Progress, SpinnerColumn, TextColumn
1114
+ from rich import box
1115
+
1116
+ console = Console()
1117
+ config = get_config()
1118
+
1119
+ console.print()
1120
+ console.print(Panel(
1121
+ "[bold]AIPTX Configuration Validator[/bold]\n\n"
1122
+ "Testing all configured services and credentials...",
1123
+ title="🔍 Self-Test",
1124
+ border_style="cyan"
1125
+ ))
1126
+ console.print()
1127
+
1128
+ results = {}
1129
+ test_all = getattr(args, 'all', False) or not any([
1130
+ getattr(args, 'llm', False),
1131
+ getattr(args, 'vps', False),
1132
+ getattr(args, 'scanners', False),
1133
+ getattr(args, 'tools', False),
1134
+ ])
1135
+
1136
+ # ======================== LLM Test ========================
1137
+ if test_all or getattr(args, 'llm', False):
1138
+ console.print("[bold cyan]━━━ LLM API Test ━━━[/bold cyan]")
1139
+
1140
+ if not config.llm.api_key:
1141
+ console.print(" [red]✗[/red] No API key configured")
1142
+ console.print(" [dim]Run 'aiptx setup' to configure[/dim]")
1143
+ results['llm'] = False
1144
+ else:
1145
+ with console.status("[yellow]Testing LLM API connection...[/yellow]"):
1146
+ try:
1147
+ import litellm
1148
+
1149
+ # Determine model string based on provider
1150
+ provider = config.llm.provider.lower()
1151
+ model = config.llm.model
1152
+
1153
+ if provider == "anthropic":
1154
+ model_str = f"anthropic/{model}" if not model.startswith("anthropic/") else model
1155
+ litellm.api_key = config.llm.api_key
1156
+ elif provider == "openai":
1157
+ model_str = f"openai/{model}" if not model.startswith("openai/") else model
1158
+ litellm.api_key = config.llm.api_key
1159
+ elif provider == "deepseek":
1160
+ model_str = f"deepseek/{model}" if not model.startswith("deepseek/") else model
1161
+ litellm.api_key = config.llm.api_key
1162
+ else:
1163
+ model_str = model
1164
+
1165
+ start = time.time()
1166
+ response = litellm.completion(
1167
+ model=model_str,
1168
+ messages=[{"role": "user", "content": "Reply with only: OK"}],
1169
+ max_tokens=10,
1170
+ timeout=30,
1171
+ )
1172
+ elapsed = time.time() - start
1173
+
1174
+ console.print(f" [green]✓[/green] LLM API connection successful")
1175
+ console.print(f" [dim]Provider: {provider}[/dim]")
1176
+ console.print(f" [dim]Model: {model}[/dim]")
1177
+ console.print(f" [dim]Response time: {elapsed:.2f}s[/dim]")
1178
+ results['llm'] = True
1179
+
1180
+ except ImportError:
1181
+ console.print(" [yellow]⚠[/yellow] litellm not installed")
1182
+ console.print(" [dim]Install with: pip install litellm[/dim]")
1183
+ results['llm'] = None
1184
+ except Exception as e:
1185
+ console.print(f" [red]✗[/red] LLM API test failed")
1186
+ console.print(f" [dim]Error: {str(e)[:100]}[/dim]")
1187
+ results['llm'] = False
1188
+
1189
+ console.print()
1190
+
1191
+ # ======================== VPS Test ========================
1192
+ if test_all or getattr(args, 'vps', False):
1193
+ console.print("[bold cyan]━━━ VPS Connection Test ━━━[/bold cyan]")
1194
+
1195
+ if not config.vps.host:
1196
+ console.print(" [yellow]○[/yellow] VPS not configured (optional)")
1197
+ results['vps'] = None
1198
+ elif not config.vps.key_path:
1199
+ console.print(" [red]✗[/red] SSH key path not configured")
1200
+ results['vps'] = False
1201
+ else:
1202
+ with console.status("[yellow]Testing SSH connection to VPS...[/yellow]"):
1203
+ try:
1204
+ from pathlib import Path
1205
+
1206
+ key_path = Path(config.vps.key_path).expanduser()
1207
+ if not key_path.exists():
1208
+ console.print(f" [red]✗[/red] SSH key not found: {key_path}")
1209
+ results['vps'] = False
1210
+ else:
1211
+ # Test SSH connection using asyncssh
1212
+ async def test_ssh():
1213
+ import asyncssh
1214
+ start = time.time()
1215
+ conn = await asyncssh.connect(
1216
+ config.vps.host,
1217
+ port=config.vps.port,
1218
+ username=config.vps.user,
1219
+ client_keys=[str(key_path)],
1220
+ known_hosts=None,
1221
+ )
1222
+ # Run a simple command to verify
1223
+ result = await conn.run("echo 'AIPTX_TEST_OK' && uname -a", check=True)
1224
+ await conn.close()
1225
+ elapsed = time.time() - start
1226
+ return result.stdout.strip(), elapsed
1227
+
1228
+ output, elapsed = asyncio.run(test_ssh())
1229
+
1230
+ if "AIPTX_TEST_OK" in output:
1231
+ uname = output.replace("AIPTX_TEST_OK", "").strip()
1232
+ console.print(f" [green]✓[/green] VPS connection successful")
1233
+ console.print(f" [dim]Host: {config.vps.user}@{config.vps.host}:{config.vps.port}[/dim]")
1234
+ console.print(f" [dim]System: {uname[:60]}...[/dim]" if len(uname) > 60 else f" [dim]System: {uname}[/dim]")
1235
+ console.print(f" [dim]Response time: {elapsed:.2f}s[/dim]")
1236
+ results['vps'] = True
1237
+ else:
1238
+ console.print(f" [red]✗[/red] VPS connection failed - unexpected response")
1239
+ results['vps'] = False
1240
+
1241
+ except ImportError:
1242
+ console.print(" [yellow]⚠[/yellow] asyncssh not installed")
1243
+ console.print(" [dim]Install with: pip install asyncssh[/dim]")
1244
+ results['vps'] = None
1245
+ except Exception as e:
1246
+ console.print(f" [red]✗[/red] VPS connection failed")
1247
+ console.print(f" [dim]Error: {str(e)[:100]}[/dim]")
1248
+ results['vps'] = False
1249
+
1250
+ console.print()
1251
+
1252
+ # ======================== Scanner Tests ========================
1253
+ if test_all or getattr(args, 'scanners', False):
1254
+ console.print("[bold cyan]━━━ Scanner Integration Tests ━━━[/bold cyan]")
1255
+
1256
+ scanners_tested = 0
1257
+
1258
+ # Acunetix
1259
+ if config.scanners.acunetix_url:
1260
+ scanners_tested += 1
1261
+ with console.status("[yellow]Testing Acunetix connection...[/yellow]"):
1262
+ try:
1263
+ import httpx
1264
+ response = httpx.get(
1265
+ f"{config.scanners.acunetix_url}/api/v1/me",
1266
+ headers={"X-Auth": config.scanners.acunetix_api_key},
1267
+ verify=False,
1268
+ timeout=10,
1269
+ )
1270
+ if response.status_code == 200:
1271
+ console.print(f" [green]✓[/green] Acunetix connected")
1272
+ console.print(f" [dim]URL: {config.scanners.acunetix_url}[/dim]")
1273
+ results['acunetix'] = True
1274
+ else:
1275
+ console.print(f" [red]✗[/red] Acunetix auth failed (HTTP {response.status_code})")
1276
+ results['acunetix'] = False
1277
+ except Exception as e:
1278
+ console.print(f" [red]✗[/red] Acunetix connection failed: {str(e)[:50]}")
1279
+ results['acunetix'] = False
1280
+
1281
+ # Burp Suite
1282
+ if config.scanners.burp_url:
1283
+ scanners_tested += 1
1284
+ with console.status("[yellow]Testing Burp Suite connection...[/yellow]"):
1285
+ try:
1286
+ import httpx
1287
+ response = httpx.get(
1288
+ f"{config.scanners.burp_url}/api-internal/versions",
1289
+ headers={"Authorization": f"Bearer {config.scanners.burp_api_key}"},
1290
+ verify=False,
1291
+ timeout=10,
1292
+ )
1293
+ if response.status_code == 200:
1294
+ console.print(f" [green]✓[/green] Burp Suite connected")
1295
+ console.print(f" [dim]URL: {config.scanners.burp_url}[/dim]")
1296
+ results['burp'] = True
1297
+ else:
1298
+ console.print(f" [red]✗[/red] Burp Suite auth failed (HTTP {response.status_code})")
1299
+ results['burp'] = False
1300
+ except Exception as e:
1301
+ console.print(f" [red]✗[/red] Burp Suite connection failed: {str(e)[:50]}")
1302
+ results['burp'] = False
1303
+
1304
+ # Nessus
1305
+ if config.scanners.nessus_url:
1306
+ scanners_tested += 1
1307
+ with console.status("[yellow]Testing Nessus connection...[/yellow]"):
1308
+ try:
1309
+ import httpx
1310
+ response = httpx.get(
1311
+ f"{config.scanners.nessus_url}/server/status",
1312
+ headers={
1313
+ "X-ApiKeys": f"accessKey={config.scanners.nessus_access_key};secretKey={config.scanners.nessus_secret_key}"
1314
+ },
1315
+ verify=False,
1316
+ timeout=10,
1317
+ )
1318
+ if response.status_code == 200:
1319
+ console.print(f" [green]✓[/green] Nessus connected")
1320
+ console.print(f" [dim]URL: {config.scanners.nessus_url}[/dim]")
1321
+ results['nessus'] = True
1322
+ else:
1323
+ console.print(f" [red]✗[/red] Nessus auth failed (HTTP {response.status_code})")
1324
+ results['nessus'] = False
1325
+ except Exception as e:
1326
+ console.print(f" [red]✗[/red] Nessus connection failed: {str(e)[:50]}")
1327
+ results['nessus'] = False
1328
+
1329
+ # ZAP
1330
+ if config.scanners.zap_url:
1331
+ scanners_tested += 1
1332
+ with console.status("[yellow]Testing OWASP ZAP connection...[/yellow]"):
1333
+ try:
1334
+ import httpx
1335
+ url = f"{config.scanners.zap_url}/JSON/core/view/version/"
1336
+ if config.scanners.zap_api_key:
1337
+ url += f"?apikey={config.scanners.zap_api_key}"
1338
+ response = httpx.get(url, timeout=10)
1339
+ if response.status_code == 200:
1340
+ version = response.json().get("version", "unknown")
1341
+ console.print(f" [green]✓[/green] OWASP ZAP connected (v{version})")
1342
+ console.print(f" [dim]URL: {config.scanners.zap_url}[/dim]")
1343
+ results['zap'] = True
1344
+ else:
1345
+ console.print(f" [red]✗[/red] ZAP connection failed (HTTP {response.status_code})")
1346
+ results['zap'] = False
1347
+ except Exception as e:
1348
+ console.print(f" [red]✗[/red] ZAP connection failed: {str(e)[:50]}")
1349
+ results['zap'] = False
1350
+
1351
+ if scanners_tested == 0:
1352
+ console.print(" [yellow]○[/yellow] No scanners configured (optional)")
1353
+
1354
+ console.print()
1355
+
1356
+ # ======================== Local Tools Test ========================
1357
+ if test_all or getattr(args, 'tools', False):
1358
+ console.print("[bold cyan]━━━ Local Security Tools ━━━[/bold cyan]")
1359
+
1360
+ tools = {
1361
+ "nmap": "nmap --version",
1362
+ "subfinder": "subfinder -version",
1363
+ "httpx": "httpx -version",
1364
+ "nuclei": "nuclei -version",
1365
+ "ffuf": "ffuf -V",
1366
+ "gobuster": "gobuster version",
1367
+ "nikto": "nikto -Version",
1368
+ "sqlmap": "sqlmap --version",
1369
+ "wpscan": "wpscan --version",
1370
+ "amass": "amass -version",
1371
+ }
1372
+
1373
+ found_tools = []
1374
+ missing_tools = []
1375
+
1376
+ for tool, check_cmd in tools.items():
1377
+ if shutil.which(tool):
1378
+ found_tools.append(tool)
1379
+ else:
1380
+ missing_tools.append(tool)
1381
+
1382
+ if found_tools:
1383
+ console.print(f" [green]✓[/green] Available: {', '.join(found_tools)}")
1384
+
1385
+ if missing_tools:
1386
+ console.print(f" [yellow]○[/yellow] Not found: {', '.join(missing_tools)}")
1387
+ console.print(" [dim]Install missing tools or use --use-vps to run on VPS[/dim]")
1388
+
1389
+ results['tools'] = len(found_tools)
1390
+ console.print()
1391
+
1392
+ # ======================== Summary ========================
1393
+ console.print("[bold cyan]━━━ Test Summary ━━━[/bold cyan]")
1394
+
1395
+ table = Table(box=box.ROUNDED)
1396
+ table.add_column("Component", style="cyan")
1397
+ table.add_column("Status", justify="center")
1398
+ table.add_column("Details")
1399
+
1400
+ for component, status in results.items():
1401
+ if status is True:
1402
+ status_str = "[green]✓ PASS[/green]"
1403
+ details = "Working correctly"
1404
+ elif status is False:
1405
+ status_str = "[red]✗ FAIL[/red]"
1406
+ details = "Check configuration"
1407
+ elif status is None:
1408
+ status_str = "[yellow]○ SKIP[/yellow]"
1409
+ details = "Not configured"
1410
+ elif isinstance(status, int):
1411
+ status_str = f"[green]✓ {status}[/green]"
1412
+ details = f"{status} tools available"
1413
+ else:
1414
+ status_str = "[dim]?[/dim]"
1415
+ details = "Unknown"
1416
+
1417
+ table.add_row(component.upper(), status_str, details)
1418
+
1419
+ console.print(table)
1420
+
1421
+ # Overall result
1422
+ failures = sum(1 for v in results.values() if v is False)
1423
+ if failures == 0:
1424
+ console.print("\n[bold green]✓ All tests passed![/bold green]")
1425
+ return 0
1426
+ else:
1427
+ console.print(f"\n[bold yellow]⚠ {failures} test(s) failed. Run 'aiptx setup' to fix.[/bold yellow]")
1428
+ return 1
1429
+
1430
+
1431
+ def run_preflight_check(console, use_vps=False, use_acunetix=False, use_burp=False, ai_mode=False):
1432
+ """
1433
+ Run pre-flight checks before starting a scan.
1434
+
1435
+ Validates that all required components are configured and reachable.
1436
+ Returns True if all checks pass, False otherwise.
1437
+
1438
+ Args:
1439
+ console: Rich console for output
1440
+ use_vps: Whether VPS will be used for the scan
1441
+ use_acunetix: Whether Acunetix scanner will be used
1442
+ use_burp: Whether Burp Suite will be used
1443
+ ai_mode: Whether AI-guided scanning is enabled
1444
+
1445
+ Returns:
1446
+ bool: True if all checks pass, False if any fail
1447
+ """
1448
+ import asyncio
1449
+ import shutil
1450
+ import time
1451
+ from rich.panel import Panel
1452
+ from rich.table import Table
1453
+ from rich import box
1454
+
1455
+ config = get_config()
1456
+ results = {}
1457
+ all_passed = True
1458
+
1459
+ console.print()
1460
+ console.print(Panel(
1461
+ "[bold]Pre-flight Configuration Check[/bold]\n\n"
1462
+ "Validating all required services before scan...",
1463
+ title="✈️ Pre-flight Check",
1464
+ border_style="cyan"
1465
+ ))
1466
+ console.print()
1467
+
1468
+ # ======================== LLM Check (always required) ========================
1469
+ console.print("[bold cyan]━━━ LLM API ━━━[/bold cyan]")
1470
+
1471
+ if not config.llm.api_key:
1472
+ console.print(" [red]✗[/red] No API key configured")
1473
+ console.print(" [dim]Run 'aiptx setup' to configure[/dim]")
1474
+ results['llm'] = False
1475
+ all_passed = False
1476
+ else:
1477
+ with console.status("[yellow]Testing LLM API...[/yellow]"):
1478
+ try:
1479
+ import litellm
1480
+
1481
+ provider = config.llm.provider.lower()
1482
+ model = config.llm.model
1483
+
1484
+ if provider == "anthropic":
1485
+ model_str = f"anthropic/{model}" if not model.startswith("anthropic/") else model
1486
+ elif provider == "openai":
1487
+ model_str = f"openai/{model}" if not model.startswith("openai/") else model
1488
+ elif provider == "deepseek":
1489
+ model_str = f"deepseek/{model}" if not model.startswith("deepseek/") else model
1490
+ else:
1491
+ model_str = model
1492
+
1493
+ start = time.time()
1494
+ response = litellm.completion(
1495
+ model=model_str,
1496
+ messages=[{"role": "user", "content": "Reply with only: OK"}],
1497
+ max_tokens=10,
1498
+ timeout=30,
1499
+ )
1500
+ elapsed = time.time() - start
1501
+
1502
+ console.print(f" [green]✓[/green] LLM ready ({provider}/{model}) - {elapsed:.1f}s")
1503
+ results['llm'] = True
1504
+
1505
+ except ImportError:
1506
+ console.print(" [yellow]⚠[/yellow] litellm not installed")
1507
+ results['llm'] = None
1508
+ except Exception as e:
1509
+ console.print(f" [red]✗[/red] LLM connection failed: {str(e)[:60]}")
1510
+ results['llm'] = False
1511
+ all_passed = False
1512
+
1513
+ console.print()
1514
+
1515
+ # ======================== VPS Check (if requested) ========================
1516
+ if use_vps:
1517
+ console.print("[bold cyan]━━━ VPS Connection ━━━[/bold cyan]")
1518
+
1519
+ if not config.vps.host:
1520
+ console.print(" [red]✗[/red] VPS not configured")
1521
+ console.print(" [dim]Run 'aiptx setup' to configure VPS[/dim]")
1522
+ results['vps'] = False
1523
+ all_passed = False
1524
+ elif not config.vps.key_path:
1525
+ console.print(" [red]✗[/red] SSH key path not configured")
1526
+ results['vps'] = False
1527
+ all_passed = False
1528
+ else:
1529
+ with console.status("[yellow]Testing SSH connection...[/yellow]"):
1530
+ try:
1531
+ from pathlib import Path
1532
+ import asyncssh
1533
+
1534
+ key_path = Path(config.vps.key_path).expanduser()
1535
+ if not key_path.exists():
1536
+ console.print(f" [red]✗[/red] SSH key not found: {key_path}")
1537
+ results['vps'] = False
1538
+ all_passed = False
1539
+ else:
1540
+ async def test_ssh():
1541
+ start = time.time()
1542
+ conn = await asyncssh.connect(
1543
+ config.vps.host,
1544
+ port=config.vps.port,
1545
+ username=config.vps.user,
1546
+ client_keys=[str(key_path)],
1547
+ known_hosts=None,
1548
+ )
1549
+ await conn.close()
1550
+ return time.time() - start
1551
+
1552
+ elapsed = asyncio.run(test_ssh())
1553
+ console.print(f" [green]✓[/green] VPS connected ({config.vps.user}@{config.vps.host}) - {elapsed:.1f}s")
1554
+ results['vps'] = True
1555
+
1556
+ except ImportError:
1557
+ console.print(" [yellow]⚠[/yellow] asyncssh not installed")
1558
+ console.print(" [dim]Install with: pip install asyncssh[/dim]")
1559
+ results['vps'] = None
1560
+ except Exception as e:
1561
+ console.print(f" [red]✗[/red] VPS connection failed: {str(e)[:60]}")
1562
+ results['vps'] = False
1563
+ all_passed = False
1564
+
1565
+ console.print()
1566
+
1567
+ # ======================== Scanner Checks (if requested) ========================
1568
+ if use_acunetix or use_burp:
1569
+ console.print("[bold cyan]━━━ Scanner Integrations ━━━[/bold cyan]")
1570
+
1571
+ # Acunetix
1572
+ if use_acunetix:
1573
+ if not config.scanners.acunetix_url:
1574
+ console.print(" [red]✗[/red] Acunetix URL not configured")
1575
+ results['acunetix'] = False
1576
+ all_passed = False
1577
+ else:
1578
+ with console.status("[yellow]Testing Acunetix...[/yellow]"):
1579
+ try:
1580
+ import httpx
1581
+ response = httpx.get(
1582
+ f"{config.scanners.acunetix_url}/api/v1/me",
1583
+ headers={"X-Auth": config.scanners.acunetix_api_key or ""},
1584
+ verify=False,
1585
+ timeout=10,
1586
+ )
1587
+ if response.status_code == 200:
1588
+ console.print(f" [green]✓[/green] Acunetix connected")
1589
+ results['acunetix'] = True
1590
+ else:
1591
+ console.print(f" [red]✗[/red] Acunetix auth failed (HTTP {response.status_code})")
1592
+ results['acunetix'] = False
1593
+ all_passed = False
1594
+ except Exception as e:
1595
+ console.print(f" [red]✗[/red] Acunetix failed: {str(e)[:50]}")
1596
+ results['acunetix'] = False
1597
+ all_passed = False
1598
+
1599
+ # Burp Suite
1600
+ if use_burp:
1601
+ if not config.scanners.burp_url:
1602
+ console.print(" [red]✗[/red] Burp Suite URL not configured")
1603
+ results['burp'] = False
1604
+ all_passed = False
1605
+ else:
1606
+ with console.status("[yellow]Testing Burp Suite...[/yellow]"):
1607
+ try:
1608
+ import httpx
1609
+ response = httpx.get(
1610
+ f"{config.scanners.burp_url}/api-internal/versions",
1611
+ headers={"Authorization": f"Bearer {config.scanners.burp_api_key or ''}"},
1612
+ verify=False,
1613
+ timeout=10,
1614
+ )
1615
+ if response.status_code == 200:
1616
+ console.print(f" [green]✓[/green] Burp Suite connected")
1617
+ results['burp'] = True
1618
+ else:
1619
+ console.print(f" [red]✗[/red] Burp Suite auth failed (HTTP {response.status_code})")
1620
+ results['burp'] = False
1621
+ all_passed = False
1622
+ except Exception as e:
1623
+ console.print(f" [red]✗[/red] Burp Suite failed: {str(e)[:50]}")
1624
+ results['burp'] = False
1625
+ all_passed = False
1626
+
1627
+ console.print()
1628
+
1629
+ # ======================== Local Tools Check ========================
1630
+ console.print("[bold cyan]━━━ Local Security Tools ━━━[/bold cyan]")
1631
+
1632
+ essential_tools = ["nmap", "httpx", "nuclei"]
1633
+ optional_tools = ["subfinder", "ffuf", "nikto"]
1634
+
1635
+ found_essential = []
1636
+ missing_essential = []
1637
+
1638
+ for tool in essential_tools:
1639
+ if shutil.which(tool):
1640
+ found_essential.append(tool)
1641
+ else:
1642
+ missing_essential.append(tool)
1643
+
1644
+ found_optional = [t for t in optional_tools if shutil.which(t)]
1645
+
1646
+ if found_essential:
1647
+ console.print(f" [green]✓[/green] Essential: {', '.join(found_essential)}")
1648
+ if missing_essential:
1649
+ console.print(f" [yellow]⚠[/yellow] Missing essential: {', '.join(missing_essential)}")
1650
+ if not use_vps:
1651
+ console.print(" [dim]Consider using --use-vps or install locally[/dim]")
1652
+ if found_optional:
1653
+ console.print(f" [dim]○[/dim] Optional available: {', '.join(found_optional)}")
1654
+
1655
+ results['tools'] = len(missing_essential) == 0 or use_vps
1656
+
1657
+ console.print()
1658
+
1659
+ # ======================== Summary ========================
1660
+ console.print("[bold cyan]━━━ Pre-flight Summary ━━━[/bold cyan]")
1661
+
1662
+ table = Table(box=box.ROUNDED, show_header=False)
1663
+ table.add_column("Component", style="cyan")
1664
+ table.add_column("Status", justify="center")
1665
+
1666
+ for component, status in results.items():
1667
+ if status is True:
1668
+ status_str = "[green]✓ READY[/green]"
1669
+ elif status is False:
1670
+ status_str = "[red]✗ FAILED[/red]"
1671
+ elif status is None:
1672
+ status_str = "[yellow]○ SKIPPED[/yellow]"
1673
+ else:
1674
+ status_str = "[dim]?[/dim]"
1675
+ table.add_row(component.upper(), status_str)
1676
+
1677
+ console.print(table)
1678
+ console.print()
1679
+
1680
+ if all_passed:
1681
+ console.print("[bold green]✓ All pre-flight checks passed! Ready to scan.[/bold green]")
1682
+ else:
1683
+ console.print("[bold red]✗ Some checks failed. Fix issues above before scanning.[/bold red]")
1684
+ console.print("[dim]Run 'aiptx setup' to configure missing components.[/dim]")
1685
+
1686
+ console.print()
1687
+
1688
+ return all_passed
1689
+
1690
+
1691
+ def show_version():
1692
+ """Show detailed version information."""
1693
+ from rich.console import Console
1694
+ from rich.panel import Panel
1695
+
1696
+ console = Console()
1697
+
1698
+ info = f"""
1699
+ [bold cyan]AIPT v2 - AI-Powered Penetration Testing Framework[/bold cyan]
1700
+ Version: {__version__}
1701
+
1702
+ [bold]Components:[/bold]
1703
+ • LLM Integration (litellm)
1704
+ • Scanner Integration (Acunetix, Burp, Nessus, ZAP)
1705
+ • VPS Execution Support
1706
+ • AI-Guided Scanning
1707
+ • Professional Report Generation
1708
+
1709
+ [bold]Documentation:[/bold]
1710
+ https://github.com/aipt/aipt-v2
1711
+
1712
+ [bold]Author:[/bold]
1713
+ Satyam Rastogi
1714
+ """
1715
+
1716
+ console.print(Panel(info, title="Version Information", border_style="cyan"))
1717
+
1718
+ return 0
1719
+
1720
+
1721
+ def run_verify(args):
1722
+ """Verify AIPTX installation."""
1723
+ import asyncio
1724
+
1725
+ from aipt_v2.verify_install import verify_installation
1726
+
1727
+ quick = getattr(args, 'quick', False)
1728
+ auto_fix = getattr(args, 'fix', False)
1729
+ report_file = getattr(args, 'report', None)
1730
+
1731
+ return asyncio.run(verify_installation(
1732
+ quick=quick,
1733
+ auto_fix=auto_fix,
1734
+ report_file=report_file,
1735
+ ))
1736
+
1737
+
1738
+ def run_shell(args):
1739
+ """Start the interactive security shell."""
1740
+ import asyncio
1741
+
1742
+ from aipt_v2.interactive_shell import start_interactive_shell
1743
+
1744
+ log_file = getattr(args, 'log', None)
1745
+ working_dir = getattr(args, 'dir', None)
1746
+
1747
+ return asyncio.run(start_interactive_shell(
1748
+ log_file=log_file,
1749
+ working_dir=working_dir,
1750
+ ))
1751
+
1752
+
1753
+ def run_tools_command(args):
1754
+ """Handle tools subcommands for local tool management."""
1755
+ import asyncio
1756
+ from rich.console import Console
1757
+ from rich.panel import Panel
1758
+ from rich.table import Table
1759
+ from rich import box
1760
+
1761
+ console = Console()
1762
+
1763
+ tools_cmd = getattr(args, 'tools_command', None)
1764
+
1765
+ if tools_cmd == "install":
1766
+ return run_tools_install(args, console)
1767
+ elif tools_cmd == "list":
1768
+ return run_tools_list(args, console)
1769
+ elif tools_cmd == "check":
1770
+ return run_tools_check(args, console)
1771
+ else:
1772
+ console.print(Panel(
1773
+ "[bold cyan]AIPTX Local Tools Management[/bold cyan]\n\n"
1774
+ "[bold]aiptx tools install[/bold] - Install security tools locally\n"
1775
+ "[bold]aiptx tools list[/bold] - List available/installed tools\n"
1776
+ "[bold]aiptx tools check[/bold] - Check installed tool status\n\n"
1777
+ "[dim]Examples:[/dim]\n"
1778
+ " aiptx tools install --core # Install core tools\n"
1779
+ " aiptx tools install -c recon scan # Install by category\n"
1780
+ " aiptx tools install -t nmap nuclei # Install specific tools",
1781
+ title="🔧 Local Security Tools",
1782
+ border_style="cyan",
1783
+ ))
1784
+ return 0
1785
+
1786
+
1787
+ def run_tools_install(args, console):
1788
+ """Install security tools on local system."""
1789
+ import asyncio
1790
+ from rich.panel import Panel
1791
+
1792
+ console.print()
1793
+ console.print(Panel(
1794
+ "[bold cyan]Local Tool Installation[/bold cyan]\n\n"
1795
+ "Installing security tools on your local system.\n"
1796
+ "Some tools may require sudo/admin privileges.",
1797
+ title="🔧 Installation",
1798
+ border_style="cyan",
1799
+ ))
1800
+ console.print()
1801
+
1802
+ async def _install():
1803
+ try:
1804
+ from aipt_v2.system_detector import SystemDetector
1805
+ from aipt_v2.local_tool_installer import LocalToolInstaller, TOOLS
1806
+
1807
+ # Detect system first
1808
+ detector = SystemDetector()
1809
+ with console.status("[bold cyan]Detecting system...[/bold cyan]"):
1810
+ system_info = await detector.detect()
1811
+
1812
+ console.print(f"[dim]Detected: {system_info.os_name} with {system_info.package_manager.value}[/dim]")
1813
+
1814
+ installer = LocalToolInstaller(system_info)
1815
+ use_sudo = not getattr(args, 'no_sudo', False)
1816
+
1817
+ # Determine what to install
1818
+ if getattr(args, 'all', False):
1819
+ console.print("[cyan]Installing all available tools...[/cyan]")
1820
+ results = await installer.install_all()
1821
+ elif getattr(args, 'tools', None):
1822
+ console.print(f"[cyan]Installing specific tools: {', '.join(args.tools)}[/cyan]")
1823
+ results = await installer.install_tools(tools=args.tools, use_sudo=use_sudo)
1824
+ elif getattr(args, 'categories', None):
1825
+ console.print(f"[cyan]Installing categories: {', '.join(args.categories)}[/cyan]")
1826
+ results = await installer.install_tools(categories=args.categories, use_sudo=use_sudo)
1827
+ else:
1828
+ # Default: core tools
1829
+ console.print("[cyan]Installing core security tools...[/cyan]")
1830
+ results = await installer.install_core_tools()
1831
+
1832
+ # Show summary
1833
+ installer.print_tool_status(results)
1834
+ return 0
1835
+
1836
+ except ImportError as e:
1837
+ console.print(f"[red]Error: Missing dependency - {e}[/red]")
1838
+ return 1
1839
+ except Exception as e:
1840
+ console.print(f"[red]Error during installation: {e}[/red]")
1841
+ return 1
1842
+
1843
+ loop = asyncio.new_event_loop()
1844
+ asyncio.set_event_loop(loop)
1845
+ try:
1846
+ return loop.run_until_complete(_install())
1847
+ finally:
1848
+ loop.close()
1849
+
1850
+
1851
+ def run_tools_list(args, console):
1852
+ """List available and installed tools."""
1853
+ import asyncio
1854
+ from rich.table import Table
1855
+ from rich import box
1856
+
1857
+ async def _list():
1858
+ try:
1859
+ from aipt_v2.local_tool_installer import LocalToolInstaller, TOOLS
1860
+
1861
+ installer = LocalToolInstaller()
1862
+ installed = await installer.get_installed_tools()
1863
+
1864
+ category = getattr(args, 'category', 'all')
1865
+ installed_only = getattr(args, 'installed_only', False)
1866
+
1867
+ table = Table(title="Security Tools", box=box.ROUNDED)
1868
+ table.add_column("Tool", style="cyan")
1869
+ table.add_column("Category", style="dim")
1870
+ table.add_column("Status", justify="center")
1871
+ table.add_column("Description")
1872
+
1873
+ for tool_name, tool_def in sorted(TOOLS.items()):
1874
+ if category != 'all' and tool_def.category.value != category:
1875
+ continue
1876
+
1877
+ is_installed = installed.get(tool_name, False)
1878
+
1879
+ if installed_only and not is_installed:
1880
+ continue
1881
+
1882
+ status = "[green]✓ Installed[/green]" if is_installed else "[dim]○ Not installed[/dim]"
1883
+ core_badge = " [yellow]★[/yellow]" if tool_def.is_core else ""
1884
+
1885
+ table.add_row(
1886
+ f"{tool_name}{core_badge}",
1887
+ tool_def.category.value,
1888
+ status,
1889
+ tool_def.description[:50] + "..." if len(tool_def.description) > 50 else tool_def.description
1890
+ )
1891
+
1892
+ console.print()
1893
+ console.print(table)
1894
+ console.print()
1895
+ console.print("[dim]Legend: [yellow]★[/yellow] = Core tool (recommended)[/dim]")
1896
+
1897
+ return 0
1898
+
1899
+ except ImportError as e:
1900
+ console.print(f"[red]Error: {e}[/red]")
1901
+ return 1
1902
+
1903
+ loop = asyncio.new_event_loop()
1904
+ asyncio.set_event_loop(loop)
1905
+ try:
1906
+ return loop.run_until_complete(_list())
1907
+ finally:
1908
+ loop.close()
1909
+
1910
+
1911
+ def run_tools_check(args, console):
1912
+ """Check installed tool status."""
1913
+ import asyncio
1914
+ from rich.table import Table
1915
+ from rich.panel import Panel
1916
+ from rich import box
1917
+
1918
+ async def _check():
1919
+ try:
1920
+ from aipt_v2.local_tool_installer import LocalToolInstaller, TOOLS
1921
+
1922
+ console.print()
1923
+ with console.status("[bold cyan]Checking installed tools...[/bold cyan]"):
1924
+ installer = LocalToolInstaller()
1925
+ installed = await installer.get_installed_tools()
1926
+
1927
+ # Count by category
1928
+ categories = {}
1929
+ for tool_name, tool_def in TOOLS.items():
1930
+ cat = tool_def.category.value
1931
+ if cat not in categories:
1932
+ categories[cat] = {"total": 0, "installed": 0}
1933
+ categories[cat]["total"] += 1
1934
+ if installed.get(tool_name, False):
1935
+ categories[cat]["installed"] += 1
1936
+
1937
+ # Summary table
1938
+ table = Table(box=box.ROUNDED)
1939
+ table.add_column("Category", style="cyan")
1940
+ table.add_column("Installed", justify="right", style="green")
1941
+ table.add_column("Total", justify="right")
1942
+ table.add_column("Coverage", justify="right")
1943
+
1944
+ total_installed = 0
1945
+ total_tools = 0
1946
+
1947
+ for cat, counts in sorted(categories.items()):
1948
+ coverage = (counts["installed"] / counts["total"] * 100) if counts["total"] > 0 else 0
1949
+ coverage_color = "green" if coverage >= 75 else "yellow" if coverage >= 50 else "red"
1950
+
1951
+ table.add_row(
1952
+ cat,
1953
+ str(counts["installed"]),
1954
+ str(counts["total"]),
1955
+ f"[{coverage_color}]{coverage:.0f}%[/{coverage_color}]"
1956
+ )
1957
+
1958
+ total_installed += counts["installed"]
1959
+ total_tools += counts["total"]
1960
+
1961
+ table.add_row("─" * 15, "─" * 5, "─" * 5, "─" * 7)
1962
+ total_coverage = (total_installed / total_tools * 100) if total_tools > 0 else 0
1963
+ table.add_row(
1964
+ "[bold]Total[/bold]",
1965
+ f"[bold]{total_installed}[/bold]",
1966
+ f"[bold]{total_tools}[/bold]",
1967
+ f"[bold]{total_coverage:.0f}%[/bold]"
1968
+ )
1969
+
1970
+ console.print()
1971
+ console.print(table)
1972
+ console.print()
1973
+
1974
+ if total_coverage < 50:
1975
+ console.print(Panel(
1976
+ "[yellow]Many security tools are not installed.[/yellow]\n\n"
1977
+ "Run [bold]aiptx tools install --core[/bold] to install essential tools.",
1978
+ title="💡 Recommendation",
1979
+ border_style="yellow"
1980
+ ))
1981
+
1982
+ return 0
1983
+
1984
+ except ImportError as e:
1985
+ console.print(f"[red]Error: {e}[/red]")
1986
+ return 1
1987
+
1988
+ loop = asyncio.new_event_loop()
1989
+ asyncio.set_event_loop(loop)
1990
+ try:
1991
+ return loop.run_until_complete(_check())
1992
+ finally:
1993
+ loop.close()
1994
+
1995
+
1996
+ def run_vps_command(args):
1997
+ """Handle VPS subcommands."""
1998
+ from rich.console import Console
1999
+ from rich.panel import Panel
2000
+ from rich.table import Table
2001
+ from rich.progress import Progress, SpinnerColumn, TextColumn
2002
+
2003
+ console = Console()
2004
+
2005
+ # Check VPS configuration
2006
+ config = get_config()
2007
+ if not config.vps.host:
2008
+ console.print(Panel(
2009
+ "[bold red]VPS not configured![/bold red]\n\n"
2010
+ "Run [bold green]aiptx setup[/bold green] to configure VPS settings.\n\n"
2011
+ "[bold]Required settings:[/bold]\n"
2012
+ " • VPS_HOST - VPS IP or hostname\n"
2013
+ " • VPS_USER - SSH username (default: ubuntu)\n"
2014
+ " • VPS_KEY - Path to SSH private key",
2015
+ title="⚠️ VPS Configuration Required",
2016
+ border_style="yellow",
2017
+ ))
2018
+ return 1
2019
+
2020
+ vps_cmd = getattr(args, 'vps_command', None)
2021
+
2022
+ if vps_cmd == "setup":
2023
+ return run_vps_setup(args, console)
2024
+ elif vps_cmd == "status":
2025
+ return run_vps_status(args, console)
2026
+ elif vps_cmd == "scan":
2027
+ return run_vps_scan(args, console)
2028
+ elif vps_cmd == "script":
2029
+ return run_vps_script(args, console)
2030
+ else:
2031
+ console.print(Panel(
2032
+ "[bold cyan]AIPTX VPS Commands[/bold cyan]\n\n"
2033
+ "[bold]aiptx vps setup[/bold] - Install security tools on VPS\n"
2034
+ "[bold]aiptx vps status[/bold] - Check VPS connection and tools\n"
2035
+ "[bold]aiptx vps scan[/bold] - Run security scan from VPS\n"
2036
+ "[bold]aiptx vps script[/bold] - Generate setup script",
2037
+ title="🖥️ VPS Remote Execution",
2038
+ border_style="cyan",
2039
+ ))
2040
+ return 0
2041
+
2042
+
2043
+ def run_vps_setup(args, console):
2044
+ """Install security tools on VPS with real-time progress."""
2045
+ from rich.live import Live
2046
+ from rich.table import Table
2047
+ from rich.panel import Panel
2048
+ from rich.text import Text
2049
+ from rich.console import Group
2050
+ from rich.spinner import Spinner
2051
+ from rich import box
2052
+
2053
+ # Check for asyncssh FIRST before any VPS operations
2054
+ try:
2055
+ import asyncssh
2056
+ except ImportError:
2057
+ console.print()
2058
+ console.print(Panel(
2059
+ "[bold red]Missing Dependency: asyncssh[/bold red]\n\n"
2060
+ "The VPS module requires asyncssh for SSH connectivity.\n\n"
2061
+ "[bold]Install with:[/bold]\n"
2062
+ " [green]pip install asyncssh[/green]\n"
2063
+ " [dim]or[/dim]\n"
2064
+ " [green]pip install aiptx[vps][/green]\n"
2065
+ " [dim]or[/dim]\n"
2066
+ " [green]pip install aiptx[full][/green]",
2067
+ title="⚠️ Dependency Required",
2068
+ border_style="yellow",
2069
+ ))
2070
+ console.print()
2071
+ return 1
2072
+
2073
+ from aipt_v2.runtime.vps import VPSRuntime, VPS_TOOLS
2074
+
2075
+ console.print()
2076
+ console.print(Panel(
2077
+ "[bold cyan]VPS Tool Installation[/bold cyan]\n\n"
2078
+ "Installing security tools on your VPS.\n"
2079
+ "This may take 10-30 minutes depending on your VPS speed.",
2080
+ title="🔧 Setup",
2081
+ border_style="cyan",
2082
+ ))
2083
+ console.print()
2084
+
2085
+ # Get categories and tools to install
2086
+ categories = getattr(args, 'categories', None)
2087
+ specific_tools = getattr(args, 'tools', None)
2088
+
2089
+ # Build list of tools to install
2090
+ tools_to_install = []
2091
+ if specific_tools:
2092
+ tools_to_install = specific_tools
2093
+ elif categories:
2094
+ for cat in categories:
2095
+ if cat in VPS_TOOLS:
2096
+ tools_to_install.extend(VPS_TOOLS[cat].keys())
2097
+ else:
2098
+ for cat_tools in VPS_TOOLS.values():
2099
+ tools_to_install.extend(cat_tools.keys())
2100
+
2101
+ # State for live display
2102
+ state = {
2103
+ "status": "Connecting...",
2104
+ "current_tool": "",
2105
+ "output": [],
2106
+ "results": {},
2107
+ "total": len(tools_to_install),
2108
+ "completed": 0,
2109
+ }
2110
+
2111
+ def make_display():
2112
+ """Generate the live display."""
2113
+ lines = []
2114
+
2115
+ # Status line
2116
+ status_text = Text()
2117
+ status_text.append("⚡ ", style="yellow")
2118
+ status_text.append(state["status"], style="bold cyan")
2119
+ lines.append(status_text)
2120
+
2121
+ # Progress
2122
+ if state["total"] > 0:
2123
+ pct = (state["completed"] / state["total"]) * 100
2124
+ bar_width = 40
2125
+ filled = int(bar_width * state["completed"] / state["total"])
2126
+ bar = "█" * filled + "░" * (bar_width - filled)
2127
+ progress_text = Text()
2128
+ progress_text.append(f" [{bar}] ", style="cyan")
2129
+ progress_text.append(f"{state['completed']}/{state['total']} ", style="bold")
2130
+ progress_text.append(f"({pct:.0f}%)", style="dim")
2131
+ lines.append(progress_text)
2132
+
2133
+ # Current tool
2134
+ if state["current_tool"]:
2135
+ tool_text = Text()
2136
+ tool_text.append(" → Installing: ", style="dim")
2137
+ tool_text.append(state["current_tool"], style="bold green")
2138
+ lines.append(tool_text)
2139
+
2140
+ # Recent output (last 5 lines)
2141
+ if state["output"]:
2142
+ lines.append(Text())
2143
+ lines.append(Text(" Recent output:", style="dim"))
2144
+ for line in state["output"][-5:]:
2145
+ output_text = Text()
2146
+ output_text.append(" │ ", style="dim cyan")
2147
+ # Truncate long lines
2148
+ display_line = line[:70] + "..." if len(line) > 70 else line
2149
+ output_text.append(display_line, style="dim")
2150
+ lines.append(output_text)
2151
+
2152
+ return Group(*lines)
2153
+
2154
+ async def setup_vps_live(live):
2155
+ runtime = VPSRuntime()
2156
+
2157
+ # Connect
2158
+ state["status"] = "Connecting to VPS..."
2159
+ live.update(make_display())
2160
+ await runtime.connect()
2161
+ state["status"] = f"Connected to {runtime.host}"
2162
+ state["output"].append(f"✓ Connected to {runtime.host}")
2163
+ live.update(make_display())
2164
+
2165
+ # Setup base dependencies first
2166
+ state["status"] = "Installing base dependencies..."
2167
+ state["current_tool"] = "apt packages, Go, Python, Ruby"
2168
+ live.update(make_display())
2169
+
2170
+ setup_script = """
2171
+ export DEBIAN_FRONTEND=noninteractive
2172
+ apt-get update -qq
2173
+ apt-get install -y -qq git curl wget python3-pip golang-go ruby-full build-essential libssl-dev libffi-dev 2>/dev/null
2174
+ export GOPATH=$HOME/go
2175
+ export PATH=$PATH:$GOPATH/bin:/usr/local/go/bin
2176
+ mkdir -p $GOPATH/bin
2177
+ echo 'Base dependencies installed'
2178
+ """
2179
+ stdout, stderr, code = await runtime._run_command(f"sudo bash -c '{setup_script}'", timeout=300)
2180
+ state["output"].append("✓ Base dependencies installed")
2181
+ live.update(make_display())
2182
+
2183
+ # Install each tool
2184
+ state["status"] = "Installing security tools..."
2185
+
2186
+ for tool_name in tools_to_install:
2187
+ state["current_tool"] = tool_name
2188
+ state["output"].append(f"Installing {tool_name}...")
2189
+ live.update(make_display())
2190
+
2191
+ success = await runtime.install_tool(tool_name)
2192
+ state["results"][tool_name] = success
2193
+ state["completed"] += 1
2194
+
2195
+ if success:
2196
+ state["output"].append(f"✓ {tool_name} installed")
2197
+ else:
2198
+ state["output"].append(f"✗ {tool_name} failed")
2199
+
2200
+ live.update(make_display())
2201
+
2202
+ state["status"] = "Installation complete!"
2203
+ state["current_tool"] = ""
2204
+ live.update(make_display())
2205
+
2206
+ await runtime.disconnect()
2207
+ return state["results"]
2208
+
2209
+ # Run with live display
2210
+ try:
2211
+ with Live(make_display(), console=console, refresh_per_second=4) as live:
2212
+ results = asyncio.run(setup_vps_live(live))
2213
+ except KeyboardInterrupt:
2214
+ console.print("\n[yellow]Installation interrupted by user[/yellow]")
2215
+ return 130
2216
+
2217
+ # Show final results
2218
+ console.print()
2219
+ table = Table(title="Installation Results", box=box.ROUNDED)
2220
+ table.add_column("Tool", style="cyan")
2221
+ table.add_column("Status", style="green")
2222
+
2223
+ installed = 0
2224
+ failed = 0
2225
+ for tool, success in sorted(results.items()):
2226
+ if success:
2227
+ table.add_row(tool, "[green]✓ Installed[/green]")
2228
+ installed += 1
2229
+ else:
2230
+ table.add_row(tool, "[red]✗ Failed[/red]")
2231
+ failed += 1
2232
+
2233
+ console.print(table)
2234
+ console.print()
2235
+
2236
+ if failed == 0:
2237
+ console.print(Panel(
2238
+ f"[bold green]✓ All {installed} tools installed successfully![/bold green]\n\n"
2239
+ "You can now run:\n"
2240
+ " [bold]aiptx vps scan target.com[/bold]",
2241
+ title="🎉 Setup Complete",
2242
+ border_style="green",
2243
+ ))
2244
+ else:
2245
+ console.print(f"[bold]Summary:[/bold] {installed} installed, [red]{failed} failed[/red]")
2246
+ console.print("[dim]Failed tools may require manual installation on VPS[/dim]")
2247
+
2248
+ return 0 if failed == 0 else 1
2249
+
2250
+
2251
+ def run_vps_status(args, console):
2252
+ """Check VPS connection and installed tools."""
2253
+ from rich.table import Table
2254
+ from rich.panel import Panel
2255
+ from rich.live import Live
2256
+ from rich.text import Text
2257
+ from rich.console import Group
2258
+ from rich import box
2259
+
2260
+ # Check for asyncssh FIRST
2261
+ try:
2262
+ import asyncssh
2263
+ except ImportError:
2264
+ console.print()
2265
+ console.print(Panel(
2266
+ "[bold red]Missing Dependency: asyncssh[/bold red]\n\n"
2267
+ "The VPS module requires asyncssh for SSH connectivity.\n\n"
2268
+ "[bold]Install with:[/bold]\n"
2269
+ " [green]pip install asyncssh[/green]\n"
2270
+ " [dim]or[/dim]\n"
2271
+ " [green]pip install aiptx[vps][/green]",
2272
+ title="⚠️ Dependency Required",
2273
+ border_style="yellow",
2274
+ ))
2275
+ console.print()
2276
+ return 1
2277
+
2278
+ from aipt_v2.runtime.vps import VPSRuntime, VPS_TOOLS
2279
+
2280
+ config = get_config()
2281
+
2282
+ # State for live display
2283
+ state = {"status": "Connecting...", "tool": "", "checked": 0, "total": 0}
2284
+
2285
+ def make_status():
2286
+ text = Text()
2287
+ text.append("⚡ ", style="yellow")
2288
+ text.append(state["status"], style="bold cyan")
2289
+ if state["tool"]:
2290
+ text.append(f" - checking {state['tool']}", style="dim")
2291
+ if state["total"] > 0:
2292
+ text.append(f" ({state['checked']}/{state['total']})", style="dim")
2293
+ return text
2294
+
2295
+ async def check_status_live(live):
2296
+ runtime = VPSRuntime()
2297
+
2298
+ # Try to connect
2299
+ state["status"] = "Connecting to VPS..."
2300
+ live.update(make_status())
2301
+
2302
+ try:
2303
+ await runtime.connect()
2304
+ except Exception as e:
2305
+ return False, str(e), {}
2306
+
2307
+ state["status"] = f"Connected to {runtime.host}"
2308
+ live.update(make_status())
2309
+
2310
+ # Count total tools
2311
+ total_tools = sum(len(tools) for tools in VPS_TOOLS.values())
2312
+ state["total"] = total_tools
2313
+ state["status"] = "Checking installed tools..."
2314
+ live.update(make_status())
2315
+
2316
+ # Check each tool
2317
+ tools_status = {}
2318
+ for category, tools in VPS_TOOLS.items():
2319
+ for tool_name, tool_info in tools.items():
2320
+ state["tool"] = tool_name
2321
+ state["checked"] += 1
2322
+ live.update(make_status())
2323
+
2324
+ check_cmd = tool_info.get("check", f"which {tool_name}")
2325
+ stdout, stderr, code = await runtime._run_command(check_cmd, timeout=10)
2326
+ tools_status[tool_name] = code == 0
2327
+
2328
+ state["status"] = "Done!"
2329
+ state["tool"] = ""
2330
+ live.update(make_status())
2331
+
2332
+ await runtime.disconnect()
2333
+ return True, "Connected", tools_status
2334
+
2335
+ console.print()
2336
+
2337
+ try:
2338
+ with Live(make_status(), console=console, refresh_per_second=4) as live:
2339
+ connected, message, tools_status = asyncio.run(check_status_live(live))
2340
+ except KeyboardInterrupt:
2341
+ console.print("\n[yellow]Interrupted[/yellow]")
2342
+ return 130
2343
+
2344
+ # Connection status
2345
+ console.print()
2346
+ if connected:
2347
+ console.print(f"[green]✓[/green] Connected to [bold]{config.vps.host}[/bold]")
2348
+ else:
2349
+ console.print(f"[red]✗[/red] Failed to connect: {message}")
2350
+ return 1
2351
+
2352
+ # Tool status table
2353
+ console.print()
2354
+ table = Table(title="Security Tools Status", box=box.ROUNDED)
2355
+ table.add_column("Category", style="cyan")
2356
+ table.add_column("Tool", style="white")
2357
+ table.add_column("Status", style="green")
2358
+
2359
+ for category, tools in VPS_TOOLS.items():
2360
+ for tool_name in tools:
2361
+ status = tools_status.get(tool_name, False)
2362
+ status_str = "[green]✓ Installed[/green]" if status else "[dim]○ Not installed[/dim]"
2363
+ table.add_row(category, tool_name, status_str)
2364
+
2365
+ console.print(table)
2366
+
2367
+ # Summary
2368
+ installed = sum(1 for v in tools_status.values() if v)
2369
+ total = len(tools_status)
2370
+ console.print()
2371
+
2372
+ if installed == total:
2373
+ console.print(Panel(
2374
+ f"[bold green]✓ All {total} tools installed![/bold green]\n\n"
2375
+ "Your VPS is ready for scanning.\n"
2376
+ "Run: [bold]aiptx vps scan target.com[/bold]",
2377
+ title="🎉 VPS Ready",
2378
+ border_style="green",
2379
+ ))
2380
+ else:
2381
+ console.print(f"[bold]Tools:[/bold] {installed}/{total} installed")
2382
+ console.print()
2383
+ console.print("[dim]Run 'aiptx vps setup' to install missing tools[/dim]")
2384
+
2385
+ return 0
2386
+
2387
+
2388
+ def run_vps_scan(args, console):
2389
+ """Run security scan from VPS."""
2390
+ from rich.progress import Progress, SpinnerColumn, TextColumn
2391
+ from rich.panel import Panel
2392
+
2393
+ # Check for asyncssh FIRST
2394
+ try:
2395
+ import asyncssh
2396
+ except ImportError:
2397
+ console.print()
2398
+ console.print(Panel(
2399
+ "[bold red]Missing Dependency: asyncssh[/bold red]\n\n"
2400
+ "The VPS module requires asyncssh for SSH connectivity.\n\n"
2401
+ "[bold]Install with:[/bold]\n"
2402
+ " [green]pip install asyncssh[/green]\n"
2403
+ " [dim]or[/dim]\n"
2404
+ " [green]pip install aiptx[vps][/green]",
2405
+ title="⚠️ Dependency Required",
2406
+ border_style="yellow",
2407
+ ))
2408
+ console.print()
2409
+ return 1
2410
+
2411
+ target = args.target
2412
+ mode = getattr(args, 'mode', 'standard')
2413
+ tools = getattr(args, 'tools', None)
2414
+
2415
+ console.print()
2416
+ console.print(Panel(
2417
+ f"[bold]Target:[/bold] {target}\n"
2418
+ f"[bold]Mode:[/bold] {mode}\n"
2419
+ f"[bold]Tools:[/bold] {', '.join(tools) if tools else 'Auto-selected'}",
2420
+ title="🎯 VPS Scan Configuration",
2421
+ border_style="cyan",
2422
+ ))
2423
+ console.print()
2424
+
2425
+ from aipt_v2.runtime.vps import VPSRuntime
2426
+
2427
+ async def run_scan():
2428
+ runtime = VPSRuntime()
2429
+ await runtime.connect()
2430
+
2431
+ results = await runtime.run_scan(
2432
+ target=target,
2433
+ scan_type=mode,
2434
+ tools=tools,
2435
+ )
2436
+
2437
+ await runtime.disconnect()
2438
+ return results
2439
+
2440
+ with Progress(
2441
+ SpinnerColumn(),
2442
+ TextColumn("[progress.description]{task.description}"),
2443
+ console=console,
2444
+ ) as progress:
2445
+ task = progress.add_task(f"[cyan]Scanning {target} from VPS...", total=None)
2446
+ results = asyncio.run(run_scan())
2447
+
2448
+ # Display results
2449
+ console.print()
2450
+ console.print("[bold green]✓ Scan complete![/bold green]")
2451
+ console.print()
2452
+ console.print(f"[bold]Results saved to:[/bold] {results.get('local_results_path', 'N/A')}")
2453
+ console.print()
2454
+
2455
+ # Show tool outputs summary
2456
+ tool_outputs = results.get('tool_outputs', {})
2457
+ if tool_outputs:
2458
+ from rich.table import Table
2459
+ table = Table(title="Tool Execution Summary")
2460
+ table.add_column("Tool", style="cyan")
2461
+ table.add_column("Exit Code", style="green")
2462
+ table.add_column("Output Size", style="yellow")
2463
+
2464
+ for tool, output in tool_outputs.items():
2465
+ exit_code = output.get('exit_code', -1)
2466
+ status = "[green]✓[/green]" if exit_code == 0 else f"[red]{exit_code}[/red]"
2467
+ stdout_len = len(output.get('stdout', ''))
2468
+ table.add_row(tool, status, f"{stdout_len} bytes")
2469
+
2470
+ console.print(table)
2471
+
2472
+ return 0
2473
+
2474
+
2475
+ def run_vps_script(args, console):
2476
+ """Generate VPS setup script."""
2477
+ from aipt_v2.runtime.vps import generate_vps_setup_script
2478
+
2479
+ categories = getattr(args, 'categories', None)
2480
+ output_file = getattr(args, 'output', None)
2481
+
2482
+ script = generate_vps_setup_script(categories=categories)
2483
+
2484
+ if output_file:
2485
+ with open(output_file, 'w') as f:
2486
+ f.write(script)
2487
+ console.print(f"[green]✓[/green] Script saved to: {output_file}")
2488
+ console.print(f"[dim]Run on VPS: curl -sL <url> | sudo bash[/dim]")
2489
+ else:
2490
+ console.print(script)
2491
+
2492
+ return 0
2493
+
2494
+
2495
+ def run_ai_command(args):
2496
+ """Handle AI security testing commands."""
2497
+ from rich.console import Console
2498
+ from rich.panel import Panel
2499
+ from rich.table import Table
2500
+ from rich.progress import Progress, SpinnerColumn, TextColumn
2501
+ from rich import box
2502
+ import json
2503
+
2504
+ console = Console()
2505
+
2506
+ ai_cmd = getattr(args, 'ai_command', None)
2507
+
2508
+ if ai_cmd == "code-review":
2509
+ return run_ai_code_review(args, console)
2510
+ elif ai_cmd == "api-test":
2511
+ return run_ai_api_test(args, console)
2512
+ elif ai_cmd == "web-pentest":
2513
+ return run_ai_web_pentest(args, console)
2514
+ elif ai_cmd == "full":
2515
+ return run_ai_full_assessment(args, console)
2516
+ else:
2517
+ console.print()
2518
+ console.print(Panel(
2519
+ "[bold cyan]AIPTX AI Security Testing[/bold cyan]\n\n"
2520
+ "AI-powered security testing using LLMs (Claude, GPT, etc.)\n\n"
2521
+ "[bold]Commands:[/bold]\n"
2522
+ " [bold green]aiptx ai code-review[/bold green] <path> - AI source code security review\n"
2523
+ " [bold green]aiptx ai api-test[/bold green] <url> - AI REST API security testing\n"
2524
+ " [bold green]aiptx ai web-pentest[/bold green] <url> - AI web penetration testing\n"
2525
+ " [bold green]aiptx ai full[/bold green] <target> - Full AI-driven assessment\n\n"
2526
+ "[bold]Examples:[/bold]\n"
2527
+ " aiptx ai code-review ./src --focus sqli xss\n"
2528
+ " aiptx ai api-test https://api.example.com --openapi swagger.json\n"
2529
+ " aiptx ai web-pentest https://example.com --quick",
2530
+ title="🤖 AI Security Testing",
2531
+ border_style="cyan",
2532
+ ))
2533
+ console.print()
2534
+ return 0
2535
+
2536
+
2537
+ def run_ai_code_review(args, console):
2538
+ """Run AI-powered source code security review."""
2539
+ from rich.panel import Panel
2540
+ from rich.table import Table
2541
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
2542
+ from rich.live import Live
2543
+ from rich.text import Text
2544
+ from rich import box
2545
+ import json
2546
+
2547
+ target = args.target
2548
+ focus = getattr(args, 'focus', None)
2549
+ model = getattr(args, 'model', 'claude-sonnet-4-20250514')
2550
+ max_steps = getattr(args, 'max_steps', 100)
2551
+ quick = getattr(args, 'quick', False)
2552
+ output_file = getattr(args, 'output', None)
2553
+
2554
+ # Verify target exists
2555
+ if not Path(target).exists():
2556
+ console.print(f"[red]Error:[/red] Target path does not exist: {target}")
2557
+ return 1
2558
+
2559
+ console.print()
2560
+ console.print(Panel(
2561
+ f"[bold]Target:[/bold] {target}\n"
2562
+ f"[bold]Model:[/bold] {model}\n"
2563
+ f"[bold]Mode:[/bold] {'Quick scan' if quick else 'Full review'}\n"
2564
+ f"[bold]Focus:[/bold] {', '.join(focus) if focus else 'All vulnerabilities'}",
2565
+ title="🔍 AI Code Review",
2566
+ border_style="cyan",
2567
+ ))
2568
+ console.print()
2569
+
2570
+ # Import agent
2571
+ from aipt_v2.skills.agents.code_review import CodeReviewAgent
2572
+ from aipt_v2.skills.agents.base import AgentConfig
2573
+
2574
+ config = AgentConfig(
2575
+ model=model,
2576
+ max_steps=max_steps,
2577
+ verbose=True,
2578
+ )
2579
+
2580
+ agent = CodeReviewAgent(
2581
+ target_path=target,
2582
+ config=config,
2583
+ focus_areas=focus,
2584
+ )
2585
+
2586
+ # Run the review
2587
+ with Progress(
2588
+ SpinnerColumn(),
2589
+ TextColumn("[progress.description]{task.description}"),
2590
+ console=console,
2591
+ ) as progress:
2592
+ task = progress.add_task("[cyan]AI reviewing code...", total=None)
2593
+
2594
+ try:
2595
+ if quick:
2596
+ result = asyncio.run(agent.quick_scan())
2597
+ else:
2598
+ result = asyncio.run(agent.run())
2599
+ except KeyboardInterrupt:
2600
+ console.print("\n[yellow]Review interrupted by user[/yellow]")
2601
+ return 130
2602
+ except Exception as e:
2603
+ console.print(f"\n[red]Error:[/red] {e}")
2604
+ return 1
2605
+
2606
+ # Display results
2607
+ console.print()
2608
+ display_ai_results(console, result, "Code Review")
2609
+
2610
+ # Save to file if requested
2611
+ if output_file:
2612
+ with open(output_file, 'w') as f:
2613
+ json.dump(result.to_dict(), f, indent=2)
2614
+ console.print(f"\n[green]✓[/green] Results saved to: {output_file}")
2615
+
2616
+ return 0 if result.success else 1
2617
+
2618
+
2619
+ def run_ai_api_test(args, console):
2620
+ """Run AI-powered API security testing."""
2621
+ from rich.panel import Panel
2622
+ from rich.progress import Progress, SpinnerColumn, TextColumn
2623
+ import json
2624
+
2625
+ target = args.target
2626
+ openapi_spec = getattr(args, 'openapi', None)
2627
+ auth_token = getattr(args, 'auth_token', None)
2628
+ model = getattr(args, 'model', 'claude-sonnet-4-20250514')
2629
+ max_steps = getattr(args, 'max_steps', 100)
2630
+ output_file = getattr(args, 'output', None)
2631
+
2632
+ console.print()
2633
+ console.print(Panel(
2634
+ f"[bold]Target:[/bold] {target}\n"
2635
+ f"[bold]Model:[/bold] {model}\n"
2636
+ f"[bold]OpenAPI Spec:[/bold] {openapi_spec or 'Not provided'}\n"
2637
+ f"[bold]Authentication:[/bold] {'Bearer token' if auth_token else 'None'}",
2638
+ title="🔌 AI API Security Test",
2639
+ border_style="cyan",
2640
+ ))
2641
+ console.print()
2642
+
2643
+ # Import agent
2644
+ from aipt_v2.skills.agents.api_tester import APITestAgent
2645
+ from aipt_v2.skills.agents.base import AgentConfig
2646
+
2647
+ config = AgentConfig(
2648
+ model=model,
2649
+ max_steps=max_steps,
2650
+ verbose=True,
2651
+ )
2652
+
2653
+ agent = APITestAgent(
2654
+ base_url=target,
2655
+ config=config,
2656
+ openapi_spec=openapi_spec,
2657
+ auth_token=auth_token,
2658
+ )
2659
+
2660
+ # Run the test
2661
+ with Progress(
2662
+ SpinnerColumn(),
2663
+ TextColumn("[progress.description]{task.description}"),
2664
+ console=console,
2665
+ ) as progress:
2666
+ task = progress.add_task("[cyan]AI testing API...", total=None)
2667
+
2668
+ try:
2669
+ result = asyncio.run(agent.run())
2670
+ except KeyboardInterrupt:
2671
+ console.print("\n[yellow]Test interrupted by user[/yellow]")
2672
+ return 130
2673
+ except Exception as e:
2674
+ console.print(f"\n[red]Error:[/red] {e}")
2675
+ return 1
2676
+
2677
+ # Display results
2678
+ console.print()
2679
+ display_ai_results(console, result, "API Test")
2680
+
2681
+ # Save to file if requested
2682
+ if output_file:
2683
+ with open(output_file, 'w') as f:
2684
+ json.dump(result.to_dict(), f, indent=2)
2685
+ console.print(f"\n[green]✓[/green] Results saved to: {output_file}")
2686
+
2687
+ return 0 if result.success else 1
2688
+
2689
+
2690
+ def run_ai_web_pentest(args, console):
2691
+ """Run AI-powered web penetration testing."""
2692
+ from rich.panel import Panel
2693
+ from rich.progress import Progress, SpinnerColumn, TextColumn
2694
+ import json
2695
+
2696
+ target = args.target
2697
+ auth_token = getattr(args, 'auth_token', None)
2698
+ cookies_list = getattr(args, 'cookie', None) or []
2699
+ model = getattr(args, 'model', 'claude-sonnet-4-20250514')
2700
+ max_steps = getattr(args, 'max_steps', 100)
2701
+ quick = getattr(args, 'quick', False)
2702
+ output_file = getattr(args, 'output', None)
2703
+
2704
+ # Parse cookies
2705
+ cookies = {}
2706
+ for cookie in cookies_list:
2707
+ if '=' in cookie:
2708
+ key, value = cookie.split('=', 1)
2709
+ cookies[key] = value
2710
+
2711
+ console.print()
2712
+ console.print(Panel(
2713
+ f"[bold]Target:[/bold] {target}\n"
2714
+ f"[bold]Model:[/bold] {model}\n"
2715
+ f"[bold]Mode:[/bold] {'Quick scan' if quick else 'Full pentest'}\n"
2716
+ f"[bold]Authentication:[/bold] {'Token + Cookies' if auth_token and cookies else 'Token' if auth_token else 'Cookies' if cookies else 'None'}",
2717
+ title="🌐 AI Web Penetration Test",
2718
+ border_style="cyan",
2719
+ ))
2720
+ console.print()
2721
+
2722
+ # Import agent
2723
+ from aipt_v2.skills.agents.web_pentest import WebPentestAgent
2724
+ from aipt_v2.skills.agents.base import AgentConfig
2725
+
2726
+ config = AgentConfig(
2727
+ model=model,
2728
+ max_steps=max_steps,
2729
+ verbose=True,
2730
+ )
2731
+
2732
+ agent = WebPentestAgent(
2733
+ target=target,
2734
+ config=config,
2735
+ cookies=cookies if cookies else None,
2736
+ auth_token=auth_token,
2737
+ )
2738
+
2739
+ # Run the test
2740
+ with Progress(
2741
+ SpinnerColumn(),
2742
+ TextColumn("[progress.description]{task.description}"),
2743
+ console=console,
2744
+ ) as progress:
2745
+ task = progress.add_task("[cyan]AI pentesting web app...", total=None)
2746
+
2747
+ try:
2748
+ if quick:
2749
+ result = asyncio.run(agent.quick_scan())
2750
+ else:
2751
+ result = asyncio.run(agent.run())
2752
+ except KeyboardInterrupt:
2753
+ console.print("\n[yellow]Pentest interrupted by user[/yellow]")
2754
+ return 130
2755
+ except Exception as e:
2756
+ console.print(f"\n[red]Error:[/red] {e}")
2757
+ return 1
2758
+
2759
+ # Display results
2760
+ console.print()
2761
+ display_ai_results(console, result, "Web Pentest")
2762
+
2763
+ # Save to file if requested
2764
+ if output_file:
2765
+ with open(output_file, 'w') as f:
2766
+ json.dump(result.to_dict(), f, indent=2)
2767
+ console.print(f"\n[green]✓[/green] Results saved to: {output_file}")
2768
+
2769
+ return 0 if result.success else 1
2770
+
2771
+
2772
+ def run_ai_full_assessment(args, console):
2773
+ """Run full AI-driven security assessment."""
2774
+ from rich.panel import Panel
2775
+ from rich.progress import Progress, SpinnerColumn, TextColumn
2776
+ import json
2777
+
2778
+ target = args.target
2779
+ test_types = getattr(args, 'types', ['web'])
2780
+ model = getattr(args, 'model', 'claude-sonnet-4-20250514')
2781
+ output_file = getattr(args, 'output', None)
2782
+
2783
+ console.print()
2784
+ console.print(Panel(
2785
+ f"[bold]Target:[/bold] {target}\n"
2786
+ f"[bold]Model:[/bold] {model}\n"
2787
+ f"[bold]Test Types:[/bold] {', '.join(test_types)}",
2788
+ title="🎯 Full AI Security Assessment",
2789
+ border_style="cyan",
2790
+ ))
2791
+ console.print()
2792
+
2793
+ # Import agent
2794
+ from aipt_v2.skills.agents.security_agent import SecurityAgent
2795
+ from aipt_v2.skills.agents.base import AgentConfig
2796
+
2797
+ config = AgentConfig(
2798
+ model=model,
2799
+ max_steps=150, # More steps for full assessment
2800
+ verbose=True,
2801
+ )
2802
+
2803
+ agent = SecurityAgent(
2804
+ target=target,
2805
+ config=config,
2806
+ test_types=test_types,
2807
+ )
2808
+
2809
+ # Run the assessment
2810
+ with Progress(
2811
+ SpinnerColumn(),
2812
+ TextColumn("[progress.description]{task.description}"),
2813
+ console=console,
2814
+ ) as progress:
2815
+ task = progress.add_task("[cyan]Running full AI assessment...", total=None)
2816
+
2817
+ try:
2818
+ results = asyncio.run(agent.run_full_assessment())
2819
+ combined = agent.combine_results(results)
2820
+ except KeyboardInterrupt:
2821
+ console.print("\n[yellow]Assessment interrupted by user[/yellow]")
2822
+ return 130
2823
+ except Exception as e:
2824
+ console.print(f"\n[red]Error:[/red] {e}")
2825
+ return 1
2826
+
2827
+ # Display results
2828
+ console.print()
2829
+ display_ai_results(console, combined, "Full Assessment")
2830
+
2831
+ # Show per-type results
2832
+ for test_type, result in results.items():
2833
+ if result.findings:
2834
+ console.print(f"\n[bold]{test_type.upper()} Findings:[/bold] {len(result.findings)}")
2835
+
2836
+ # Save to file if requested
2837
+ if output_file:
2838
+ with open(output_file, 'w') as f:
2839
+ json.dump(combined.to_dict(), f, indent=2)
2840
+ console.print(f"\n[green]✓[/green] Results saved to: {output_file}")
2841
+
2842
+ return 0 if combined.success else 1
2843
+
2844
+
2845
+ def display_ai_results(console, result, test_name):
2846
+ """Display AI testing results in a formatted way."""
2847
+ from rich.table import Table
2848
+ from rich.panel import Panel
2849
+ from rich import box
2850
+
2851
+ # Summary panel
2852
+ severity_colors = {
2853
+ "critical": "red",
2854
+ "high": "orange1",
2855
+ "medium": "yellow",
2856
+ "low": "blue",
2857
+ "info": "dim",
2858
+ }
2859
+
2860
+ # Count by severity
2861
+ severity_counts = {}
2862
+ for finding in result.findings:
2863
+ sev = finding.severity.value
2864
+ severity_counts[sev] = severity_counts.get(sev, 0) + 1
2865
+
2866
+ summary_parts = []
2867
+ for sev in ["critical", "high", "medium", "low", "info"]:
2868
+ count = severity_counts.get(sev, 0)
2869
+ if count > 0:
2870
+ color = severity_colors.get(sev, "white")
2871
+ summary_parts.append(f"[{color}]{sev.upper()}: {count}[/{color}]")
2872
+
2873
+ summary = " | ".join(summary_parts) if summary_parts else "[green]No vulnerabilities found[/green]"
2874
+
2875
+ console.print(Panel(
2876
+ f"[bold]Findings:[/bold] {len(result.findings)}\n"
2877
+ f"[bold]Severity:[/bold] {summary}\n"
2878
+ f"[bold]Steps:[/bold] {result.total_steps}\n"
2879
+ f"[bold]Time:[/bold] {result.execution_time:.1f}s\n"
2880
+ f"[bold]Model:[/bold] {result.model_used}",
2881
+ title=f"📊 {test_name} Results",
2882
+ border_style="green" if result.success else "red",
2883
+ ))
2884
+
2885
+ # Findings table
2886
+ if result.findings:
2887
+ console.print()
2888
+ table = Table(title="Security Findings", box=box.ROUNDED)
2889
+ table.add_column("#", style="dim", width=3)
2890
+ table.add_column("Severity", width=10)
2891
+ table.add_column("Title", style="white")
2892
+ table.add_column("Location", style="dim")
2893
+
2894
+ for i, finding in enumerate(result.findings, 1):
2895
+ sev_color = severity_colors.get(finding.severity.value, "white")
2896
+ severity_text = f"[{sev_color}]{finding.severity.value.upper()}[/{sev_color}]"
2897
+ table.add_row(
2898
+ str(i),
2899
+ severity_text,
2900
+ finding.title[:50] + "..." if len(finding.title) > 50 else finding.title,
2901
+ finding.location[:40] + "..." if len(finding.location) > 40 else finding.location,
2902
+ )
2903
+
2904
+ console.print(table)
2905
+
2906
+ # Show details for critical/high findings
2907
+ critical_high = [f for f in result.findings if f.severity.value in ["critical", "high"]]
2908
+ if critical_high:
2909
+ console.print()
2910
+ console.print("[bold red]Critical/High Severity Details:[/bold red]")
2911
+ for finding in critical_high[:5]: # Limit to 5 detailed findings
2912
+ console.print(f"\n[bold]{finding.title}[/bold]")
2913
+ console.print(f" [dim]Location:[/dim] {finding.location}")
2914
+ console.print(f" [dim]Description:[/dim] {finding.description[:200]}...")
2915
+ if finding.remediation:
2916
+ console.print(f" [dim]Fix:[/dim] {finding.remediation[:150]}...")
2917
+
2918
+ # Errors
2919
+ if result.errors:
2920
+ console.print()
2921
+ console.print("[bold red]Errors:[/bold red]")
2922
+ for error in result.errors:
2923
+ console.print(f" [red]•[/red] {error}")
2924
+
2925
+
2926
+ if __name__ == "__main__":
2927
+ try:
2928
+ sys.exit(main())
2929
+ except KeyboardInterrupt:
2930
+ # Handle Ctrl+C gracefully without traceback
2931
+ from rich.console import Console
2932
+ Console().print("\n[yellow]Operation cancelled.[/yellow]")
2933
+ sys.exit(130)