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.
- aipt_v2/__init__.py +110 -0
- aipt_v2/__main__.py +24 -0
- aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
- aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
- aipt_v2/agents/__init__.py +46 -0
- aipt_v2/agents/base.py +520 -0
- aipt_v2/agents/exploit_agent.py +688 -0
- aipt_v2/agents/ptt.py +406 -0
- aipt_v2/agents/state.py +168 -0
- aipt_v2/app.py +957 -0
- aipt_v2/browser/__init__.py +31 -0
- aipt_v2/browser/automation.py +458 -0
- aipt_v2/browser/crawler.py +453 -0
- aipt_v2/cli.py +2933 -0
- aipt_v2/compliance/__init__.py +71 -0
- aipt_v2/compliance/compliance_report.py +449 -0
- aipt_v2/compliance/framework_mapper.py +424 -0
- aipt_v2/compliance/nist_mapping.py +345 -0
- aipt_v2/compliance/owasp_mapping.py +330 -0
- aipt_v2/compliance/pci_mapping.py +297 -0
- aipt_v2/config.py +341 -0
- aipt_v2/core/__init__.py +43 -0
- aipt_v2/core/agent.py +630 -0
- aipt_v2/core/llm.py +395 -0
- aipt_v2/core/memory.py +305 -0
- aipt_v2/core/ptt.py +329 -0
- aipt_v2/database/__init__.py +14 -0
- aipt_v2/database/models.py +232 -0
- aipt_v2/database/repository.py +384 -0
- aipt_v2/docker/__init__.py +23 -0
- aipt_v2/docker/builder.py +260 -0
- aipt_v2/docker/manager.py +222 -0
- aipt_v2/docker/sandbox.py +371 -0
- aipt_v2/evasion/__init__.py +58 -0
- aipt_v2/evasion/request_obfuscator.py +272 -0
- aipt_v2/evasion/tls_fingerprint.py +285 -0
- aipt_v2/evasion/ua_rotator.py +301 -0
- aipt_v2/evasion/waf_bypass.py +439 -0
- aipt_v2/execution/__init__.py +23 -0
- aipt_v2/execution/executor.py +302 -0
- aipt_v2/execution/parser.py +544 -0
- aipt_v2/execution/terminal.py +337 -0
- aipt_v2/health.py +437 -0
- aipt_v2/intelligence/__init__.py +194 -0
- aipt_v2/intelligence/adaptation.py +474 -0
- aipt_v2/intelligence/auth.py +520 -0
- aipt_v2/intelligence/chaining.py +775 -0
- aipt_v2/intelligence/correlation.py +536 -0
- aipt_v2/intelligence/cve_aipt.py +334 -0
- aipt_v2/intelligence/cve_info.py +1111 -0
- aipt_v2/intelligence/knowledge_graph.py +590 -0
- aipt_v2/intelligence/learning.py +626 -0
- aipt_v2/intelligence/llm_analyzer.py +502 -0
- aipt_v2/intelligence/llm_tool_selector.py +518 -0
- aipt_v2/intelligence/payload_generator.py +562 -0
- aipt_v2/intelligence/rag.py +239 -0
- aipt_v2/intelligence/scope.py +442 -0
- aipt_v2/intelligence/searchers/__init__.py +5 -0
- aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
- aipt_v2/intelligence/searchers/github_searcher.py +467 -0
- aipt_v2/intelligence/searchers/google_searcher.py +281 -0
- aipt_v2/intelligence/tools.json +443 -0
- aipt_v2/intelligence/triage.py +670 -0
- aipt_v2/interactive_shell.py +559 -0
- aipt_v2/interface/__init__.py +5 -0
- aipt_v2/interface/cli.py +230 -0
- aipt_v2/interface/main.py +501 -0
- aipt_v2/interface/tui.py +1276 -0
- aipt_v2/interface/utils.py +583 -0
- aipt_v2/llm/__init__.py +39 -0
- aipt_v2/llm/config.py +26 -0
- aipt_v2/llm/llm.py +514 -0
- aipt_v2/llm/memory.py +214 -0
- aipt_v2/llm/request_queue.py +89 -0
- aipt_v2/llm/utils.py +89 -0
- aipt_v2/local_tool_installer.py +1467 -0
- aipt_v2/models/__init__.py +15 -0
- aipt_v2/models/findings.py +295 -0
- aipt_v2/models/phase_result.py +224 -0
- aipt_v2/models/scan_config.py +207 -0
- aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
- aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
- aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
- aipt_v2/monitoring/prometheus.yml +60 -0
- aipt_v2/orchestration/__init__.py +52 -0
- aipt_v2/orchestration/pipeline.py +398 -0
- aipt_v2/orchestration/progress.py +300 -0
- aipt_v2/orchestration/scheduler.py +296 -0
- aipt_v2/orchestrator.py +2427 -0
- aipt_v2/payloads/__init__.py +27 -0
- aipt_v2/payloads/cmdi.py +150 -0
- aipt_v2/payloads/sqli.py +263 -0
- aipt_v2/payloads/ssrf.py +204 -0
- aipt_v2/payloads/templates.py +222 -0
- aipt_v2/payloads/traversal.py +166 -0
- aipt_v2/payloads/xss.py +204 -0
- aipt_v2/prompts/__init__.py +60 -0
- aipt_v2/proxy/__init__.py +29 -0
- aipt_v2/proxy/history.py +352 -0
- aipt_v2/proxy/interceptor.py +452 -0
- aipt_v2/recon/__init__.py +44 -0
- aipt_v2/recon/dns.py +241 -0
- aipt_v2/recon/osint.py +367 -0
- aipt_v2/recon/subdomain.py +372 -0
- aipt_v2/recon/tech_detect.py +311 -0
- aipt_v2/reports/__init__.py +17 -0
- aipt_v2/reports/generator.py +313 -0
- aipt_v2/reports/html_report.py +378 -0
- aipt_v2/runtime/__init__.py +53 -0
- aipt_v2/runtime/base.py +30 -0
- aipt_v2/runtime/docker.py +401 -0
- aipt_v2/runtime/local.py +346 -0
- aipt_v2/runtime/tool_server.py +205 -0
- aipt_v2/runtime/vps.py +830 -0
- aipt_v2/scanners/__init__.py +28 -0
- aipt_v2/scanners/base.py +273 -0
- aipt_v2/scanners/nikto.py +244 -0
- aipt_v2/scanners/nmap.py +402 -0
- aipt_v2/scanners/nuclei.py +273 -0
- aipt_v2/scanners/web.py +454 -0
- aipt_v2/scripts/security_audit.py +366 -0
- aipt_v2/setup_wizard.py +941 -0
- aipt_v2/skills/__init__.py +80 -0
- aipt_v2/skills/agents/__init__.py +14 -0
- aipt_v2/skills/agents/api_tester.py +706 -0
- aipt_v2/skills/agents/base.py +477 -0
- aipt_v2/skills/agents/code_review.py +459 -0
- aipt_v2/skills/agents/security_agent.py +336 -0
- aipt_v2/skills/agents/web_pentest.py +818 -0
- aipt_v2/skills/prompts/__init__.py +647 -0
- aipt_v2/system_detector.py +539 -0
- aipt_v2/telemetry/__init__.py +7 -0
- aipt_v2/telemetry/tracer.py +347 -0
- aipt_v2/terminal/__init__.py +28 -0
- aipt_v2/terminal/executor.py +400 -0
- aipt_v2/terminal/sandbox.py +350 -0
- aipt_v2/tools/__init__.py +44 -0
- aipt_v2/tools/active_directory/__init__.py +78 -0
- aipt_v2/tools/active_directory/ad_config.py +238 -0
- aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
- aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
- aipt_v2/tools/active_directory/ldap_enum.py +533 -0
- aipt_v2/tools/active_directory/smb_attacks.py +505 -0
- aipt_v2/tools/agents_graph/__init__.py +19 -0
- aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
- aipt_v2/tools/api_security/__init__.py +76 -0
- aipt_v2/tools/api_security/api_discovery.py +608 -0
- aipt_v2/tools/api_security/graphql_scanner.py +622 -0
- aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
- aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
- aipt_v2/tools/browser/__init__.py +5 -0
- aipt_v2/tools/browser/browser_actions.py +238 -0
- aipt_v2/tools/browser/browser_instance.py +535 -0
- aipt_v2/tools/browser/tab_manager.py +344 -0
- aipt_v2/tools/cloud/__init__.py +70 -0
- aipt_v2/tools/cloud/cloud_config.py +273 -0
- aipt_v2/tools/cloud/cloud_scanner.py +639 -0
- aipt_v2/tools/cloud/prowler_tool.py +571 -0
- aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
- aipt_v2/tools/executor.py +307 -0
- aipt_v2/tools/parser.py +408 -0
- aipt_v2/tools/proxy/__init__.py +5 -0
- aipt_v2/tools/proxy/proxy_actions.py +103 -0
- aipt_v2/tools/proxy/proxy_manager.py +789 -0
- aipt_v2/tools/registry.py +196 -0
- aipt_v2/tools/scanners/__init__.py +343 -0
- aipt_v2/tools/scanners/acunetix_tool.py +712 -0
- aipt_v2/tools/scanners/burp_tool.py +631 -0
- aipt_v2/tools/scanners/config.py +156 -0
- aipt_v2/tools/scanners/nessus_tool.py +588 -0
- aipt_v2/tools/scanners/zap_tool.py +612 -0
- aipt_v2/tools/terminal/__init__.py +5 -0
- aipt_v2/tools/terminal/terminal_actions.py +37 -0
- aipt_v2/tools/terminal/terminal_manager.py +153 -0
- aipt_v2/tools/terminal/terminal_session.py +449 -0
- aipt_v2/tools/tool_processing.py +108 -0
- aipt_v2/utils/__init__.py +17 -0
- aipt_v2/utils/logging.py +202 -0
- aipt_v2/utils/model_manager.py +187 -0
- aipt_v2/utils/searchers/__init__.py +269 -0
- aipt_v2/verify_install.py +793 -0
- aiptx-2.0.7.dist-info/METADATA +345 -0
- aiptx-2.0.7.dist-info/RECORD +187 -0
- aiptx-2.0.7.dist-info/WHEEL +5 -0
- aiptx-2.0.7.dist-info/entry_points.txt +7 -0
- aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
- aiptx-2.0.7.dist-info/top_level.txt +1 -0
aipt_v2/setup_wizard.py
ADDED
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIPTX Interactive Setup Wizard
|
|
3
|
+
==============================
|
|
4
|
+
|
|
5
|
+
First-run setup wizard that guides users through configuration.
|
|
6
|
+
Collects API keys and settings interactively with a beautiful TUI.
|
|
7
|
+
|
|
8
|
+
NEW in v2.1:
|
|
9
|
+
- Automatic system detection (OS, package manager, architecture)
|
|
10
|
+
- Local security tool installation
|
|
11
|
+
- Ollama/local LLM support for offline operation
|
|
12
|
+
- Prerequisites verification and installation
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
aiptx setup # Run setup wizard
|
|
16
|
+
aiptx scan example.com # Auto-triggers if not configured
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
import os
|
|
21
|
+
import shutil
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Optional, Tuple, Dict, List
|
|
25
|
+
|
|
26
|
+
from rich.console import Console
|
|
27
|
+
from rich.panel import Panel
|
|
28
|
+
from rich.prompt import Prompt, Confirm
|
|
29
|
+
from rich.table import Table
|
|
30
|
+
from rich.text import Text
|
|
31
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
32
|
+
from rich import box
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
console = Console()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Lazy imports to avoid circular dependencies
|
|
39
|
+
def _get_system_detector():
|
|
40
|
+
from aipt_v2.system_detector import SystemDetector, SystemInfo
|
|
41
|
+
return SystemDetector, SystemInfo
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_tool_installer():
|
|
45
|
+
from aipt_v2.local_tool_installer import LocalToolInstaller, TOOLS, ToolCategory
|
|
46
|
+
return LocalToolInstaller, TOOLS, ToolCategory
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ============================================================================
|
|
50
|
+
# Configuration File Management
|
|
51
|
+
# ============================================================================
|
|
52
|
+
|
|
53
|
+
def get_config_path() -> Path:
|
|
54
|
+
"""Get the path to the .env config file."""
|
|
55
|
+
# Check for existing .env in current directory
|
|
56
|
+
local_env = Path(".env")
|
|
57
|
+
if local_env.exists():
|
|
58
|
+
return local_env
|
|
59
|
+
|
|
60
|
+
# Check for global config in home directory
|
|
61
|
+
home_env = Path.home() / ".aiptx" / ".env"
|
|
62
|
+
if home_env.exists():
|
|
63
|
+
return home_env
|
|
64
|
+
|
|
65
|
+
# Default to home directory for new installations
|
|
66
|
+
return home_env
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def load_existing_config() -> dict:
|
|
70
|
+
"""Load existing configuration from .env file."""
|
|
71
|
+
config = {}
|
|
72
|
+
config_path = get_config_path()
|
|
73
|
+
|
|
74
|
+
if config_path.exists():
|
|
75
|
+
with open(config_path, "r") as f:
|
|
76
|
+
for line in f:
|
|
77
|
+
line = line.strip()
|
|
78
|
+
if line and not line.startswith("#") and "=" in line:
|
|
79
|
+
key, value = line.split("=", 1)
|
|
80
|
+
config[key.strip()] = value.strip().strip('"').strip("'")
|
|
81
|
+
|
|
82
|
+
return config
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def save_config(config: dict, path: Optional[Path] = None) -> Path:
|
|
86
|
+
"""Save configuration to .env file."""
|
|
87
|
+
if path is None:
|
|
88
|
+
path = Path.home() / ".aiptx" / ".env"
|
|
89
|
+
|
|
90
|
+
# Create directory if needed
|
|
91
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
|
|
93
|
+
# Build config content
|
|
94
|
+
lines = [
|
|
95
|
+
"# AIPTX Configuration",
|
|
96
|
+
"# Generated by 'aiptx setup'",
|
|
97
|
+
"# Edit this file or run 'aiptx setup' again to reconfigure",
|
|
98
|
+
"",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
# Group settings
|
|
102
|
+
sections = {
|
|
103
|
+
"LLM": [
|
|
104
|
+
"ANTHROPIC_API_KEY", "OPENAI_API_KEY", "DEEPSEEK_API_KEY", "LLM_API_KEY",
|
|
105
|
+
"AIPT_LLM__PROVIDER", "AIPT_LLM__MODEL", "AIPT_LLM__OLLAMA_BASE_URL"
|
|
106
|
+
],
|
|
107
|
+
"Acunetix": ["AIPT_SCANNERS__ACUNETIX_URL", "AIPT_SCANNERS__ACUNETIX_API_KEY"],
|
|
108
|
+
"Burp Suite": ["AIPT_SCANNERS__BURP_URL", "AIPT_SCANNERS__BURP_API_KEY"],
|
|
109
|
+
"Nessus": ["AIPT_SCANNERS__NESSUS_URL", "AIPT_SCANNERS__NESSUS_ACCESS_KEY",
|
|
110
|
+
"AIPT_SCANNERS__NESSUS_SECRET_KEY"],
|
|
111
|
+
"OWASP ZAP": ["AIPT_SCANNERS__ZAP_URL", "AIPT_SCANNERS__ZAP_API_KEY"],
|
|
112
|
+
"VPS": ["AIPT_VPS__HOST", "AIPT_VPS__USER", "AIPT_VPS__KEY_PATH", "AIPT_VPS__PORT"],
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for section, keys in sections.items():
|
|
116
|
+
section_values = [(k, config.get(k)) for k in keys if config.get(k)]
|
|
117
|
+
if section_values:
|
|
118
|
+
lines.append(f"\n# {section} Configuration")
|
|
119
|
+
for key, value in section_values:
|
|
120
|
+
lines.append(f'{key}="{value}"')
|
|
121
|
+
|
|
122
|
+
# Write file
|
|
123
|
+
with open(path, "w") as f:
|
|
124
|
+
f.write("\n".join(lines) + "\n")
|
|
125
|
+
|
|
126
|
+
# Secure the file (readable only by owner)
|
|
127
|
+
os.chmod(path, 0o600)
|
|
128
|
+
|
|
129
|
+
return path
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def is_configured() -> bool:
|
|
133
|
+
"""Check if AIPTX has been configured with at least an LLM API key."""
|
|
134
|
+
# Check environment variables
|
|
135
|
+
for key in ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "DEEPSEEK_API_KEY", "LLM_API_KEY"]:
|
|
136
|
+
if os.getenv(key):
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
# Check .env files
|
|
140
|
+
config = load_existing_config()
|
|
141
|
+
for key in ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "DEEPSEEK_API_KEY", "LLM_API_KEY"]:
|
|
142
|
+
if config.get(key):
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# ============================================================================
|
|
149
|
+
# Interactive Setup Wizard
|
|
150
|
+
# ============================================================================
|
|
151
|
+
|
|
152
|
+
def print_welcome():
|
|
153
|
+
"""Print welcome banner."""
|
|
154
|
+
banner = """
|
|
155
|
+
[bold cyan]╔═══════════════════════════════════════════════════════════════╗
|
|
156
|
+
║ ║
|
|
157
|
+
║ █████╗ ██╗██████╗ ████████╗██╗ ██╗ ║
|
|
158
|
+
║ ██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗██╔╝ ║
|
|
159
|
+
║ ███████║██║██████╔╝ ██║ ╚███╔╝ ║
|
|
160
|
+
║ ██╔══██║██║██╔═══╝ ██║ ██╔██╗ ║
|
|
161
|
+
║ ██║ ██║██║██║ ██║ ██╔╝ ██╗ ║
|
|
162
|
+
║ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ║
|
|
163
|
+
║ ║
|
|
164
|
+
║ AI-Powered Penetration Testing Framework ║
|
|
165
|
+
║ ║
|
|
166
|
+
╚═══════════════════════════════════════════════════════════════╝[/bold cyan]
|
|
167
|
+
"""
|
|
168
|
+
console.print(banner)
|
|
169
|
+
console.print("[bold green]Welcome to AIPTX Setup![/bold green]")
|
|
170
|
+
console.print("This wizard will help you configure AIPTX for first use.\n")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def detect_system() -> Optional[object]:
|
|
174
|
+
"""
|
|
175
|
+
Detect and display system information.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
SystemInfo object or None if detection fails
|
|
179
|
+
"""
|
|
180
|
+
console.print(Panel(
|
|
181
|
+
"[bold]System Detection[/bold]\n\n"
|
|
182
|
+
"Detecting your system configuration to optimize installation...",
|
|
183
|
+
title="🔍 Auto-Detection",
|
|
184
|
+
border_style="cyan"
|
|
185
|
+
))
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
SystemDetector, SystemInfo = _get_system_detector()
|
|
189
|
+
detector = SystemDetector()
|
|
190
|
+
|
|
191
|
+
with console.status("[bold cyan]Detecting system...[/bold cyan]"):
|
|
192
|
+
system_info = await detector.detect()
|
|
193
|
+
|
|
194
|
+
# Display results
|
|
195
|
+
table = Table(box=box.ROUNDED, show_header=False)
|
|
196
|
+
table.add_column("Property", style="cyan")
|
|
197
|
+
table.add_column("Value", style="green")
|
|
198
|
+
|
|
199
|
+
table.add_row("Operating System", system_info.os_name)
|
|
200
|
+
table.add_row("Version", f"{system_info.os_version}" +
|
|
201
|
+
(f" ({system_info.os_codename})" if system_info.os_codename else ""))
|
|
202
|
+
table.add_row("Architecture", system_info.architecture.value)
|
|
203
|
+
table.add_row("Package Manager", system_info.package_manager.value)
|
|
204
|
+
|
|
205
|
+
if system_info.is_wsl:
|
|
206
|
+
table.add_row("Environment", "WSL")
|
|
207
|
+
elif system_info.is_container:
|
|
208
|
+
table.add_row("Environment", "Container")
|
|
209
|
+
|
|
210
|
+
console.print(table)
|
|
211
|
+
|
|
212
|
+
# Show capabilities summary
|
|
213
|
+
caps = system_info.capabilities
|
|
214
|
+
cap_status = []
|
|
215
|
+
if caps.has_python3:
|
|
216
|
+
cap_status.append("[green]Python3[/green]")
|
|
217
|
+
if caps.has_go:
|
|
218
|
+
cap_status.append("[green]Go[/green]")
|
|
219
|
+
else:
|
|
220
|
+
cap_status.append("[yellow]Go (will install)[/yellow]")
|
|
221
|
+
if caps.has_docker:
|
|
222
|
+
cap_status.append("[green]Docker[/green]")
|
|
223
|
+
if caps.has_git:
|
|
224
|
+
cap_status.append("[green]Git[/green]")
|
|
225
|
+
|
|
226
|
+
console.print(f"\n[bold]Available runtimes:[/bold] {', '.join(cap_status)}")
|
|
227
|
+
|
|
228
|
+
return system_info
|
|
229
|
+
|
|
230
|
+
except Exception as e:
|
|
231
|
+
console.print(f"[yellow]Warning: Could not fully detect system: {e}[/yellow]")
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def check_ollama_installed() -> Tuple[bool, str]:
|
|
236
|
+
"""Check if Ollama is installed and get version."""
|
|
237
|
+
ollama_path = shutil.which("ollama")
|
|
238
|
+
if ollama_path:
|
|
239
|
+
try:
|
|
240
|
+
import subprocess
|
|
241
|
+
result = subprocess.run(
|
|
242
|
+
["ollama", "--version"],
|
|
243
|
+
capture_output=True,
|
|
244
|
+
text=True,
|
|
245
|
+
timeout=5
|
|
246
|
+
)
|
|
247
|
+
version = result.stdout.strip() or result.stderr.strip()
|
|
248
|
+
return True, version
|
|
249
|
+
except Exception:
|
|
250
|
+
return True, "unknown version"
|
|
251
|
+
return False, ""
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
async def check_ollama_running() -> bool:
|
|
255
|
+
"""Check if Ollama server is running."""
|
|
256
|
+
try:
|
|
257
|
+
import asyncio
|
|
258
|
+
proc = await asyncio.create_subprocess_shell(
|
|
259
|
+
"curl -s http://localhost:11434/api/version",
|
|
260
|
+
stdout=asyncio.subprocess.PIPE,
|
|
261
|
+
stderr=asyncio.subprocess.PIPE,
|
|
262
|
+
)
|
|
263
|
+
stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=5)
|
|
264
|
+
return proc.returncode == 0 and b"version" in stdout
|
|
265
|
+
except Exception:
|
|
266
|
+
return False
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
async def get_ollama_models() -> List[str]:
|
|
270
|
+
"""Get list of available Ollama models."""
|
|
271
|
+
try:
|
|
272
|
+
proc = await asyncio.create_subprocess_shell(
|
|
273
|
+
"ollama list",
|
|
274
|
+
stdout=asyncio.subprocess.PIPE,
|
|
275
|
+
stderr=asyncio.subprocess.PIPE,
|
|
276
|
+
)
|
|
277
|
+
stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=10)
|
|
278
|
+
if proc.returncode == 0:
|
|
279
|
+
lines = stdout.decode().strip().split("\n")[1:] # Skip header
|
|
280
|
+
models = []
|
|
281
|
+
for line in lines:
|
|
282
|
+
if line.strip():
|
|
283
|
+
model_name = line.split()[0]
|
|
284
|
+
models.append(model_name)
|
|
285
|
+
return models
|
|
286
|
+
except Exception:
|
|
287
|
+
pass
|
|
288
|
+
return []
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def setup_llm() -> dict:
|
|
292
|
+
"""Configure LLM provider and API key."""
|
|
293
|
+
config = {}
|
|
294
|
+
|
|
295
|
+
console.print(Panel(
|
|
296
|
+
"[bold]Step 2: LLM Configuration[/bold]\n\n"
|
|
297
|
+
"AIPTX uses AI to guide penetration testing.\n"
|
|
298
|
+
"Choose a cloud provider or run locally with Ollama.",
|
|
299
|
+
title="🤖 AI Provider",
|
|
300
|
+
border_style="cyan"
|
|
301
|
+
))
|
|
302
|
+
|
|
303
|
+
# Check Ollama status
|
|
304
|
+
ollama_installed, ollama_version = check_ollama_installed()
|
|
305
|
+
|
|
306
|
+
# Choose provider
|
|
307
|
+
console.print("\n[bold]Select your LLM provider:[/bold]")
|
|
308
|
+
console.print(" [1] Anthropic (Claude) - [green]Recommended for best results[/green]")
|
|
309
|
+
console.print(" [2] OpenAI (GPT-4)")
|
|
310
|
+
console.print(" [3] DeepSeek - [dim]Cost-effective option[/dim]")
|
|
311
|
+
|
|
312
|
+
if ollama_installed:
|
|
313
|
+
console.print(f" [4] Ollama (Local) - [green]✓ Installed ({ollama_version})[/green] - [bold]FREE, runs offline[/bold]")
|
|
314
|
+
else:
|
|
315
|
+
console.print(" [4] Ollama (Local) - [yellow]○ Not installed[/yellow] - [dim]Will install[/dim]")
|
|
316
|
+
|
|
317
|
+
console.print(" [5] Other (custom)")
|
|
318
|
+
|
|
319
|
+
choice = Prompt.ask(
|
|
320
|
+
"\nEnter choice",
|
|
321
|
+
choices=["1", "2", "3", "4", "5"],
|
|
322
|
+
default="1"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
providers = {
|
|
326
|
+
"1": ("anthropic", "claude-sonnet-4-20250514", "ANTHROPIC_API_KEY"),
|
|
327
|
+
"2": ("openai", "gpt-4o", "OPENAI_API_KEY"),
|
|
328
|
+
"3": ("deepseek", "deepseek-chat", "DEEPSEEK_API_KEY"),
|
|
329
|
+
"4": ("ollama", "llama3.2", None), # No API key needed
|
|
330
|
+
"5": ("custom", "", "LLM_API_KEY"),
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
provider, model, key_name = providers[choice]
|
|
334
|
+
|
|
335
|
+
# Handle Ollama setup
|
|
336
|
+
if choice == "4":
|
|
337
|
+
config.update(_setup_ollama(ollama_installed))
|
|
338
|
+
return config
|
|
339
|
+
|
|
340
|
+
if choice == "5":
|
|
341
|
+
provider = Prompt.ask("Enter provider name")
|
|
342
|
+
model = Prompt.ask("Enter model name")
|
|
343
|
+
key_name = "LLM_API_KEY"
|
|
344
|
+
|
|
345
|
+
config["AIPT_LLM__PROVIDER"] = provider
|
|
346
|
+
config["AIPT_LLM__MODEL"] = model
|
|
347
|
+
|
|
348
|
+
# Get API key
|
|
349
|
+
console.print(f"\n[bold]Enter your {provider.title()} API key:[/bold]")
|
|
350
|
+
|
|
351
|
+
if provider == "anthropic":
|
|
352
|
+
console.print("[dim]Get one at: https://console.anthropic.com/settings/keys[/dim]")
|
|
353
|
+
elif provider == "openai":
|
|
354
|
+
console.print("[dim]Get one at: https://platform.openai.com/api-keys[/dim]")
|
|
355
|
+
elif provider == "deepseek":
|
|
356
|
+
console.print("[dim]Get one at: https://platform.deepseek.com/api_keys[/dim]")
|
|
357
|
+
|
|
358
|
+
api_key = Prompt.ask("API Key", password=True)
|
|
359
|
+
|
|
360
|
+
if api_key and key_name:
|
|
361
|
+
config[key_name] = api_key
|
|
362
|
+
|
|
363
|
+
return config
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _setup_ollama(ollama_installed: bool) -> dict:
|
|
367
|
+
"""Configure Ollama for local LLM."""
|
|
368
|
+
config = {}
|
|
369
|
+
|
|
370
|
+
console.print("\n[bold cyan]═══ Ollama Local LLM Setup ═══[/bold cyan]")
|
|
371
|
+
|
|
372
|
+
if not ollama_installed:
|
|
373
|
+
console.print("\n[yellow]Ollama is not installed.[/yellow]")
|
|
374
|
+
console.print("Ollama allows you to run LLMs locally for FREE and offline.")
|
|
375
|
+
|
|
376
|
+
if Confirm.ask("\nWould you like to install Ollama now?", default=True):
|
|
377
|
+
_install_ollama()
|
|
378
|
+
else:
|
|
379
|
+
console.print("[dim]You can install Ollama later from: https://ollama.ai[/dim]")
|
|
380
|
+
console.print("[yellow]Falling back to cloud provider...[/yellow]")
|
|
381
|
+
return setup_llm() # Restart LLM setup
|
|
382
|
+
|
|
383
|
+
# Check if Ollama is running
|
|
384
|
+
loop = asyncio.new_event_loop()
|
|
385
|
+
try:
|
|
386
|
+
is_running = loop.run_until_complete(check_ollama_running())
|
|
387
|
+
finally:
|
|
388
|
+
loop.close()
|
|
389
|
+
|
|
390
|
+
if not is_running:
|
|
391
|
+
console.print("\n[yellow]Ollama server is not running.[/yellow]")
|
|
392
|
+
console.print("Start it with: [bold]ollama serve[/bold]")
|
|
393
|
+
|
|
394
|
+
if Confirm.ask("\nWould you like to start Ollama now?", default=True):
|
|
395
|
+
import subprocess
|
|
396
|
+
subprocess.Popen(
|
|
397
|
+
["ollama", "serve"],
|
|
398
|
+
stdout=subprocess.DEVNULL,
|
|
399
|
+
stderr=subprocess.DEVNULL,
|
|
400
|
+
)
|
|
401
|
+
console.print("[green]✓ Ollama server started[/green]")
|
|
402
|
+
import time
|
|
403
|
+
time.sleep(2) # Wait for server to start
|
|
404
|
+
|
|
405
|
+
# Get available models
|
|
406
|
+
loop = asyncio.new_event_loop()
|
|
407
|
+
try:
|
|
408
|
+
models = loop.run_until_complete(get_ollama_models())
|
|
409
|
+
finally:
|
|
410
|
+
loop.close()
|
|
411
|
+
|
|
412
|
+
# Recommended models for pentesting
|
|
413
|
+
recommended_models = [
|
|
414
|
+
("llama3.2", "Meta Llama 3.2 - Fast, good balance"),
|
|
415
|
+
("qwen2.5:14b", "Qwen 2.5 14B - Excellent for code/reasoning"),
|
|
416
|
+
("deepseek-r1:8b", "DeepSeek R1 8B - Strong reasoning"),
|
|
417
|
+
("codellama:13b", "Code Llama 13B - Optimized for code"),
|
|
418
|
+
]
|
|
419
|
+
|
|
420
|
+
console.print("\n[bold]Select a model:[/bold]")
|
|
421
|
+
|
|
422
|
+
# Show installed models first
|
|
423
|
+
if models:
|
|
424
|
+
console.print("\n[dim]Installed models:[/dim]")
|
|
425
|
+
for i, model in enumerate(models[:5], 1):
|
|
426
|
+
console.print(f" [{i}] {model} [green]✓ Ready[/green]")
|
|
427
|
+
|
|
428
|
+
console.print("\n[dim]Recommended models (will download):[/dim]")
|
|
429
|
+
offset = len(models[:5]) + 1
|
|
430
|
+
for i, (model, desc) in enumerate(recommended_models, offset):
|
|
431
|
+
installed = model.split(":")[0] in [m.split(":")[0] for m in models]
|
|
432
|
+
status = "[green]✓[/green]" if installed else "[dim]↓[/dim]"
|
|
433
|
+
console.print(f" [{i}] {model} - {desc} {status}")
|
|
434
|
+
|
|
435
|
+
# Let user choose
|
|
436
|
+
max_choice = offset + len(recommended_models) - 1
|
|
437
|
+
model_choice = Prompt.ask(
|
|
438
|
+
f"\nEnter choice (1-{max_choice}) or model name",
|
|
439
|
+
default="1"
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
try:
|
|
443
|
+
idx = int(model_choice)
|
|
444
|
+
if idx <= len(models[:5]):
|
|
445
|
+
selected_model = models[idx - 1]
|
|
446
|
+
else:
|
|
447
|
+
selected_model = recommended_models[idx - offset][0]
|
|
448
|
+
except ValueError:
|
|
449
|
+
selected_model = model_choice
|
|
450
|
+
|
|
451
|
+
# Check if model needs to be pulled
|
|
452
|
+
if selected_model not in models:
|
|
453
|
+
console.print(f"\n[cyan]Downloading model: {selected_model}[/cyan]")
|
|
454
|
+
console.print("[dim]This may take a few minutes...[/dim]")
|
|
455
|
+
|
|
456
|
+
import subprocess
|
|
457
|
+
try:
|
|
458
|
+
subprocess.run(
|
|
459
|
+
["ollama", "pull", selected_model],
|
|
460
|
+
check=True
|
|
461
|
+
)
|
|
462
|
+
console.print(f"[green]✓ Model {selected_model} downloaded[/green]")
|
|
463
|
+
except subprocess.CalledProcessError:
|
|
464
|
+
console.print(f"[red]Failed to download {selected_model}[/red]")
|
|
465
|
+
console.print("[yellow]You can download it later with: ollama pull {selected_model}[/yellow]")
|
|
466
|
+
|
|
467
|
+
config["AIPT_LLM__PROVIDER"] = "ollama"
|
|
468
|
+
config["AIPT_LLM__MODEL"] = selected_model
|
|
469
|
+
config["AIPT_LLM__OLLAMA_BASE_URL"] = "http://localhost:11434"
|
|
470
|
+
|
|
471
|
+
console.print(Panel(
|
|
472
|
+
f"[green]✓ Ollama configured![/green]\n\n"
|
|
473
|
+
f"Model: [bold]{selected_model}[/bold]\n"
|
|
474
|
+
f"Server: http://localhost:11434\n\n"
|
|
475
|
+
f"[dim]Benefits: Free, runs offline, no API limits[/dim]",
|
|
476
|
+
title="🦙 Local LLM Ready",
|
|
477
|
+
border_style="green"
|
|
478
|
+
))
|
|
479
|
+
|
|
480
|
+
return config
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _install_ollama():
|
|
484
|
+
"""Install Ollama on the system."""
|
|
485
|
+
import platform
|
|
486
|
+
import subprocess
|
|
487
|
+
|
|
488
|
+
system = platform.system().lower()
|
|
489
|
+
|
|
490
|
+
console.print("\n[cyan]Installing Ollama...[/cyan]")
|
|
491
|
+
|
|
492
|
+
try:
|
|
493
|
+
if system == "darwin": # macOS
|
|
494
|
+
# Check if Homebrew is available
|
|
495
|
+
if shutil.which("brew"):
|
|
496
|
+
subprocess.run(["brew", "install", "ollama"], check=True)
|
|
497
|
+
else:
|
|
498
|
+
# Use curl installer
|
|
499
|
+
subprocess.run(
|
|
500
|
+
["curl", "-fsSL", "https://ollama.ai/install.sh"],
|
|
501
|
+
stdout=subprocess.PIPE,
|
|
502
|
+
check=True
|
|
503
|
+
)
|
|
504
|
+
elif system == "linux":
|
|
505
|
+
subprocess.run(
|
|
506
|
+
"curl -fsSL https://ollama.ai/install.sh | sh",
|
|
507
|
+
shell=True,
|
|
508
|
+
check=True
|
|
509
|
+
)
|
|
510
|
+
elif system == "windows":
|
|
511
|
+
console.print("[yellow]Please install Ollama manually from: https://ollama.ai[/yellow]")
|
|
512
|
+
console.print("[dim]After installation, run 'ollama serve' to start the server[/dim]")
|
|
513
|
+
return
|
|
514
|
+
|
|
515
|
+
console.print("[green]✓ Ollama installed successfully[/green]")
|
|
516
|
+
|
|
517
|
+
except subprocess.CalledProcessError as e:
|
|
518
|
+
console.print(f"[red]Failed to install Ollama: {e}[/red]")
|
|
519
|
+
console.print("[dim]Please install manually from: https://ollama.ai[/dim]")
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def setup_scanners() -> dict:
|
|
523
|
+
"""Configure enterprise scanners (optional)."""
|
|
524
|
+
config = {}
|
|
525
|
+
|
|
526
|
+
console.print(Panel(
|
|
527
|
+
"[bold]Step 2: Enterprise Scanners (Optional)[/bold]\n\n"
|
|
528
|
+
"AIPTX integrates with enterprise DAST scanners for\n"
|
|
529
|
+
"comprehensive vulnerability assessment.",
|
|
530
|
+
title="🔍 Scanners",
|
|
531
|
+
border_style="cyan"
|
|
532
|
+
))
|
|
533
|
+
|
|
534
|
+
if not Confirm.ask("\nDo you want to configure enterprise scanners?", default=False):
|
|
535
|
+
console.print("[dim]Skipping scanner configuration...[/dim]\n")
|
|
536
|
+
return config
|
|
537
|
+
|
|
538
|
+
# Acunetix
|
|
539
|
+
if Confirm.ask("\n[bold]Configure Acunetix?[/bold]", default=False):
|
|
540
|
+
url = Prompt.ask(" Acunetix URL (e.g., https://your-instance:3443)")
|
|
541
|
+
api_key = Prompt.ask(" Acunetix API Key", password=True)
|
|
542
|
+
if url:
|
|
543
|
+
config["AIPT_SCANNERS__ACUNETIX_URL"] = url
|
|
544
|
+
if api_key:
|
|
545
|
+
config["AIPT_SCANNERS__ACUNETIX_API_KEY"] = api_key
|
|
546
|
+
|
|
547
|
+
# Burp Suite
|
|
548
|
+
if Confirm.ask("\n[bold]Configure Burp Suite Enterprise?[/bold]", default=False):
|
|
549
|
+
url = Prompt.ask(" Burp Suite URL (e.g., https://your-burp:8080)")
|
|
550
|
+
api_key = Prompt.ask(" Burp Suite API Key", password=True)
|
|
551
|
+
if url:
|
|
552
|
+
config["AIPT_SCANNERS__BURP_URL"] = url
|
|
553
|
+
if api_key:
|
|
554
|
+
config["AIPT_SCANNERS__BURP_API_KEY"] = api_key
|
|
555
|
+
|
|
556
|
+
# Nessus
|
|
557
|
+
if Confirm.ask("\n[bold]Configure Nessus?[/bold]", default=False):
|
|
558
|
+
url = Prompt.ask(" Nessus URL (e.g., https://your-nessus:8834)")
|
|
559
|
+
access_key = Prompt.ask(" Nessus Access Key", password=True)
|
|
560
|
+
secret_key = Prompt.ask(" Nessus Secret Key", password=True)
|
|
561
|
+
if url:
|
|
562
|
+
config["AIPT_SCANNERS__NESSUS_URL"] = url
|
|
563
|
+
if access_key:
|
|
564
|
+
config["AIPT_SCANNERS__NESSUS_ACCESS_KEY"] = access_key
|
|
565
|
+
if secret_key:
|
|
566
|
+
config["AIPT_SCANNERS__NESSUS_SECRET_KEY"] = secret_key
|
|
567
|
+
|
|
568
|
+
# OWASP ZAP
|
|
569
|
+
if Confirm.ask("\n[bold]Configure OWASP ZAP?[/bold]", default=False):
|
|
570
|
+
url = Prompt.ask(" ZAP URL (e.g., http://localhost:8080)")
|
|
571
|
+
api_key = Prompt.ask(" ZAP API Key (leave empty if disabled)", password=True)
|
|
572
|
+
if url:
|
|
573
|
+
config["AIPT_SCANNERS__ZAP_URL"] = url
|
|
574
|
+
if api_key:
|
|
575
|
+
config["AIPT_SCANNERS__ZAP_API_KEY"] = api_key
|
|
576
|
+
|
|
577
|
+
return config
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def setup_vps() -> dict:
|
|
581
|
+
"""Configure VPS for remote execution (optional)."""
|
|
582
|
+
config = {}
|
|
583
|
+
|
|
584
|
+
console.print(Panel(
|
|
585
|
+
"[bold]Step 3: VPS Configuration (Optional)[/bold]\n\n"
|
|
586
|
+
"Run security tools on a remote VPS to avoid\n"
|
|
587
|
+
"network restrictions and maintain anonymity.",
|
|
588
|
+
title="🖥️ VPS",
|
|
589
|
+
border_style="cyan"
|
|
590
|
+
))
|
|
591
|
+
|
|
592
|
+
if not Confirm.ask("\nDo you want to configure a VPS for remote execution?", default=False):
|
|
593
|
+
console.print("[dim]Skipping VPS configuration...[/dim]\n")
|
|
594
|
+
return config
|
|
595
|
+
|
|
596
|
+
host = Prompt.ask(" VPS IP or hostname")
|
|
597
|
+
user = Prompt.ask(" SSH username", default="ubuntu")
|
|
598
|
+
key_path = Prompt.ask(" Path to SSH private key (e.g., ~/.ssh/id_rsa)")
|
|
599
|
+
port = Prompt.ask(" SSH port", default="22")
|
|
600
|
+
|
|
601
|
+
if host:
|
|
602
|
+
config["AIPT_VPS__HOST"] = host
|
|
603
|
+
if user:
|
|
604
|
+
config["AIPT_VPS__USER"] = user
|
|
605
|
+
if key_path:
|
|
606
|
+
# Expand ~ to home directory
|
|
607
|
+
expanded_path = str(Path(key_path).expanduser())
|
|
608
|
+
config["AIPT_VPS__KEY_PATH"] = expanded_path
|
|
609
|
+
if port:
|
|
610
|
+
config["AIPT_VPS__PORT"] = port
|
|
611
|
+
|
|
612
|
+
return config
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
async def setup_security_tools(system_info=None) -> Dict[str, bool]:
|
|
616
|
+
"""
|
|
617
|
+
Install security tools on the local system.
|
|
618
|
+
|
|
619
|
+
Args:
|
|
620
|
+
system_info: Pre-detected system info
|
|
621
|
+
|
|
622
|
+
Returns:
|
|
623
|
+
Dict mapping tool names to installation status
|
|
624
|
+
"""
|
|
625
|
+
console.print(Panel(
|
|
626
|
+
"[bold]Step 4: Security Tools Installation[/bold]\n\n"
|
|
627
|
+
"AIPTX uses various security tools for penetration testing.\n"
|
|
628
|
+
"These tools will be installed on your local system.",
|
|
629
|
+
title="🔧 Security Tools",
|
|
630
|
+
border_style="cyan"
|
|
631
|
+
))
|
|
632
|
+
|
|
633
|
+
try:
|
|
634
|
+
LocalToolInstaller, TOOLS, ToolCategory = _get_tool_installer()
|
|
635
|
+
except ImportError:
|
|
636
|
+
console.print("[yellow]Tool installer not available. Skipping...[/yellow]")
|
|
637
|
+
return {}
|
|
638
|
+
|
|
639
|
+
# Show what will be installed
|
|
640
|
+
core_tools = [name for name, tool in TOOLS.items() if tool.is_core]
|
|
641
|
+
|
|
642
|
+
console.print("\n[bold]Core tools to install:[/bold]")
|
|
643
|
+
for tool_name in core_tools[:8]:
|
|
644
|
+
tool = TOOLS.get(tool_name)
|
|
645
|
+
console.print(f" • {tool_name} - [dim]{tool.description[:50]}...[/dim]")
|
|
646
|
+
|
|
647
|
+
console.print(f"\n[dim]Total: {len(core_tools)} core tools, {len(TOOLS)} available[/dim]")
|
|
648
|
+
|
|
649
|
+
# Choose installation scope
|
|
650
|
+
console.print("\n[bold]Installation options:[/bold]")
|
|
651
|
+
console.print(" [1] Core tools only - [green]Recommended[/green] - Quick install of essential tools")
|
|
652
|
+
console.print(" [2] Full installation - Install all available security tools")
|
|
653
|
+
console.print(" [3] Custom selection - Choose categories to install")
|
|
654
|
+
console.print(" [4] Skip - Don't install any tools now")
|
|
655
|
+
|
|
656
|
+
choice = Prompt.ask("\nEnter choice", choices=["1", "2", "3", "4"], default="1")
|
|
657
|
+
|
|
658
|
+
if choice == "4":
|
|
659
|
+
console.print("[dim]Skipping tool installation. You can install later with: aiptx tools install[/dim]")
|
|
660
|
+
return {}
|
|
661
|
+
|
|
662
|
+
installer = LocalToolInstaller(system_info)
|
|
663
|
+
|
|
664
|
+
# Check if we need sudo
|
|
665
|
+
has_sudo = system_info.capabilities.has_sudo if system_info else True
|
|
666
|
+
if not has_sudo:
|
|
667
|
+
console.print("\n[yellow]Note: Some tools may require sudo/admin privileges.[/yellow]")
|
|
668
|
+
console.print("[dim]You may be prompted for your password.[/dim]")
|
|
669
|
+
|
|
670
|
+
results = {}
|
|
671
|
+
|
|
672
|
+
if choice == "1":
|
|
673
|
+
# Core tools only
|
|
674
|
+
console.print("\n[cyan]Installing core security tools...[/cyan]")
|
|
675
|
+
results = await installer.install_core_tools()
|
|
676
|
+
|
|
677
|
+
elif choice == "2":
|
|
678
|
+
# Full installation
|
|
679
|
+
console.print("\n[cyan]Installing all security tools...[/cyan]")
|
|
680
|
+
console.print("[dim]This may take 10-20 minutes...[/dim]")
|
|
681
|
+
results = await installer.install_all()
|
|
682
|
+
|
|
683
|
+
elif choice == "3":
|
|
684
|
+
# Custom selection
|
|
685
|
+
console.print("\n[bold]Select categories to install:[/bold]")
|
|
686
|
+
console.print(" [1] Recon - Subdomain discovery, port scanning, fingerprinting")
|
|
687
|
+
console.print(" [2] Scan - Vulnerability scanning, fuzzing, content discovery")
|
|
688
|
+
console.print(" [3] Exploit - SQL injection, brute forcing, exploitation")
|
|
689
|
+
console.print(" [4] Network - Fast port scanning, network analysis")
|
|
690
|
+
console.print(" [5] API - API security testing tools")
|
|
691
|
+
|
|
692
|
+
cat_choice = Prompt.ask(
|
|
693
|
+
"\nEnter categories (comma-separated, e.g., 1,2,3)",
|
|
694
|
+
default="1,2"
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
category_map = {
|
|
698
|
+
"1": "recon",
|
|
699
|
+
"2": "scan",
|
|
700
|
+
"3": "exploit",
|
|
701
|
+
"4": "network",
|
|
702
|
+
"5": "api",
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
categories = [
|
|
706
|
+
category_map[c.strip()]
|
|
707
|
+
for c in cat_choice.split(",")
|
|
708
|
+
if c.strip() in category_map
|
|
709
|
+
]
|
|
710
|
+
|
|
711
|
+
if categories:
|
|
712
|
+
console.print(f"\n[cyan]Installing {', '.join(categories)} tools...[/cyan]")
|
|
713
|
+
results = await installer.install_tools(categories=categories)
|
|
714
|
+
else:
|
|
715
|
+
console.print("[yellow]No valid categories selected.[/yellow]")
|
|
716
|
+
|
|
717
|
+
# Show summary
|
|
718
|
+
if results:
|
|
719
|
+
installed = sum(1 for r in results.values() if r.success and not r.already_installed)
|
|
720
|
+
already = sum(1 for r in results.values() if r.already_installed)
|
|
721
|
+
failed = sum(1 for r in results.values() if not r.success)
|
|
722
|
+
|
|
723
|
+
console.print(Panel(
|
|
724
|
+
f"[bold]Installation Complete[/bold]\n\n"
|
|
725
|
+
f"[green]✓ Installed:[/green] {installed}\n"
|
|
726
|
+
f"[dim]○ Already installed:[/dim] {already}\n"
|
|
727
|
+
f"[red]✗ Failed:[/red] {failed}",
|
|
728
|
+
title="📊 Tool Installation Summary",
|
|
729
|
+
border_style="green" if failed == 0 else "yellow"
|
|
730
|
+
))
|
|
731
|
+
|
|
732
|
+
return {name: result.success for name, result in results.items()} if results else {}
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def show_summary(config: dict, tools_installed: Dict[str, bool] = None):
|
|
736
|
+
"""Show configuration summary."""
|
|
737
|
+
console.print(Panel(
|
|
738
|
+
"[bold]Configuration Summary[/bold]",
|
|
739
|
+
title="📋 Summary",
|
|
740
|
+
border_style="green"
|
|
741
|
+
))
|
|
742
|
+
|
|
743
|
+
table = Table(box=box.ROUNDED)
|
|
744
|
+
table.add_column("Setting", style="cyan")
|
|
745
|
+
table.add_column("Value", style="green")
|
|
746
|
+
|
|
747
|
+
# LLM
|
|
748
|
+
provider = config.get("AIPT_LLM__PROVIDER", "Not set")
|
|
749
|
+
model = config.get("AIPT_LLM__MODEL", "Not set")
|
|
750
|
+
has_key = any(k in config for k in ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "DEEPSEEK_API_KEY", "LLM_API_KEY"])
|
|
751
|
+
is_ollama = provider == "ollama"
|
|
752
|
+
|
|
753
|
+
table.add_row("LLM Provider", provider)
|
|
754
|
+
table.add_row("LLM Model", model)
|
|
755
|
+
if is_ollama:
|
|
756
|
+
table.add_row("LLM Mode", "✓ Local (Ollama)")
|
|
757
|
+
else:
|
|
758
|
+
table.add_row("LLM API Key", "✓ Configured" if has_key else "✗ Not set")
|
|
759
|
+
|
|
760
|
+
# Scanners
|
|
761
|
+
table.add_row("─" * 20, "─" * 20)
|
|
762
|
+
table.add_row("Acunetix", "✓ Configured" if config.get("AIPT_SCANNERS__ACUNETIX_URL") else "○ Not configured")
|
|
763
|
+
table.add_row("Burp Suite", "✓ Configured" if config.get("AIPT_SCANNERS__BURP_URL") else "○ Not configured")
|
|
764
|
+
table.add_row("Nessus", "✓ Configured" if config.get("AIPT_SCANNERS__NESSUS_URL") else "○ Not configured")
|
|
765
|
+
table.add_row("OWASP ZAP", "✓ Configured" if config.get("AIPT_SCANNERS__ZAP_URL") else "○ Not configured")
|
|
766
|
+
|
|
767
|
+
# VPS
|
|
768
|
+
table.add_row("─" * 20, "─" * 20)
|
|
769
|
+
table.add_row("VPS", "✓ " + config.get("AIPT_VPS__HOST", "") if config.get("AIPT_VPS__HOST") else "○ Not configured")
|
|
770
|
+
|
|
771
|
+
# Tools
|
|
772
|
+
if tools_installed:
|
|
773
|
+
table.add_row("─" * 20, "─" * 20)
|
|
774
|
+
installed_count = sum(1 for v in tools_installed.values() if v)
|
|
775
|
+
table.add_row("Security Tools", f"✓ {installed_count} tools installed")
|
|
776
|
+
|
|
777
|
+
console.print(table)
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def run_setup_wizard(force: bool = False) -> bool:
|
|
781
|
+
"""
|
|
782
|
+
Run the interactive setup wizard.
|
|
783
|
+
|
|
784
|
+
Args:
|
|
785
|
+
force: Run even if already configured
|
|
786
|
+
|
|
787
|
+
Returns:
|
|
788
|
+
True if setup completed successfully
|
|
789
|
+
"""
|
|
790
|
+
# Use asyncio to run the async version
|
|
791
|
+
try:
|
|
792
|
+
loop = asyncio.new_event_loop()
|
|
793
|
+
asyncio.set_event_loop(loop)
|
|
794
|
+
try:
|
|
795
|
+
return loop.run_until_complete(_run_setup_wizard_async(force))
|
|
796
|
+
finally:
|
|
797
|
+
loop.close()
|
|
798
|
+
except KeyboardInterrupt:
|
|
799
|
+
console.print("\n[yellow]Setup cancelled.[/yellow]")
|
|
800
|
+
return False
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
async def _run_setup_wizard_async(force: bool = False) -> bool:
|
|
804
|
+
"""
|
|
805
|
+
Async implementation of the setup wizard.
|
|
806
|
+
|
|
807
|
+
Args:
|
|
808
|
+
force: Run even if already configured
|
|
809
|
+
|
|
810
|
+
Returns:
|
|
811
|
+
True if setup completed successfully
|
|
812
|
+
"""
|
|
813
|
+
try:
|
|
814
|
+
# Check if already configured
|
|
815
|
+
if not force and is_configured():
|
|
816
|
+
console.print("[yellow]AIPTX is already configured.[/yellow]")
|
|
817
|
+
if not Confirm.ask("Do you want to reconfigure?", default=False):
|
|
818
|
+
return False
|
|
819
|
+
|
|
820
|
+
print_welcome()
|
|
821
|
+
|
|
822
|
+
# Collect configuration
|
|
823
|
+
config = load_existing_config() # Start with existing config
|
|
824
|
+
tools_installed = {}
|
|
825
|
+
|
|
826
|
+
# Step 1: System Detection (NEW)
|
|
827
|
+
console.print()
|
|
828
|
+
system_info = await detect_system()
|
|
829
|
+
|
|
830
|
+
# Step 2: LLM Configuration
|
|
831
|
+
console.print()
|
|
832
|
+
llm_config = setup_llm()
|
|
833
|
+
config.update(llm_config)
|
|
834
|
+
|
|
835
|
+
# Check if we got an API key or using Ollama
|
|
836
|
+
is_ollama = config.get("AIPT_LLM__PROVIDER") == "ollama"
|
|
837
|
+
has_key = any(k in config for k in ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "DEEPSEEK_API_KEY", "LLM_API_KEY"])
|
|
838
|
+
|
|
839
|
+
if not has_key and not is_ollama:
|
|
840
|
+
console.print("\n[bold red]Error:[/bold red] An LLM API key is required to use AIPTX.")
|
|
841
|
+
console.print("Please run [bold]aiptx setup[/bold] again with a valid API key.")
|
|
842
|
+
return False
|
|
843
|
+
|
|
844
|
+
# Step 3: Scanners (optional)
|
|
845
|
+
console.print()
|
|
846
|
+
scanner_config = setup_scanners()
|
|
847
|
+
config.update(scanner_config)
|
|
848
|
+
|
|
849
|
+
# Step 4: Security Tools Installation (NEW)
|
|
850
|
+
console.print()
|
|
851
|
+
if Confirm.ask("\nWould you like to install security tools now?", default=True):
|
|
852
|
+
tools_installed = await setup_security_tools(system_info)
|
|
853
|
+
|
|
854
|
+
# Step 5: VPS (optional)
|
|
855
|
+
console.print()
|
|
856
|
+
vps_config = setup_vps()
|
|
857
|
+
config.update(vps_config)
|
|
858
|
+
|
|
859
|
+
# Show summary
|
|
860
|
+
console.print()
|
|
861
|
+
show_summary(config, tools_installed)
|
|
862
|
+
|
|
863
|
+
# Confirm and save
|
|
864
|
+
console.print()
|
|
865
|
+
if Confirm.ask("[bold]Save this configuration?[/bold]", default=True):
|
|
866
|
+
config_path = save_config(config)
|
|
867
|
+
|
|
868
|
+
# Build dynamic completion message
|
|
869
|
+
next_steps = []
|
|
870
|
+
if is_ollama:
|
|
871
|
+
next_steps.append(" [bold]ollama serve[/bold] - Start Ollama (if not running)")
|
|
872
|
+
next_steps.extend([
|
|
873
|
+
" [bold]aiptx scan example.com[/bold] - Run a security scan",
|
|
874
|
+
" [bold]aiptx status[/bold] - Check configuration",
|
|
875
|
+
" [bold]aiptx tools install[/bold] - Install more security tools",
|
|
876
|
+
" [bold]aiptx setup[/bold] - Reconfigure AIPTX",
|
|
877
|
+
])
|
|
878
|
+
|
|
879
|
+
console.print(Panel(
|
|
880
|
+
f"[bold green]✓ Configuration saved![/bold green]\n\n"
|
|
881
|
+
f"Config file: [cyan]{config_path}[/cyan]\n\n"
|
|
882
|
+
f"[bold]Next steps:[/bold]\n" +
|
|
883
|
+
"\n".join(next_steps),
|
|
884
|
+
title="🎉 Setup Complete",
|
|
885
|
+
border_style="green"
|
|
886
|
+
))
|
|
887
|
+
|
|
888
|
+
# Load the config into environment for immediate use
|
|
889
|
+
for key, value in config.items():
|
|
890
|
+
os.environ[key] = value
|
|
891
|
+
|
|
892
|
+
return True
|
|
893
|
+
else:
|
|
894
|
+
console.print("[yellow]Setup cancelled. No changes saved.[/yellow]")
|
|
895
|
+
return False
|
|
896
|
+
|
|
897
|
+
except KeyboardInterrupt:
|
|
898
|
+
# Handle Ctrl+C gracefully
|
|
899
|
+
console.print("\n[yellow]Setup cancelled.[/yellow]")
|
|
900
|
+
return False
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
def prompt_first_run_setup() -> bool:
|
|
904
|
+
"""
|
|
905
|
+
Prompt for setup on first run when configuration is missing.
|
|
906
|
+
|
|
907
|
+
Returns:
|
|
908
|
+
True if setup completed and user can proceed
|
|
909
|
+
"""
|
|
910
|
+
try:
|
|
911
|
+
console.print(Panel(
|
|
912
|
+
"[bold yellow]⚠ AIPTX is not configured![/bold yellow]\n\n"
|
|
913
|
+
"This appears to be your first time running AIPTX.\n"
|
|
914
|
+
"You need to configure at least an LLM API key to proceed.",
|
|
915
|
+
title="First Run Setup Required",
|
|
916
|
+
border_style="yellow"
|
|
917
|
+
))
|
|
918
|
+
|
|
919
|
+
if Confirm.ask("\nWould you like to run the setup wizard now?", default=True):
|
|
920
|
+
return run_setup_wizard(force=True)
|
|
921
|
+
else:
|
|
922
|
+
console.print("\n[dim]You can run setup later with: [bold]aiptx setup[/bold][/dim]")
|
|
923
|
+
console.print("[dim]Or set environment variables manually:[/dim]")
|
|
924
|
+
console.print("[dim] export ANTHROPIC_API_KEY=your-key-here[/dim]\n")
|
|
925
|
+
return False
|
|
926
|
+
except KeyboardInterrupt:
|
|
927
|
+
console.print("\n[yellow]Cancelled.[/yellow]")
|
|
928
|
+
return False
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
# ============================================================================
|
|
932
|
+
# CLI Entry Points
|
|
933
|
+
# ============================================================================
|
|
934
|
+
|
|
935
|
+
def main():
|
|
936
|
+
"""Standalone setup wizard entry point."""
|
|
937
|
+
run_setup_wizard(force=True)
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
if __name__ == "__main__":
|
|
941
|
+
main()
|