aiptx 2.0.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. aipt_v2/__init__.py +110 -0
  2. aipt_v2/__main__.py +24 -0
  3. aipt_v2/agents/AIPTxAgent/__init__.py +10 -0
  4. aipt_v2/agents/AIPTxAgent/aiptx_agent.py +211 -0
  5. aipt_v2/agents/__init__.py +46 -0
  6. aipt_v2/agents/base.py +520 -0
  7. aipt_v2/agents/exploit_agent.py +688 -0
  8. aipt_v2/agents/ptt.py +406 -0
  9. aipt_v2/agents/state.py +168 -0
  10. aipt_v2/app.py +957 -0
  11. aipt_v2/browser/__init__.py +31 -0
  12. aipt_v2/browser/automation.py +458 -0
  13. aipt_v2/browser/crawler.py +453 -0
  14. aipt_v2/cli.py +2933 -0
  15. aipt_v2/compliance/__init__.py +71 -0
  16. aipt_v2/compliance/compliance_report.py +449 -0
  17. aipt_v2/compliance/framework_mapper.py +424 -0
  18. aipt_v2/compliance/nist_mapping.py +345 -0
  19. aipt_v2/compliance/owasp_mapping.py +330 -0
  20. aipt_v2/compliance/pci_mapping.py +297 -0
  21. aipt_v2/config.py +341 -0
  22. aipt_v2/core/__init__.py +43 -0
  23. aipt_v2/core/agent.py +630 -0
  24. aipt_v2/core/llm.py +395 -0
  25. aipt_v2/core/memory.py +305 -0
  26. aipt_v2/core/ptt.py +329 -0
  27. aipt_v2/database/__init__.py +14 -0
  28. aipt_v2/database/models.py +232 -0
  29. aipt_v2/database/repository.py +384 -0
  30. aipt_v2/docker/__init__.py +23 -0
  31. aipt_v2/docker/builder.py +260 -0
  32. aipt_v2/docker/manager.py +222 -0
  33. aipt_v2/docker/sandbox.py +371 -0
  34. aipt_v2/evasion/__init__.py +58 -0
  35. aipt_v2/evasion/request_obfuscator.py +272 -0
  36. aipt_v2/evasion/tls_fingerprint.py +285 -0
  37. aipt_v2/evasion/ua_rotator.py +301 -0
  38. aipt_v2/evasion/waf_bypass.py +439 -0
  39. aipt_v2/execution/__init__.py +23 -0
  40. aipt_v2/execution/executor.py +302 -0
  41. aipt_v2/execution/parser.py +544 -0
  42. aipt_v2/execution/terminal.py +337 -0
  43. aipt_v2/health.py +437 -0
  44. aipt_v2/intelligence/__init__.py +194 -0
  45. aipt_v2/intelligence/adaptation.py +474 -0
  46. aipt_v2/intelligence/auth.py +520 -0
  47. aipt_v2/intelligence/chaining.py +775 -0
  48. aipt_v2/intelligence/correlation.py +536 -0
  49. aipt_v2/intelligence/cve_aipt.py +334 -0
  50. aipt_v2/intelligence/cve_info.py +1111 -0
  51. aipt_v2/intelligence/knowledge_graph.py +590 -0
  52. aipt_v2/intelligence/learning.py +626 -0
  53. aipt_v2/intelligence/llm_analyzer.py +502 -0
  54. aipt_v2/intelligence/llm_tool_selector.py +518 -0
  55. aipt_v2/intelligence/payload_generator.py +562 -0
  56. aipt_v2/intelligence/rag.py +239 -0
  57. aipt_v2/intelligence/scope.py +442 -0
  58. aipt_v2/intelligence/searchers/__init__.py +5 -0
  59. aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
  60. aipt_v2/intelligence/searchers/github_searcher.py +467 -0
  61. aipt_v2/intelligence/searchers/google_searcher.py +281 -0
  62. aipt_v2/intelligence/tools.json +443 -0
  63. aipt_v2/intelligence/triage.py +670 -0
  64. aipt_v2/interactive_shell.py +559 -0
  65. aipt_v2/interface/__init__.py +5 -0
  66. aipt_v2/interface/cli.py +230 -0
  67. aipt_v2/interface/main.py +501 -0
  68. aipt_v2/interface/tui.py +1276 -0
  69. aipt_v2/interface/utils.py +583 -0
  70. aipt_v2/llm/__init__.py +39 -0
  71. aipt_v2/llm/config.py +26 -0
  72. aipt_v2/llm/llm.py +514 -0
  73. aipt_v2/llm/memory.py +214 -0
  74. aipt_v2/llm/request_queue.py +89 -0
  75. aipt_v2/llm/utils.py +89 -0
  76. aipt_v2/local_tool_installer.py +1467 -0
  77. aipt_v2/models/__init__.py +15 -0
  78. aipt_v2/models/findings.py +295 -0
  79. aipt_v2/models/phase_result.py +224 -0
  80. aipt_v2/models/scan_config.py +207 -0
  81. aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
  82. aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
  83. aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
  84. aipt_v2/monitoring/prometheus.yml +60 -0
  85. aipt_v2/orchestration/__init__.py +52 -0
  86. aipt_v2/orchestration/pipeline.py +398 -0
  87. aipt_v2/orchestration/progress.py +300 -0
  88. aipt_v2/orchestration/scheduler.py +296 -0
  89. aipt_v2/orchestrator.py +2427 -0
  90. aipt_v2/payloads/__init__.py +27 -0
  91. aipt_v2/payloads/cmdi.py +150 -0
  92. aipt_v2/payloads/sqli.py +263 -0
  93. aipt_v2/payloads/ssrf.py +204 -0
  94. aipt_v2/payloads/templates.py +222 -0
  95. aipt_v2/payloads/traversal.py +166 -0
  96. aipt_v2/payloads/xss.py +204 -0
  97. aipt_v2/prompts/__init__.py +60 -0
  98. aipt_v2/proxy/__init__.py +29 -0
  99. aipt_v2/proxy/history.py +352 -0
  100. aipt_v2/proxy/interceptor.py +452 -0
  101. aipt_v2/recon/__init__.py +44 -0
  102. aipt_v2/recon/dns.py +241 -0
  103. aipt_v2/recon/osint.py +367 -0
  104. aipt_v2/recon/subdomain.py +372 -0
  105. aipt_v2/recon/tech_detect.py +311 -0
  106. aipt_v2/reports/__init__.py +17 -0
  107. aipt_v2/reports/generator.py +313 -0
  108. aipt_v2/reports/html_report.py +378 -0
  109. aipt_v2/runtime/__init__.py +53 -0
  110. aipt_v2/runtime/base.py +30 -0
  111. aipt_v2/runtime/docker.py +401 -0
  112. aipt_v2/runtime/local.py +346 -0
  113. aipt_v2/runtime/tool_server.py +205 -0
  114. aipt_v2/runtime/vps.py +830 -0
  115. aipt_v2/scanners/__init__.py +28 -0
  116. aipt_v2/scanners/base.py +273 -0
  117. aipt_v2/scanners/nikto.py +244 -0
  118. aipt_v2/scanners/nmap.py +402 -0
  119. aipt_v2/scanners/nuclei.py +273 -0
  120. aipt_v2/scanners/web.py +454 -0
  121. aipt_v2/scripts/security_audit.py +366 -0
  122. aipt_v2/setup_wizard.py +941 -0
  123. aipt_v2/skills/__init__.py +80 -0
  124. aipt_v2/skills/agents/__init__.py +14 -0
  125. aipt_v2/skills/agents/api_tester.py +706 -0
  126. aipt_v2/skills/agents/base.py +477 -0
  127. aipt_v2/skills/agents/code_review.py +459 -0
  128. aipt_v2/skills/agents/security_agent.py +336 -0
  129. aipt_v2/skills/agents/web_pentest.py +818 -0
  130. aipt_v2/skills/prompts/__init__.py +647 -0
  131. aipt_v2/system_detector.py +539 -0
  132. aipt_v2/telemetry/__init__.py +7 -0
  133. aipt_v2/telemetry/tracer.py +347 -0
  134. aipt_v2/terminal/__init__.py +28 -0
  135. aipt_v2/terminal/executor.py +400 -0
  136. aipt_v2/terminal/sandbox.py +350 -0
  137. aipt_v2/tools/__init__.py +44 -0
  138. aipt_v2/tools/active_directory/__init__.py +78 -0
  139. aipt_v2/tools/active_directory/ad_config.py +238 -0
  140. aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
  141. aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
  142. aipt_v2/tools/active_directory/ldap_enum.py +533 -0
  143. aipt_v2/tools/active_directory/smb_attacks.py +505 -0
  144. aipt_v2/tools/agents_graph/__init__.py +19 -0
  145. aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
  146. aipt_v2/tools/api_security/__init__.py +76 -0
  147. aipt_v2/tools/api_security/api_discovery.py +608 -0
  148. aipt_v2/tools/api_security/graphql_scanner.py +622 -0
  149. aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
  150. aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
  151. aipt_v2/tools/browser/__init__.py +5 -0
  152. aipt_v2/tools/browser/browser_actions.py +238 -0
  153. aipt_v2/tools/browser/browser_instance.py +535 -0
  154. aipt_v2/tools/browser/tab_manager.py +344 -0
  155. aipt_v2/tools/cloud/__init__.py +70 -0
  156. aipt_v2/tools/cloud/cloud_config.py +273 -0
  157. aipt_v2/tools/cloud/cloud_scanner.py +639 -0
  158. aipt_v2/tools/cloud/prowler_tool.py +571 -0
  159. aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
  160. aipt_v2/tools/executor.py +307 -0
  161. aipt_v2/tools/parser.py +408 -0
  162. aipt_v2/tools/proxy/__init__.py +5 -0
  163. aipt_v2/tools/proxy/proxy_actions.py +103 -0
  164. aipt_v2/tools/proxy/proxy_manager.py +789 -0
  165. aipt_v2/tools/registry.py +196 -0
  166. aipt_v2/tools/scanners/__init__.py +343 -0
  167. aipt_v2/tools/scanners/acunetix_tool.py +712 -0
  168. aipt_v2/tools/scanners/burp_tool.py +631 -0
  169. aipt_v2/tools/scanners/config.py +156 -0
  170. aipt_v2/tools/scanners/nessus_tool.py +588 -0
  171. aipt_v2/tools/scanners/zap_tool.py +612 -0
  172. aipt_v2/tools/terminal/__init__.py +5 -0
  173. aipt_v2/tools/terminal/terminal_actions.py +37 -0
  174. aipt_v2/tools/terminal/terminal_manager.py +153 -0
  175. aipt_v2/tools/terminal/terminal_session.py +449 -0
  176. aipt_v2/tools/tool_processing.py +108 -0
  177. aipt_v2/utils/__init__.py +17 -0
  178. aipt_v2/utils/logging.py +202 -0
  179. aipt_v2/utils/model_manager.py +187 -0
  180. aipt_v2/utils/searchers/__init__.py +269 -0
  181. aipt_v2/verify_install.py +793 -0
  182. aiptx-2.0.7.dist-info/METADATA +345 -0
  183. aiptx-2.0.7.dist-info/RECORD +187 -0
  184. aiptx-2.0.7.dist-info/WHEEL +5 -0
  185. aiptx-2.0.7.dist-info/entry_points.txt +7 -0
  186. aiptx-2.0.7.dist-info/licenses/LICENSE +21 -0
  187. aiptx-2.0.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,793 @@
1
+ """
2
+ AIPTX Post-Installation Verification
3
+ =====================================
4
+
5
+ Verifies that AIPTX is correctly installed and configured.
6
+ Tests all components and generates a health report.
7
+
8
+ Features:
9
+ - System requirements check
10
+ - Python dependencies verification
11
+ - Security tools validation
12
+ - LLM connectivity test
13
+ - Configuration validation
14
+ - Performance benchmarks
15
+
16
+ Usage:
17
+ aiptx verify # Run full verification
18
+ aiptx verify --quick # Quick check
19
+ aiptx verify --fix # Auto-fix issues where possible
20
+ aiptx verify --report out.md # Generate markdown report
21
+ """
22
+
23
+ import asyncio
24
+ import importlib
25
+ import json
26
+ import os
27
+ import shutil
28
+ import subprocess
29
+ import sys
30
+ import time
31
+ from dataclasses import dataclass, field
32
+ from datetime import datetime
33
+ from enum import Enum
34
+ from pathlib import Path
35
+ from typing import Dict, List, Optional, Tuple, Any
36
+
37
+ from rich.console import Console
38
+ from rich.panel import Panel
39
+ from rich.table import Table
40
+ from rich.progress import Progress, SpinnerColumn, TextColumn
41
+ from rich import box
42
+
43
+
44
+ console = Console()
45
+
46
+
47
+ class CheckStatus(Enum):
48
+ """Status of a verification check."""
49
+ PASS = "pass"
50
+ WARN = "warn"
51
+ FAIL = "fail"
52
+ SKIP = "skip"
53
+
54
+
55
+ @dataclass
56
+ class CheckResult:
57
+ """Result of a single verification check."""
58
+ name: str
59
+ status: CheckStatus
60
+ message: str
61
+ details: Optional[str] = None
62
+ fix_command: Optional[str] = None
63
+ duration_ms: float = 0
64
+
65
+
66
+ @dataclass
67
+ class VerificationReport:
68
+ """Complete verification report."""
69
+ timestamp: datetime = field(default_factory=datetime.now)
70
+ system_info: Dict[str, str] = field(default_factory=dict)
71
+ checks: List[CheckResult] = field(default_factory=list)
72
+ tool_status: Dict[str, bool] = field(default_factory=dict)
73
+ summary: Dict[str, int] = field(default_factory=dict)
74
+
75
+ def add_check(self, result: CheckResult):
76
+ """Add a check result."""
77
+ self.checks.append(result)
78
+
79
+ def compute_summary(self):
80
+ """Compute summary statistics."""
81
+ self.summary = {
82
+ "total": len(self.checks),
83
+ "passed": sum(1 for c in self.checks if c.status == CheckStatus.PASS),
84
+ "warnings": sum(1 for c in self.checks if c.status == CheckStatus.WARN),
85
+ "failed": sum(1 for c in self.checks if c.status == CheckStatus.FAIL),
86
+ "skipped": sum(1 for c in self.checks if c.status == CheckStatus.SKIP),
87
+ }
88
+
89
+ def is_healthy(self) -> bool:
90
+ """Check if installation is healthy (no failures)."""
91
+ return all(c.status != CheckStatus.FAIL for c in self.checks)
92
+
93
+
94
+ class InstallVerifier:
95
+ """
96
+ Verifies AIPTX installation and configuration.
97
+
98
+ Runs a series of checks to ensure the system is properly
99
+ set up and all components are working correctly.
100
+ """
101
+
102
+ def __init__(self, quick: bool = False, auto_fix: bool = False):
103
+ """
104
+ Initialize verifier.
105
+
106
+ Args:
107
+ quick: Run quick checks only
108
+ auto_fix: Attempt to fix issues automatically
109
+ """
110
+ self.quick = quick
111
+ self.auto_fix = auto_fix
112
+ self.report = VerificationReport()
113
+
114
+ async def run_all_checks(self) -> VerificationReport:
115
+ """Run all verification checks."""
116
+ console.print()
117
+ console.print(Panel(
118
+ "[bold cyan]AIPTX Installation Verification[/bold cyan]\n\n"
119
+ "Running comprehensive checks to verify your installation...",
120
+ title="🔍 Verification",
121
+ border_style="cyan"
122
+ ))
123
+ console.print()
124
+
125
+ # Collect system info
126
+ await self._collect_system_info()
127
+
128
+ # Run checks with progress
129
+ checks = [
130
+ ("Python Version", self._check_python_version),
131
+ ("Python Dependencies", self._check_python_deps),
132
+ ("AIPTX Package", self._check_aiptx_package),
133
+ ("Configuration File", self._check_config_file),
134
+ ("LLM Configuration", self._check_llm_config),
135
+ ("Go Installation", self._check_go),
136
+ ("Docker (optional)", self._check_docker),
137
+ ("Core Tools", self._check_core_tools),
138
+ ("Path Configuration", self._check_path),
139
+ ("Permissions", self._check_permissions),
140
+ ]
141
+
142
+ if not self.quick:
143
+ checks.extend([
144
+ ("All Security Tools", self._check_all_tools),
145
+ ("Network Connectivity", self._check_network),
146
+ ("LLM Connectivity", self._check_llm_connectivity),
147
+ ])
148
+
149
+ with Progress(
150
+ SpinnerColumn(),
151
+ TextColumn("[progress.description]{task.description}"),
152
+ console=console,
153
+ ) as progress:
154
+ task = progress.add_task("Running checks...", total=len(checks))
155
+
156
+ for check_name, check_func in checks:
157
+ progress.update(task, description=f"Checking {check_name}...")
158
+
159
+ start_time = time.time()
160
+ try:
161
+ result = await check_func()
162
+ result.duration_ms = (time.time() - start_time) * 1000
163
+ self.report.add_check(result)
164
+ except Exception as e:
165
+ self.report.add_check(CheckResult(
166
+ name=check_name,
167
+ status=CheckStatus.FAIL,
168
+ message=f"Check failed with error: {str(e)}",
169
+ duration_ms=(time.time() - start_time) * 1000,
170
+ ))
171
+
172
+ progress.advance(task)
173
+
174
+ # Compute summary
175
+ self.report.compute_summary()
176
+
177
+ return self.report
178
+
179
+ async def _collect_system_info(self):
180
+ """Collect system information."""
181
+ import platform
182
+
183
+ self.report.system_info = {
184
+ "os": platform.system(),
185
+ "os_version": platform.version(),
186
+ "architecture": platform.machine(),
187
+ "python_version": platform.python_version(),
188
+ "hostname": platform.node(),
189
+ }
190
+
191
+ # Try to get package manager
192
+ for pm in ["brew", "apt", "dnf", "yum", "pacman"]:
193
+ if shutil.which(pm):
194
+ self.report.system_info["package_manager"] = pm
195
+ break
196
+
197
+ async def _check_python_version(self) -> CheckResult:
198
+ """Check Python version."""
199
+ version = sys.version_info
200
+ version_str = f"{version.major}.{version.minor}.{version.micro}"
201
+
202
+ if version.major >= 3 and version.minor >= 9:
203
+ return CheckResult(
204
+ name="Python Version",
205
+ status=CheckStatus.PASS,
206
+ message=f"Python {version_str} installed",
207
+ )
208
+ elif version.major >= 3 and version.minor >= 8:
209
+ return CheckResult(
210
+ name="Python Version",
211
+ status=CheckStatus.WARN,
212
+ message=f"Python {version_str} (3.9+ recommended)",
213
+ )
214
+ else:
215
+ return CheckResult(
216
+ name="Python Version",
217
+ status=CheckStatus.FAIL,
218
+ message=f"Python {version_str} (requires 3.9+)",
219
+ fix_command="Install Python 3.9 or higher",
220
+ )
221
+
222
+ async def _check_python_deps(self) -> CheckResult:
223
+ """Check required Python dependencies."""
224
+ required = [
225
+ "rich", "typer", "httpx", "pydantic", "litellm",
226
+ "sqlalchemy", "structlog", "fastapi",
227
+ ]
228
+
229
+ missing = []
230
+ for pkg in required:
231
+ try:
232
+ importlib.import_module(pkg)
233
+ except ImportError:
234
+ missing.append(pkg)
235
+
236
+ if not missing:
237
+ return CheckResult(
238
+ name="Python Dependencies",
239
+ status=CheckStatus.PASS,
240
+ message=f"All {len(required)} core dependencies installed",
241
+ )
242
+ else:
243
+ return CheckResult(
244
+ name="Python Dependencies",
245
+ status=CheckStatus.FAIL,
246
+ message=f"Missing: {', '.join(missing)}",
247
+ fix_command=f"pip install {' '.join(missing)}",
248
+ )
249
+
250
+ async def _check_aiptx_package(self) -> CheckResult:
251
+ """Check AIPTX package installation."""
252
+ try:
253
+ import aipt_v2
254
+ version = getattr(aipt_v2, "__version__", "unknown")
255
+ return CheckResult(
256
+ name="AIPTX Package",
257
+ status=CheckStatus.PASS,
258
+ message=f"AIPTX version {version} installed",
259
+ )
260
+ except ImportError:
261
+ return CheckResult(
262
+ name="AIPTX Package",
263
+ status=CheckStatus.FAIL,
264
+ message="AIPTX package not found",
265
+ fix_command="pip install aiptx",
266
+ )
267
+
268
+ async def _check_config_file(self) -> CheckResult:
269
+ """Check configuration file exists."""
270
+ config_paths = [
271
+ Path.home() / ".aiptx" / ".env",
272
+ Path(".env"),
273
+ ]
274
+
275
+ for path in config_paths:
276
+ if path.exists():
277
+ return CheckResult(
278
+ name="Configuration File",
279
+ status=CheckStatus.PASS,
280
+ message=f"Config found at {path}",
281
+ )
282
+
283
+ return CheckResult(
284
+ name="Configuration File",
285
+ status=CheckStatus.WARN,
286
+ message="No configuration file found",
287
+ details="Run 'aiptx setup' to configure",
288
+ fix_command="aiptx setup",
289
+ )
290
+
291
+ async def _check_llm_config(self) -> CheckResult:
292
+ """Check LLM configuration."""
293
+ llm_keys = [
294
+ "ANTHROPIC_API_KEY",
295
+ "OPENAI_API_KEY",
296
+ "DEEPSEEK_API_KEY",
297
+ "LLM_API_KEY",
298
+ ]
299
+
300
+ # Check for Ollama
301
+ if shutil.which("ollama"):
302
+ # Check if Ollama is running
303
+ try:
304
+ proc = await asyncio.create_subprocess_shell(
305
+ "curl -s http://localhost:11434/api/version",
306
+ stdout=asyncio.subprocess.PIPE,
307
+ stderr=asyncio.subprocess.PIPE,
308
+ )
309
+ stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=5)
310
+ if proc.returncode == 0 and b"version" in stdout:
311
+ return CheckResult(
312
+ name="LLM Configuration",
313
+ status=CheckStatus.PASS,
314
+ message="Ollama running locally",
315
+ )
316
+ except Exception:
317
+ pass
318
+
319
+ # Check for API keys
320
+ for key in llm_keys:
321
+ if os.environ.get(key):
322
+ return CheckResult(
323
+ name="LLM Configuration",
324
+ status=CheckStatus.PASS,
325
+ message=f"{key} configured",
326
+ )
327
+
328
+ # Check .env file
329
+ env_path = Path.home() / ".aiptx" / ".env"
330
+ if env_path.exists():
331
+ with open(env_path) as f:
332
+ content = f.read()
333
+ for key in llm_keys:
334
+ if key in content:
335
+ return CheckResult(
336
+ name="LLM Configuration",
337
+ status=CheckStatus.PASS,
338
+ message=f"{key} found in config",
339
+ )
340
+
341
+ return CheckResult(
342
+ name="LLM Configuration",
343
+ status=CheckStatus.FAIL,
344
+ message="No LLM API key or Ollama configured",
345
+ fix_command="aiptx setup",
346
+ )
347
+
348
+ async def _check_go(self) -> CheckResult:
349
+ """Check Go installation."""
350
+ if shutil.which("go"):
351
+ try:
352
+ proc = await asyncio.create_subprocess_shell(
353
+ "go version",
354
+ stdout=asyncio.subprocess.PIPE,
355
+ stderr=asyncio.subprocess.PIPE,
356
+ )
357
+ stdout, _ = await proc.communicate()
358
+ version = stdout.decode().strip()
359
+ return CheckResult(
360
+ name="Go Installation",
361
+ status=CheckStatus.PASS,
362
+ message=version.replace("go version ", ""),
363
+ )
364
+ except Exception:
365
+ pass
366
+
367
+ return CheckResult(
368
+ name="Go Installation",
369
+ status=CheckStatus.WARN,
370
+ message="Go not installed (required for some tools)",
371
+ fix_command="aiptx tools install -t go",
372
+ )
373
+
374
+ async def _check_docker(self) -> CheckResult:
375
+ """Check Docker installation (optional)."""
376
+ if shutil.which("docker"):
377
+ try:
378
+ proc = await asyncio.create_subprocess_shell(
379
+ "docker --version",
380
+ stdout=asyncio.subprocess.PIPE,
381
+ stderr=asyncio.subprocess.PIPE,
382
+ )
383
+ stdout, _ = await proc.communicate()
384
+ if proc.returncode == 0:
385
+ version = stdout.decode().strip()
386
+ return CheckResult(
387
+ name="Docker (optional)",
388
+ status=CheckStatus.PASS,
389
+ message=version,
390
+ )
391
+ except Exception:
392
+ pass
393
+
394
+ return CheckResult(
395
+ name="Docker (optional)",
396
+ status=CheckStatus.SKIP,
397
+ message="Docker not available (optional for sandboxing)",
398
+ )
399
+
400
+ async def _check_core_tools(self) -> CheckResult:
401
+ """Check core security tools."""
402
+ core_tools = ["nmap", "nuclei", "sqlmap", "ffuf", "httpx"]
403
+ installed = []
404
+ missing = []
405
+
406
+ for tool in core_tools:
407
+ if shutil.which(tool):
408
+ installed.append(tool)
409
+ else:
410
+ missing.append(tool)
411
+
412
+ self.report.tool_status.update({t: t in installed for t in core_tools})
413
+
414
+ if not missing:
415
+ return CheckResult(
416
+ name="Core Tools",
417
+ status=CheckStatus.PASS,
418
+ message=f"All {len(core_tools)} core tools installed",
419
+ )
420
+ elif len(missing) <= 2:
421
+ return CheckResult(
422
+ name="Core Tools",
423
+ status=CheckStatus.WARN,
424
+ message=f"Missing: {', '.join(missing)}",
425
+ fix_command=f"aiptx tools install -t {' '.join(missing)}",
426
+ )
427
+ else:
428
+ return CheckResult(
429
+ name="Core Tools",
430
+ status=CheckStatus.FAIL,
431
+ message=f"Missing {len(missing)}/{len(core_tools)} core tools",
432
+ fix_command="aiptx tools install --core",
433
+ )
434
+
435
+ async def _check_all_tools(self) -> CheckResult:
436
+ """Check all available security tools."""
437
+ try:
438
+ from aipt_v2.local_tool_installer import TOOLS
439
+
440
+ installed = 0
441
+ total = len(TOOLS)
442
+
443
+ for tool_name in TOOLS:
444
+ if shutil.which(tool_name):
445
+ installed += 1
446
+ self.report.tool_status[tool_name] = True
447
+ else:
448
+ self.report.tool_status[tool_name] = False
449
+
450
+ coverage = (installed / total * 100) if total > 0 else 0
451
+
452
+ if coverage >= 50:
453
+ return CheckResult(
454
+ name="All Security Tools",
455
+ status=CheckStatus.PASS,
456
+ message=f"{installed}/{total} tools installed ({coverage:.0f}%)",
457
+ )
458
+ elif coverage >= 25:
459
+ return CheckResult(
460
+ name="All Security Tools",
461
+ status=CheckStatus.WARN,
462
+ message=f"{installed}/{total} tools installed ({coverage:.0f}%)",
463
+ fix_command="aiptx tools install --all",
464
+ )
465
+ else:
466
+ return CheckResult(
467
+ name="All Security Tools",
468
+ status=CheckStatus.WARN,
469
+ message=f"Only {installed}/{total} tools installed",
470
+ fix_command="aiptx tools install --core",
471
+ )
472
+
473
+ except ImportError:
474
+ return CheckResult(
475
+ name="All Security Tools",
476
+ status=CheckStatus.SKIP,
477
+ message="Tool catalog not available",
478
+ )
479
+
480
+ async def _check_path(self) -> CheckResult:
481
+ """Check PATH configuration."""
482
+ path = os.environ.get("PATH", "")
483
+ home = str(Path.home())
484
+
485
+ required_paths = [
486
+ f"{home}/.local/bin",
487
+ f"{home}/go/bin",
488
+ ]
489
+
490
+ missing = [p for p in required_paths if p not in path]
491
+
492
+ if not missing:
493
+ return CheckResult(
494
+ name="Path Configuration",
495
+ status=CheckStatus.PASS,
496
+ message="PATH includes required directories",
497
+ )
498
+ else:
499
+ return CheckResult(
500
+ name="Path Configuration",
501
+ status=CheckStatus.WARN,
502
+ message=f"Missing from PATH: {', '.join(missing)}",
503
+ details="Add to your shell profile (.bashrc, .zshrc)",
504
+ fix_command=f"export PATH=$PATH:{':'.join(missing)}",
505
+ )
506
+
507
+ async def _check_permissions(self) -> CheckResult:
508
+ """Check file permissions."""
509
+ config_dir = Path.home() / ".aiptx"
510
+
511
+ if config_dir.exists():
512
+ # Check if .env has proper permissions
513
+ env_file = config_dir / ".env"
514
+ if env_file.exists():
515
+ mode = env_file.stat().st_mode & 0o777
516
+ if mode == 0o600:
517
+ return CheckResult(
518
+ name="Permissions",
519
+ status=CheckStatus.PASS,
520
+ message="Config file has secure permissions",
521
+ )
522
+ else:
523
+ return CheckResult(
524
+ name="Permissions",
525
+ status=CheckStatus.WARN,
526
+ message=f"Config file permissions: {oct(mode)} (should be 600)",
527
+ fix_command=f"chmod 600 {env_file}",
528
+ )
529
+
530
+ return CheckResult(
531
+ name="Permissions",
532
+ status=CheckStatus.SKIP,
533
+ message="No config files to check",
534
+ )
535
+
536
+ async def _check_network(self) -> CheckResult:
537
+ """Check network connectivity."""
538
+ try:
539
+ proc = await asyncio.create_subprocess_shell(
540
+ "curl -s --connect-timeout 5 -o /dev/null -w '%{http_code}' https://api.anthropic.com",
541
+ stdout=asyncio.subprocess.PIPE,
542
+ stderr=asyncio.subprocess.PIPE,
543
+ )
544
+ stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=10)
545
+ status_code = stdout.decode().strip()
546
+
547
+ if status_code in ["200", "401", "403"]:
548
+ return CheckResult(
549
+ name="Network Connectivity",
550
+ status=CheckStatus.PASS,
551
+ message="Internet connectivity OK",
552
+ )
553
+ else:
554
+ return CheckResult(
555
+ name="Network Connectivity",
556
+ status=CheckStatus.WARN,
557
+ message=f"API returned status {status_code}",
558
+ )
559
+
560
+ except asyncio.TimeoutError:
561
+ return CheckResult(
562
+ name="Network Connectivity",
563
+ status=CheckStatus.FAIL,
564
+ message="Connection timeout",
565
+ )
566
+ except Exception as e:
567
+ return CheckResult(
568
+ name="Network Connectivity",
569
+ status=CheckStatus.FAIL,
570
+ message=f"Network error: {str(e)}",
571
+ )
572
+
573
+ async def _check_llm_connectivity(self) -> CheckResult:
574
+ """Test LLM connectivity."""
575
+ # Check Ollama first
576
+ if shutil.which("ollama"):
577
+ try:
578
+ proc = await asyncio.create_subprocess_shell(
579
+ "curl -s http://localhost:11434/api/tags",
580
+ stdout=asyncio.subprocess.PIPE,
581
+ stderr=asyncio.subprocess.PIPE,
582
+ )
583
+ stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=5)
584
+ if proc.returncode == 0:
585
+ data = json.loads(stdout.decode())
586
+ models = len(data.get("models", []))
587
+ return CheckResult(
588
+ name="LLM Connectivity",
589
+ status=CheckStatus.PASS,
590
+ message=f"Ollama running with {models} models",
591
+ )
592
+ except Exception:
593
+ pass
594
+
595
+ # Skip API test if no key configured
596
+ api_keys = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY"]
597
+ has_key = any(os.environ.get(k) for k in api_keys)
598
+
599
+ if not has_key:
600
+ return CheckResult(
601
+ name="LLM Connectivity",
602
+ status=CheckStatus.SKIP,
603
+ message="No API key configured (using Ollama or not configured)",
604
+ )
605
+
606
+ return CheckResult(
607
+ name="LLM Connectivity",
608
+ status=CheckStatus.WARN,
609
+ message="API key configured but not tested",
610
+ )
611
+
612
+ def print_report(self):
613
+ """Print verification report to console."""
614
+ console.print()
615
+
616
+ # Results table
617
+ table = Table(title="Verification Results", box=box.ROUNDED)
618
+ table.add_column("Check", style="cyan")
619
+ table.add_column("Status", justify="center")
620
+ table.add_column("Message")
621
+ table.add_column("Time", justify="right", style="dim")
622
+
623
+ for check in self.report.checks:
624
+ if check.status == CheckStatus.PASS:
625
+ status = "[green]✓ PASS[/green]"
626
+ elif check.status == CheckStatus.WARN:
627
+ status = "[yellow]⚠ WARN[/yellow]"
628
+ elif check.status == CheckStatus.FAIL:
629
+ status = "[red]✗ FAIL[/red]"
630
+ else:
631
+ status = "[dim]○ SKIP[/dim]"
632
+
633
+ table.add_row(
634
+ check.name,
635
+ status,
636
+ check.message,
637
+ f"{check.duration_ms:.0f}ms"
638
+ )
639
+
640
+ console.print(table)
641
+
642
+ # Summary
643
+ s = self.report.summary
644
+ console.print()
645
+
646
+ if s["failed"] == 0:
647
+ console.print(Panel(
648
+ f"[bold green]✓ Verification Passed[/bold green]\n\n"
649
+ f"Passed: {s['passed']} Warnings: {s['warnings']} Skipped: {s['skipped']}",
650
+ title="Summary",
651
+ border_style="green"
652
+ ))
653
+ else:
654
+ console.print(Panel(
655
+ f"[bold red]✗ Verification Failed[/bold red]\n\n"
656
+ f"Passed: {s['passed']} Warnings: {s['warnings']} "
657
+ f"[red]Failed: {s['failed']}[/red] Skipped: {s['skipped']}",
658
+ title="Summary",
659
+ border_style="red"
660
+ ))
661
+
662
+ # Show fix commands
663
+ failed_checks = [c for c in self.report.checks if c.status == CheckStatus.FAIL and c.fix_command]
664
+ if failed_checks:
665
+ console.print("\n[bold]Suggested fixes:[/bold]")
666
+ for check in failed_checks:
667
+ console.print(f" [cyan]{check.name}:[/cyan] {check.fix_command}")
668
+
669
+ console.print()
670
+
671
+ def generate_markdown_report(self) -> str:
672
+ """Generate markdown report."""
673
+ lines = [
674
+ "# AIPTX Installation Verification Report",
675
+ "",
676
+ f"Generated: {self.report.timestamp.strftime('%Y-%m-%d %H:%M:%S')}",
677
+ "",
678
+ "## System Information",
679
+ "",
680
+ ]
681
+
682
+ for key, value in self.report.system_info.items():
683
+ lines.append(f"- **{key}**: {value}")
684
+
685
+ lines.extend([
686
+ "",
687
+ "## Verification Checks",
688
+ "",
689
+ "| Check | Status | Message |",
690
+ "|-------|--------|---------|",
691
+ ])
692
+
693
+ for check in self.report.checks:
694
+ status_emoji = {
695
+ CheckStatus.PASS: "✅",
696
+ CheckStatus.WARN: "⚠️",
697
+ CheckStatus.FAIL: "❌",
698
+ CheckStatus.SKIP: "⏭️",
699
+ }.get(check.status, "?")
700
+
701
+ lines.append(f"| {check.name} | {status_emoji} {check.status.value.upper()} | {check.message} |")
702
+
703
+ s = self.report.summary
704
+ lines.extend([
705
+ "",
706
+ "## Summary",
707
+ "",
708
+ f"- **Total Checks**: {s['total']}",
709
+ f"- **Passed**: {s['passed']}",
710
+ f"- **Warnings**: {s['warnings']}",
711
+ f"- **Failed**: {s['failed']}",
712
+ f"- **Skipped**: {s['skipped']}",
713
+ "",
714
+ ])
715
+
716
+ # Tool status
717
+ if self.report.tool_status:
718
+ installed = sum(1 for v in self.report.tool_status.values() if v)
719
+ total = len(self.report.tool_status)
720
+ lines.extend([
721
+ "## Security Tools",
722
+ "",
723
+ f"Installed: {installed}/{total} ({installed/total*100:.0f}%)",
724
+ "",
725
+ ])
726
+
727
+ return "\n".join(lines)
728
+
729
+
730
+ async def verify_installation(
731
+ quick: bool = False,
732
+ auto_fix: bool = False,
733
+ report_file: Optional[str] = None,
734
+ ) -> int:
735
+ """
736
+ Verify AIPTX installation.
737
+
738
+ Args:
739
+ quick: Run quick checks only
740
+ auto_fix: Auto-fix issues
741
+ report_file: Path to save markdown report
742
+
743
+ Returns:
744
+ Exit code (0 = healthy, 1 = issues found)
745
+ """
746
+ verifier = InstallVerifier(quick=quick, auto_fix=auto_fix)
747
+ report = await verifier.run_all_checks()
748
+
749
+ # Print results
750
+ verifier.print_report()
751
+
752
+ # Save report if requested
753
+ if report_file:
754
+ md_report = verifier.generate_markdown_report()
755
+ Path(report_file).write_text(md_report)
756
+ console.print(f"[dim]Report saved to: {report_file}[/dim]")
757
+
758
+ return 0 if report.is_healthy() else 1
759
+
760
+
761
+ def main():
762
+ """CLI entry point."""
763
+ import argparse
764
+
765
+ parser = argparse.ArgumentParser(description="Verify AIPTX installation")
766
+ parser.add_argument(
767
+ "--quick", "-q",
768
+ action="store_true",
769
+ help="Run quick checks only"
770
+ )
771
+ parser.add_argument(
772
+ "--fix",
773
+ action="store_true",
774
+ help="Attempt to auto-fix issues"
775
+ )
776
+ parser.add_argument(
777
+ "--report", "-r",
778
+ help="Save markdown report to file"
779
+ )
780
+
781
+ args = parser.parse_args()
782
+
783
+ exit_code = asyncio.run(verify_installation(
784
+ quick=args.quick,
785
+ auto_fix=args.fix,
786
+ report_file=args.report,
787
+ ))
788
+
789
+ sys.exit(exit_code)
790
+
791
+
792
+ if __name__ == "__main__":
793
+ main()