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
|
@@ -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")
|