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,588 @@
1
+ """
2
+ Nessus Vulnerability Scanner Integration for AIPTX
3
+
4
+ Provides comprehensive vulnerability assessment through Nessus Professional/Expert API.
5
+ Supports host-based scanning, compliance checks, and credential scanning.
6
+
7
+ Environment Variables:
8
+ NESSUS_URL: Nessus server URL (default: https://localhost:8834)
9
+ NESSUS_ACCESS_KEY: API access key
10
+ NESSUS_SECRET_KEY: API secret key
11
+
12
+ Usage:
13
+ from aipt_v2.tools.scanners.nessus_tool import get_nessus, nessus_scan
14
+
15
+ nessus = get_nessus()
16
+ if nessus.connect():
17
+ scan_id = nessus.scan_host("192.168.1.1")
18
+ result = nessus.wait_for_scan(scan_id)
19
+ vulns = nessus.get_vulnerabilities(scan_id)
20
+ """
21
+
22
+ import json
23
+ import logging
24
+ import os
25
+ import time
26
+ from dataclasses import dataclass, field
27
+ from datetime import datetime, timezone
28
+ from enum import Enum
29
+ from typing import Any, Callable, Dict, List, Optional
30
+
31
+ import requests
32
+ from requests.packages.urllib3.exceptions import InsecureRequestWarning
33
+
34
+ # Suppress SSL warnings for self-signed certs
35
+ requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ # ==================== Enums ====================
41
+
42
+ class ScanStatus(Enum):
43
+ """Nessus scan status."""
44
+ PENDING = "pending"
45
+ RUNNING = "running"
46
+ COMPLETED = "completed"
47
+ CANCELED = "canceled"
48
+ PAUSED = "paused"
49
+ STOPPING = "stopping"
50
+ FAILED = "failed"
51
+
52
+
53
+ class Severity(Enum):
54
+ """Nessus severity levels (CVSS-based)."""
55
+ CRITICAL = 4 # CVSS >= 9.0
56
+ HIGH = 3 # CVSS 7.0-8.9
57
+ MEDIUM = 2 # CVSS 4.0-6.9
58
+ LOW = 1 # CVSS 0.1-3.9
59
+ INFO = 0 # Informational
60
+
61
+
62
+ class ScanTemplate(Enum):
63
+ """Nessus scan templates."""
64
+ BASIC_NETWORK = "basic"
65
+ ADVANCED = "advanced"
66
+ WEB_APP = "webapp"
67
+ MALWARE = "malware"
68
+ COMPLIANCE = "compliance"
69
+ DISCOVERY = "discovery"
70
+
71
+
72
+ # ==================== Data Classes ====================
73
+
74
+ @dataclass
75
+ class NessusConfig:
76
+ """Configuration for Nessus connection."""
77
+ base_url: str = field(default_factory=lambda: os.getenv("NESSUS_URL", "https://localhost:8834"))
78
+ access_key: str = field(default_factory=lambda: os.getenv("NESSUS_ACCESS_KEY", ""))
79
+ secret_key: str = field(default_factory=lambda: os.getenv("NESSUS_SECRET_KEY", ""))
80
+ verify_ssl: bool = False
81
+ timeout: int = 30
82
+
83
+
84
+ @dataclass
85
+ class ScanResult:
86
+ """Result of a Nessus scan."""
87
+ scan_id: str
88
+ status: str
89
+ progress: int = 0
90
+ hosts_total: int = 0
91
+ hosts_completed: int = 0
92
+ vulnerabilities_count: int = 0
93
+ start_time: str = ""
94
+ end_time: str = ""
95
+ error: str = ""
96
+
97
+
98
+ @dataclass
99
+ class Vulnerability:
100
+ """Nessus vulnerability finding."""
101
+ vuln_id: str
102
+ plugin_id: str
103
+ plugin_name: str
104
+ severity: int
105
+ severity_name: str
106
+ host: str
107
+ port: int
108
+ protocol: str
109
+ description: str = ""
110
+ solution: str = ""
111
+ cvss_score: float = 0.0
112
+ cvss_vector: str = ""
113
+ cve: List[str] = field(default_factory=list)
114
+ references: List[str] = field(default_factory=list)
115
+ plugin_output: str = ""
116
+
117
+
118
+ # ==================== Main Tool Class ====================
119
+
120
+ class NessusTool:
121
+ """
122
+ Nessus Vulnerability Scanner integration.
123
+
124
+ Provides methods for:
125
+ - Host/network vulnerability scanning
126
+ - Compliance scanning
127
+ - Credential-based scanning
128
+ - Vulnerability retrieval and export
129
+ """
130
+
131
+ def __init__(self, config: Optional[NessusConfig] = None):
132
+ """Initialize Nessus tool with configuration."""
133
+ self.config = config or NessusConfig()
134
+ self._session = requests.Session()
135
+ self._connected = False
136
+ self._headers = {
137
+ "X-ApiKeys": f"accessKey={self.config.access_key}; secretKey={self.config.secret_key}",
138
+ "Content-Type": "application/json",
139
+ "Accept": "application/json"
140
+ }
141
+ self._session.headers.update(self._headers)
142
+
143
+ def _request(self, method: str, endpoint: str, data: dict = None) -> dict:
144
+ """Make authenticated request to Nessus API."""
145
+ url = f"{self.config.base_url}{endpoint}"
146
+ try:
147
+ response = self._session.request(
148
+ method=method,
149
+ url=url,
150
+ json=data,
151
+ verify=self.config.verify_ssl,
152
+ timeout=self.config.timeout
153
+ )
154
+ response.raise_for_status()
155
+ return response.json() if response.text else {}
156
+ except requests.exceptions.RequestException as e:
157
+ logger.error(f"Nessus API error: {e}")
158
+ raise
159
+
160
+ # ==================== Connection ====================
161
+
162
+ def connect(self) -> bool:
163
+ """Test connection to Nessus server."""
164
+ try:
165
+ result = self._request("GET", "/server/status")
166
+ self._connected = result.get("status") == "ready"
167
+ if self._connected:
168
+ logger.info(f"Connected to Nessus at {self.config.base_url}")
169
+ return self._connected
170
+ except Exception as e:
171
+ logger.error(f"Failed to connect to Nessus: {e}")
172
+ self._connected = False
173
+ return False
174
+
175
+ def is_connected(self) -> bool:
176
+ """Check if connected to Nessus."""
177
+ return self._connected
178
+
179
+ def get_info(self) -> Dict:
180
+ """Get Nessus server information."""
181
+ try:
182
+ return self._request("GET", "/server/properties")
183
+ except Exception:
184
+ return {}
185
+
186
+ # ==================== Scan Management ====================
187
+
188
+ def list_scans(self, folder_id: int = None) -> List[Dict]:
189
+ """List all scans."""
190
+ endpoint = "/scans"
191
+ if folder_id:
192
+ endpoint += f"?folder_id={folder_id}"
193
+ result = self._request("GET", endpoint)
194
+ return result.get("scans", [])
195
+
196
+ def get_templates(self) -> List[Dict]:
197
+ """Get available scan templates."""
198
+ result = self._request("GET", "/editor/scan/templates")
199
+ return result.get("templates", [])
200
+
201
+ def create_scan(
202
+ self,
203
+ name: str,
204
+ targets: str,
205
+ template: str = "basic",
206
+ description: str = "",
207
+ credentials: dict = None
208
+ ) -> str:
209
+ """
210
+ Create a new scan.
211
+
212
+ Args:
213
+ name: Scan name
214
+ targets: Comma-separated list of targets (IPs, hostnames, ranges)
215
+ template: Scan template name
216
+ description: Scan description
217
+ credentials: Optional credentials for authenticated scanning
218
+
219
+ Returns:
220
+ Scan ID
221
+ """
222
+ # Get template UUID
223
+ templates = self.get_templates()
224
+ template_uuid = None
225
+ for t in templates:
226
+ if t.get("name", "").lower() == template.lower():
227
+ template_uuid = t.get("uuid")
228
+ break
229
+
230
+ if not template_uuid:
231
+ # Default to basic network scan
232
+ template_uuid = templates[0].get("uuid") if templates else None
233
+
234
+ settings = {
235
+ "name": name,
236
+ "description": description or f"AIPTX Scan - {datetime.now().isoformat()}",
237
+ "text_targets": targets,
238
+ "launch_now": False
239
+ }
240
+
241
+ data = {
242
+ "uuid": template_uuid,
243
+ "settings": settings
244
+ }
245
+
246
+ if credentials:
247
+ data["credentials"] = credentials
248
+
249
+ result = self._request("POST", "/scans", data)
250
+ scan_id = str(result.get("scan", {}).get("id", ""))
251
+ logger.info(f"Created Nessus scan: {scan_id} for {targets}")
252
+ return scan_id
253
+
254
+ def launch_scan(self, scan_id: str) -> bool:
255
+ """Launch an existing scan."""
256
+ try:
257
+ self._request("POST", f"/scans/{scan_id}/launch")
258
+ logger.info(f"Launched scan: {scan_id}")
259
+ return True
260
+ except Exception as e:
261
+ logger.error(f"Failed to launch scan {scan_id}: {e}")
262
+ return False
263
+
264
+ def scan_host(
265
+ self,
266
+ target: str,
267
+ template: str = "basic",
268
+ name: str = None,
269
+ credentials: dict = None
270
+ ) -> str:
271
+ """
272
+ Convenience method to create and launch a scan.
273
+
274
+ Args:
275
+ target: Target IP/hostname
276
+ template: Scan template
277
+ name: Optional scan name
278
+ credentials: Optional credentials
279
+
280
+ Returns:
281
+ Scan ID
282
+ """
283
+ name = name or f"AIPTX-{target}-{datetime.now().strftime('%Y%m%d%H%M%S')}"
284
+ scan_id = self.create_scan(name, target, template, credentials=credentials)
285
+ if scan_id:
286
+ self.launch_scan(scan_id)
287
+ return scan_id
288
+
289
+ def get_scan_status(self, scan_id: str) -> ScanResult:
290
+ """Get current scan status."""
291
+ try:
292
+ result = self._request("GET", f"/scans/{scan_id}")
293
+ info = result.get("info", {})
294
+
295
+ return ScanResult(
296
+ scan_id=scan_id,
297
+ status=info.get("status", "unknown"),
298
+ progress=info.get("scanner_progress", 0) or 0,
299
+ hosts_total=info.get("hostcount", 0) or 0,
300
+ hosts_completed=info.get("hosts_done", 0) or 0,
301
+ vulnerabilities_count=len(result.get("vulnerabilities", [])),
302
+ start_time=info.get("scan_start", ""),
303
+ end_time=info.get("scan_end", "")
304
+ )
305
+ except Exception as e:
306
+ return ScanResult(scan_id=scan_id, status="error", error=str(e))
307
+
308
+ def wait_for_scan(
309
+ self,
310
+ scan_id: str,
311
+ timeout: int = 3600,
312
+ poll_interval: int = 30,
313
+ callback: Callable[[ScanResult], None] = None
314
+ ) -> ScanResult:
315
+ """
316
+ Wait for scan to complete.
317
+
318
+ Args:
319
+ scan_id: Scan ID to monitor
320
+ timeout: Maximum wait time in seconds
321
+ poll_interval: Polling interval in seconds
322
+ callback: Optional progress callback
323
+
324
+ Returns:
325
+ Final scan result
326
+ """
327
+ start_time = time.time()
328
+ while time.time() - start_time < timeout:
329
+ result = self.get_scan_status(scan_id)
330
+
331
+ if callback:
332
+ callback(result)
333
+
334
+ if result.status in ["completed", "canceled", "failed"]:
335
+ return result
336
+
337
+ time.sleep(poll_interval)
338
+
339
+ return ScanResult(
340
+ scan_id=scan_id,
341
+ status="timeout",
342
+ error=f"Scan timed out after {timeout}s"
343
+ )
344
+
345
+ def stop_scan(self, scan_id: str) -> bool:
346
+ """Stop a running scan."""
347
+ try:
348
+ self._request("POST", f"/scans/{scan_id}/stop")
349
+ return True
350
+ except Exception:
351
+ return False
352
+
353
+ def pause_scan(self, scan_id: str) -> bool:
354
+ """Pause a running scan."""
355
+ try:
356
+ self._request("POST", f"/scans/{scan_id}/pause")
357
+ return True
358
+ except Exception:
359
+ return False
360
+
361
+ def resume_scan(self, scan_id: str) -> bool:
362
+ """Resume a paused scan."""
363
+ try:
364
+ self._request("POST", f"/scans/{scan_id}/resume")
365
+ return True
366
+ except Exception:
367
+ return False
368
+
369
+ def delete_scan(self, scan_id: str) -> bool:
370
+ """Delete a scan."""
371
+ try:
372
+ self._request("DELETE", f"/scans/{scan_id}")
373
+ return True
374
+ except Exception:
375
+ return False
376
+
377
+ # ==================== Vulnerability Management ====================
378
+
379
+ def get_vulnerabilities(self, scan_id: str, severity: int = None) -> List[Vulnerability]:
380
+ """
381
+ Get vulnerabilities from a scan.
382
+
383
+ Args:
384
+ scan_id: Scan ID
385
+ severity: Optional minimum severity filter (0-4)
386
+
387
+ Returns:
388
+ List of Vulnerability objects
389
+ """
390
+ try:
391
+ result = self._request("GET", f"/scans/{scan_id}")
392
+ vulns = []
393
+
394
+ for host in result.get("hosts", []):
395
+ host_id = host.get("host_id")
396
+ hostname = host.get("hostname", "unknown")
397
+
398
+ # Get host vulnerabilities
399
+ host_result = self._request("GET", f"/scans/{scan_id}/hosts/{host_id}")
400
+
401
+ for vuln in host_result.get("vulnerabilities", []):
402
+ sev = vuln.get("severity", 0)
403
+ if severity is not None and sev < severity:
404
+ continue
405
+
406
+ plugin_id = vuln.get("plugin_id")
407
+
408
+ # Get plugin details
409
+ try:
410
+ plugin_result = self._request(
411
+ "GET",
412
+ f"/scans/{scan_id}/hosts/{host_id}/plugins/{plugin_id}"
413
+ )
414
+ plugin_info = plugin_result.get("info", {}).get("plugindescription", {})
415
+ plugin_attrs = plugin_info.get("pluginattributes", {})
416
+ except Exception:
417
+ plugin_info = {}
418
+ plugin_attrs = {}
419
+
420
+ vulns.append(Vulnerability(
421
+ vuln_id=f"{scan_id}-{host_id}-{plugin_id}",
422
+ plugin_id=str(plugin_id),
423
+ plugin_name=vuln.get("plugin_name", "Unknown"),
424
+ severity=sev,
425
+ severity_name=self._severity_name(sev),
426
+ host=hostname,
427
+ port=vuln.get("port", 0),
428
+ protocol=vuln.get("protocol", "tcp"),
429
+ description=plugin_attrs.get("description", ""),
430
+ solution=plugin_attrs.get("solution", ""),
431
+ cvss_score=float(plugin_attrs.get("cvss_base_score", 0) or 0),
432
+ cvss_vector=plugin_attrs.get("cvss_vector", ""),
433
+ cve=plugin_attrs.get("cve", []) or [],
434
+ references=plugin_attrs.get("see_also", []) or [],
435
+ plugin_output=plugin_result.get("outputs", [{}])[0].get("plugin_output", "") if plugin_result.get("outputs") else ""
436
+ ))
437
+
438
+ return vulns
439
+ except Exception as e:
440
+ logger.error(f"Failed to get vulnerabilities: {e}")
441
+ return []
442
+
443
+ def _severity_name(self, severity: int) -> str:
444
+ """Convert severity int to name."""
445
+ names = {4: "critical", 3: "high", 2: "medium", 1: "low", 0: "info"}
446
+ return names.get(severity, "info")
447
+
448
+ # ==================== AIPTX Integration ====================
449
+
450
+ def to_finding(self, vuln: Vulnerability) -> Dict:
451
+ """Convert Nessus vulnerability to AIPTX finding format."""
452
+ return {
453
+ "type": "vulnerability",
454
+ "value": vuln.plugin_name,
455
+ "description": vuln.description or vuln.plugin_name,
456
+ "severity": vuln.severity_name,
457
+ "phase": "scan",
458
+ "tool": "nessus",
459
+ "target": f"{vuln.host}:{vuln.port}",
460
+ "metadata": {
461
+ "plugin_id": vuln.plugin_id,
462
+ "cvss_score": vuln.cvss_score,
463
+ "cve": vuln.cve,
464
+ "protocol": vuln.protocol,
465
+ "solution": vuln.solution
466
+ },
467
+ "timestamp": datetime.now(timezone.utc).isoformat()
468
+ }
469
+
470
+ def export_findings(self, scan_id: str, output_path: str = None) -> List[Dict]:
471
+ """
472
+ Export scan findings in AIPTX format.
473
+
474
+ Args:
475
+ scan_id: Scan ID
476
+ output_path: Optional file path to save JSON
477
+
478
+ Returns:
479
+ List of AIPTX findings
480
+ """
481
+ vulns = self.get_vulnerabilities(scan_id)
482
+ findings = [self.to_finding(v) for v in vulns]
483
+
484
+ if output_path:
485
+ with open(output_path, "w") as f:
486
+ json.dump(findings, f, indent=2)
487
+
488
+ return findings
489
+
490
+
491
+ # ==================== Global Instance & Helper Functions ====================
492
+
493
+ _nessus: Optional[NessusTool] = None
494
+
495
+
496
+ def get_nessus(config: Optional[NessusConfig] = None) -> NessusTool:
497
+ """Get or create global Nessus instance."""
498
+ global _nessus
499
+ if _nessus is None or config is not None:
500
+ _nessus = NessusTool(config)
501
+ return _nessus
502
+
503
+
504
+ def nessus_scan(target: str, template: str = "basic") -> ScanResult:
505
+ """Quick scan a target."""
506
+ nessus = get_nessus()
507
+ if not nessus.connect():
508
+ return ScanResult(scan_id="", status="error", error="Connection failed")
509
+
510
+ scan_id = nessus.scan_host(target, template)
511
+ return nessus.get_scan_status(scan_id)
512
+
513
+
514
+ def nessus_status(scan_id: str) -> ScanResult:
515
+ """Get scan status."""
516
+ return get_nessus().get_scan_status(scan_id)
517
+
518
+
519
+ def nessus_vulns(scan_id: str = None, severity: str = None) -> List[Dict]:
520
+ """Get vulnerabilities in AIPTX format."""
521
+ nessus = get_nessus()
522
+ sev_map = {"critical": 4, "high": 3, "medium": 2, "low": 1, "info": 0}
523
+ sev_int = sev_map.get(severity.lower()) if severity else None
524
+
525
+ if scan_id:
526
+ vulns = nessus.get_vulnerabilities(scan_id, sev_int)
527
+ else:
528
+ # Get from most recent scan
529
+ scans = nessus.list_scans()
530
+ if scans:
531
+ scan_id = str(scans[0].get("id"))
532
+ vulns = nessus.get_vulnerabilities(scan_id, sev_int)
533
+ else:
534
+ vulns = []
535
+
536
+ return [nessus.to_finding(v) for v in vulns]
537
+
538
+
539
+ def nessus_summary() -> Dict:
540
+ """Get Nessus scanner summary."""
541
+ nessus = get_nessus()
542
+ if not nessus.connect():
543
+ return {"connected": False, "error": "Connection failed"}
544
+
545
+ info = nessus.get_info()
546
+ scans = nessus.list_scans()
547
+
548
+ return {
549
+ "connected": True,
550
+ "url": nessus.config.base_url,
551
+ "version": info.get("nessus_ui_version", "unknown"),
552
+ "scans_count": len(scans)
553
+ }
554
+
555
+
556
+ # ==================== CLI Testing ====================
557
+
558
+ if __name__ == "__main__":
559
+ import sys
560
+
561
+ print("Nessus Integration Test")
562
+ print("=" * 50)
563
+
564
+ nessus = get_nessus()
565
+
566
+ if nessus.connect():
567
+ print(f"✓ Connected to Nessus at {nessus.config.base_url}")
568
+
569
+ info = nessus.get_info()
570
+ print(f" Version: {info.get('nessus_ui_version', 'unknown')}")
571
+
572
+ scans = nessus.list_scans()
573
+ print(f" Scans: {len(scans)}")
574
+
575
+ if len(sys.argv) > 1:
576
+ target = sys.argv[1]
577
+ print(f"\nScanning {target}...")
578
+ scan_id = nessus.scan_host(target)
579
+ print(f" Scan ID: {scan_id}")
580
+
581
+ result = nessus.wait_for_scan(scan_id, timeout=1800)
582
+ print(f" Status: {result.status}")
583
+
584
+ vulns = nessus.get_vulnerabilities(scan_id)
585
+ print(f" Vulnerabilities: {len(vulns)}")
586
+ else:
587
+ print("✗ Failed to connect to Nessus")
588
+ print(" Set NESSUS_URL, NESSUS_ACCESS_KEY, NESSUS_SECRET_KEY")