aiptx 2.0.2__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.

Potentially problematic release.


This version of aiptx might be problematic. Click here for more details.

Files changed (165) 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 +24 -0
  6. aipt_v2/agents/base.py +520 -0
  7. aipt_v2/agents/ptt.py +406 -0
  8. aipt_v2/agents/state.py +168 -0
  9. aipt_v2/app.py +960 -0
  10. aipt_v2/browser/__init__.py +31 -0
  11. aipt_v2/browser/automation.py +458 -0
  12. aipt_v2/browser/crawler.py +453 -0
  13. aipt_v2/cli.py +321 -0
  14. aipt_v2/compliance/__init__.py +71 -0
  15. aipt_v2/compliance/compliance_report.py +449 -0
  16. aipt_v2/compliance/framework_mapper.py +424 -0
  17. aipt_v2/compliance/nist_mapping.py +345 -0
  18. aipt_v2/compliance/owasp_mapping.py +330 -0
  19. aipt_v2/compliance/pci_mapping.py +297 -0
  20. aipt_v2/config.py +288 -0
  21. aipt_v2/core/__init__.py +43 -0
  22. aipt_v2/core/agent.py +630 -0
  23. aipt_v2/core/llm.py +395 -0
  24. aipt_v2/core/memory.py +305 -0
  25. aipt_v2/core/ptt.py +329 -0
  26. aipt_v2/database/__init__.py +14 -0
  27. aipt_v2/database/models.py +232 -0
  28. aipt_v2/database/repository.py +384 -0
  29. aipt_v2/docker/__init__.py +23 -0
  30. aipt_v2/docker/builder.py +260 -0
  31. aipt_v2/docker/manager.py +222 -0
  32. aipt_v2/docker/sandbox.py +371 -0
  33. aipt_v2/evasion/__init__.py +58 -0
  34. aipt_v2/evasion/request_obfuscator.py +272 -0
  35. aipt_v2/evasion/tls_fingerprint.py +285 -0
  36. aipt_v2/evasion/ua_rotator.py +301 -0
  37. aipt_v2/evasion/waf_bypass.py +439 -0
  38. aipt_v2/execution/__init__.py +23 -0
  39. aipt_v2/execution/executor.py +302 -0
  40. aipt_v2/execution/parser.py +544 -0
  41. aipt_v2/execution/terminal.py +337 -0
  42. aipt_v2/health.py +437 -0
  43. aipt_v2/intelligence/__init__.py +85 -0
  44. aipt_v2/intelligence/auth.py +520 -0
  45. aipt_v2/intelligence/chaining.py +775 -0
  46. aipt_v2/intelligence/cve_aipt.py +334 -0
  47. aipt_v2/intelligence/cve_info.py +1111 -0
  48. aipt_v2/intelligence/rag.py +239 -0
  49. aipt_v2/intelligence/scope.py +442 -0
  50. aipt_v2/intelligence/searchers/__init__.py +5 -0
  51. aipt_v2/intelligence/searchers/exploitdb_searcher.py +523 -0
  52. aipt_v2/intelligence/searchers/github_searcher.py +467 -0
  53. aipt_v2/intelligence/searchers/google_searcher.py +281 -0
  54. aipt_v2/intelligence/tools.json +443 -0
  55. aipt_v2/intelligence/triage.py +670 -0
  56. aipt_v2/interface/__init__.py +5 -0
  57. aipt_v2/interface/cli.py +230 -0
  58. aipt_v2/interface/main.py +501 -0
  59. aipt_v2/interface/tui.py +1276 -0
  60. aipt_v2/interface/utils.py +583 -0
  61. aipt_v2/llm/__init__.py +39 -0
  62. aipt_v2/llm/config.py +26 -0
  63. aipt_v2/llm/llm.py +514 -0
  64. aipt_v2/llm/memory.py +214 -0
  65. aipt_v2/llm/request_queue.py +89 -0
  66. aipt_v2/llm/utils.py +89 -0
  67. aipt_v2/models/__init__.py +15 -0
  68. aipt_v2/models/findings.py +295 -0
  69. aipt_v2/models/phase_result.py +224 -0
  70. aipt_v2/models/scan_config.py +207 -0
  71. aipt_v2/monitoring/grafana/dashboards/aipt-dashboard.json +355 -0
  72. aipt_v2/monitoring/grafana/dashboards/default.yml +17 -0
  73. aipt_v2/monitoring/grafana/datasources/prometheus.yml +17 -0
  74. aipt_v2/monitoring/prometheus.yml +60 -0
  75. aipt_v2/orchestration/__init__.py +52 -0
  76. aipt_v2/orchestration/pipeline.py +398 -0
  77. aipt_v2/orchestration/progress.py +300 -0
  78. aipt_v2/orchestration/scheduler.py +296 -0
  79. aipt_v2/orchestrator.py +2284 -0
  80. aipt_v2/payloads/__init__.py +27 -0
  81. aipt_v2/payloads/cmdi.py +150 -0
  82. aipt_v2/payloads/sqli.py +263 -0
  83. aipt_v2/payloads/ssrf.py +204 -0
  84. aipt_v2/payloads/templates.py +222 -0
  85. aipt_v2/payloads/traversal.py +166 -0
  86. aipt_v2/payloads/xss.py +204 -0
  87. aipt_v2/prompts/__init__.py +60 -0
  88. aipt_v2/proxy/__init__.py +29 -0
  89. aipt_v2/proxy/history.py +352 -0
  90. aipt_v2/proxy/interceptor.py +452 -0
  91. aipt_v2/recon/__init__.py +44 -0
  92. aipt_v2/recon/dns.py +241 -0
  93. aipt_v2/recon/osint.py +367 -0
  94. aipt_v2/recon/subdomain.py +372 -0
  95. aipt_v2/recon/tech_detect.py +311 -0
  96. aipt_v2/reports/__init__.py +17 -0
  97. aipt_v2/reports/generator.py +313 -0
  98. aipt_v2/reports/html_report.py +378 -0
  99. aipt_v2/runtime/__init__.py +44 -0
  100. aipt_v2/runtime/base.py +30 -0
  101. aipt_v2/runtime/docker.py +401 -0
  102. aipt_v2/runtime/local.py +346 -0
  103. aipt_v2/runtime/tool_server.py +205 -0
  104. aipt_v2/scanners/__init__.py +28 -0
  105. aipt_v2/scanners/base.py +273 -0
  106. aipt_v2/scanners/nikto.py +244 -0
  107. aipt_v2/scanners/nmap.py +402 -0
  108. aipt_v2/scanners/nuclei.py +273 -0
  109. aipt_v2/scanners/web.py +454 -0
  110. aipt_v2/scripts/security_audit.py +366 -0
  111. aipt_v2/telemetry/__init__.py +7 -0
  112. aipt_v2/telemetry/tracer.py +347 -0
  113. aipt_v2/terminal/__init__.py +28 -0
  114. aipt_v2/terminal/executor.py +400 -0
  115. aipt_v2/terminal/sandbox.py +350 -0
  116. aipt_v2/tools/__init__.py +44 -0
  117. aipt_v2/tools/active_directory/__init__.py +78 -0
  118. aipt_v2/tools/active_directory/ad_config.py +238 -0
  119. aipt_v2/tools/active_directory/bloodhound_wrapper.py +447 -0
  120. aipt_v2/tools/active_directory/kerberos_attacks.py +430 -0
  121. aipt_v2/tools/active_directory/ldap_enum.py +533 -0
  122. aipt_v2/tools/active_directory/smb_attacks.py +505 -0
  123. aipt_v2/tools/agents_graph/__init__.py +19 -0
  124. aipt_v2/tools/agents_graph/agents_graph_actions.py +69 -0
  125. aipt_v2/tools/api_security/__init__.py +76 -0
  126. aipt_v2/tools/api_security/api_discovery.py +608 -0
  127. aipt_v2/tools/api_security/graphql_scanner.py +622 -0
  128. aipt_v2/tools/api_security/jwt_analyzer.py +577 -0
  129. aipt_v2/tools/api_security/openapi_fuzzer.py +761 -0
  130. aipt_v2/tools/browser/__init__.py +5 -0
  131. aipt_v2/tools/browser/browser_actions.py +238 -0
  132. aipt_v2/tools/browser/browser_instance.py +535 -0
  133. aipt_v2/tools/browser/tab_manager.py +344 -0
  134. aipt_v2/tools/cloud/__init__.py +70 -0
  135. aipt_v2/tools/cloud/cloud_config.py +273 -0
  136. aipt_v2/tools/cloud/cloud_scanner.py +639 -0
  137. aipt_v2/tools/cloud/prowler_tool.py +571 -0
  138. aipt_v2/tools/cloud/scoutsuite_tool.py +359 -0
  139. aipt_v2/tools/executor.py +307 -0
  140. aipt_v2/tools/parser.py +408 -0
  141. aipt_v2/tools/proxy/__init__.py +5 -0
  142. aipt_v2/tools/proxy/proxy_actions.py +103 -0
  143. aipt_v2/tools/proxy/proxy_manager.py +789 -0
  144. aipt_v2/tools/registry.py +196 -0
  145. aipt_v2/tools/scanners/__init__.py +343 -0
  146. aipt_v2/tools/scanners/acunetix_tool.py +712 -0
  147. aipt_v2/tools/scanners/burp_tool.py +631 -0
  148. aipt_v2/tools/scanners/config.py +156 -0
  149. aipt_v2/tools/scanners/nessus_tool.py +588 -0
  150. aipt_v2/tools/scanners/zap_tool.py +612 -0
  151. aipt_v2/tools/terminal/__init__.py +5 -0
  152. aipt_v2/tools/terminal/terminal_actions.py +37 -0
  153. aipt_v2/tools/terminal/terminal_manager.py +153 -0
  154. aipt_v2/tools/terminal/terminal_session.py +449 -0
  155. aipt_v2/tools/tool_processing.py +108 -0
  156. aipt_v2/utils/__init__.py +17 -0
  157. aipt_v2/utils/logging.py +201 -0
  158. aipt_v2/utils/model_manager.py +187 -0
  159. aipt_v2/utils/searchers/__init__.py +269 -0
  160. aiptx-2.0.2.dist-info/METADATA +324 -0
  161. aiptx-2.0.2.dist-info/RECORD +165 -0
  162. aiptx-2.0.2.dist-info/WHEEL +5 -0
  163. aiptx-2.0.2.dist-info/entry_points.txt +7 -0
  164. aiptx-2.0.2.dist-info/licenses/LICENSE +21 -0
  165. aiptx-2.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,273 @@
1
+ """
2
+ AIPT Base Scanner
3
+
4
+ Abstract base class for all scanner integrations.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import logging
10
+ import shutil
11
+ from abc import ABC, abstractmethod
12
+ from dataclasses import dataclass, field
13
+ from datetime import datetime
14
+ from enum import Enum
15
+ from typing import Any, AsyncIterator, Optional
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ScanSeverity(Enum):
21
+ """Vulnerability severity levels"""
22
+ INFO = "info"
23
+ LOW = "low"
24
+ MEDIUM = "medium"
25
+ HIGH = "high"
26
+ CRITICAL = "critical"
27
+
28
+
29
+ @dataclass
30
+ class ScanFinding:
31
+ """Individual scan finding"""
32
+ title: str
33
+ severity: ScanSeverity
34
+ description: str = ""
35
+ url: str = ""
36
+ host: str = ""
37
+ port: int = 0
38
+
39
+ # Details
40
+ evidence: str = ""
41
+ request: str = ""
42
+ response: str = ""
43
+
44
+ # Classification
45
+ cve: Optional[str] = None
46
+ cwe: Optional[str] = None
47
+ cvss: Optional[float] = None
48
+
49
+ # Scanner metadata
50
+ scanner: str = ""
51
+ template: str = ""
52
+ tags: list[str] = field(default_factory=list)
53
+
54
+ # Timestamps
55
+ found_at: datetime = field(default_factory=datetime.utcnow)
56
+
57
+ def to_dict(self) -> dict:
58
+ return {
59
+ "title": self.title,
60
+ "severity": self.severity.value,
61
+ "description": self.description,
62
+ "url": self.url,
63
+ "host": self.host,
64
+ "port": self.port,
65
+ "evidence": self.evidence[:500] if self.evidence else "",
66
+ "cve": self.cve,
67
+ "cwe": self.cwe,
68
+ "cvss": self.cvss,
69
+ "scanner": self.scanner,
70
+ "template": self.template,
71
+ "tags": self.tags,
72
+ "found_at": self.found_at.isoformat(),
73
+ }
74
+
75
+
76
+ @dataclass
77
+ class ScanResult:
78
+ """Complete scan result"""
79
+ scanner: str
80
+ target: str
81
+ status: str = "pending" # pending, running, completed, failed
82
+ findings: list[ScanFinding] = field(default_factory=list)
83
+
84
+ # Timing
85
+ start_time: Optional[datetime] = None
86
+ end_time: Optional[datetime] = None
87
+ duration_seconds: float = 0.0
88
+
89
+ # Statistics
90
+ requests_made: int = 0
91
+ errors: list[str] = field(default_factory=list)
92
+
93
+ # Raw output
94
+ raw_output: str = ""
95
+
96
+ def add_finding(self, finding: ScanFinding) -> None:
97
+ """Add a finding"""
98
+ finding.scanner = self.scanner
99
+ self.findings.append(finding)
100
+
101
+ def get_findings_by_severity(self, severity: ScanSeverity) -> list[ScanFinding]:
102
+ """Get findings of a specific severity"""
103
+ return [f for f in self.findings if f.severity == severity]
104
+
105
+ def get_critical_and_high(self) -> list[ScanFinding]:
106
+ """Get critical and high severity findings"""
107
+ return [
108
+ f for f in self.findings
109
+ if f.severity in [ScanSeverity.CRITICAL, ScanSeverity.HIGH]
110
+ ]
111
+
112
+ def severity_counts(self) -> dict[str, int]:
113
+ """Get count by severity"""
114
+ counts = {s.value: 0 for s in ScanSeverity}
115
+ for finding in self.findings:
116
+ counts[finding.severity.value] += 1
117
+ return counts
118
+
119
+ def to_dict(self) -> dict:
120
+ return {
121
+ "scanner": self.scanner,
122
+ "target": self.target,
123
+ "status": self.status,
124
+ "findings_count": len(self.findings),
125
+ "severity_counts": self.severity_counts(),
126
+ "duration_seconds": self.duration_seconds,
127
+ "requests_made": self.requests_made,
128
+ "errors": self.errors,
129
+ "start_time": self.start_time.isoformat() if self.start_time else None,
130
+ "end_time": self.end_time.isoformat() if self.end_time else None,
131
+ }
132
+
133
+
134
+ class BaseScanner(ABC):
135
+ """
136
+ Abstract base class for scanner integrations.
137
+
138
+ Subclasses must implement:
139
+ - scan(): Perform the scan
140
+ - is_available(): Check if scanner is installed
141
+ - parse_output(): Parse scanner output
142
+
143
+ Example:
144
+ class MyScanner(BaseScanner):
145
+ async def scan(self, target):
146
+ result = ScanResult(scanner="my_scanner", target=target)
147
+ # Run scan...
148
+ return result
149
+ """
150
+
151
+ def __init__(self):
152
+ self._running = False
153
+ self._process: Optional[asyncio.subprocess.Process] = None
154
+
155
+ @abstractmethod
156
+ async def scan(self, target: str, **kwargs) -> ScanResult:
157
+ """Perform scan on target"""
158
+ pass
159
+
160
+ @abstractmethod
161
+ def is_available(self) -> bool:
162
+ """Check if scanner is available/installed"""
163
+ pass
164
+
165
+ @abstractmethod
166
+ def parse_output(self, output: str) -> list[ScanFinding]:
167
+ """Parse scanner output into findings"""
168
+ pass
169
+
170
+ async def stream_scan(self, target: str, **kwargs) -> AsyncIterator[str]:
171
+ """Stream scan output (if supported)"""
172
+ yield "Streaming not implemented for this scanner"
173
+
174
+ async def stop(self) -> bool:
175
+ """Stop running scan"""
176
+ if self._process:
177
+ try:
178
+ self._process.terminate()
179
+ await asyncio.wait_for(self._process.wait(), timeout=5.0)
180
+ return True
181
+ except asyncio.TimeoutError:
182
+ self._process.kill()
183
+ return True
184
+ except Exception:
185
+ return False
186
+ return True
187
+
188
+ def _check_tool(self, tool_name: str) -> bool:
189
+ """Check if a tool is available in PATH"""
190
+ return shutil.which(tool_name) is not None
191
+
192
+ async def _run_command(
193
+ self,
194
+ command: list[str],
195
+ timeout: float = 300.0,
196
+ ) -> tuple[int, str, str]:
197
+ """
198
+ Run a command and return exit code, stdout, stderr.
199
+
200
+ Args:
201
+ command: Command and arguments
202
+ timeout: Timeout in seconds
203
+
204
+ Returns:
205
+ Tuple of (exit_code, stdout, stderr)
206
+ """
207
+ try:
208
+ self._running = True
209
+ self._process = await asyncio.create_subprocess_exec(
210
+ *command,
211
+ stdout=asyncio.subprocess.PIPE,
212
+ stderr=asyncio.subprocess.PIPE,
213
+ )
214
+
215
+ try:
216
+ stdout, stderr = await asyncio.wait_for(
217
+ self._process.communicate(),
218
+ timeout=timeout,
219
+ )
220
+ return (
221
+ self._process.returncode or 0,
222
+ stdout.decode("utf-8", errors="replace"),
223
+ stderr.decode("utf-8", errors="replace"),
224
+ )
225
+ except asyncio.TimeoutError:
226
+ self._process.kill()
227
+ await self._process.wait()
228
+ return -1, "", "Command timed out"
229
+
230
+ except FileNotFoundError:
231
+ return -1, "", f"Command not found: {command[0]}"
232
+ except Exception as e:
233
+ return -1, "", str(e)
234
+ finally:
235
+ self._running = False
236
+ self._process = None
237
+
238
+ async def _stream_command(
239
+ self,
240
+ command: list[str],
241
+ timeout: float = 300.0,
242
+ ) -> AsyncIterator[str]:
243
+ """Stream command output line by line"""
244
+ try:
245
+ self._running = True
246
+ self._process = await asyncio.create_subprocess_exec(
247
+ *command,
248
+ stdout=asyncio.subprocess.PIPE,
249
+ stderr=asyncio.subprocess.STDOUT,
250
+ )
251
+
252
+ start_time = asyncio.get_event_loop().time()
253
+
254
+ async for line in self._process.stdout:
255
+ # Check timeout
256
+ if asyncio.get_event_loop().time() - start_time > timeout:
257
+ self._process.kill()
258
+ yield "[TIMEOUT]"
259
+ break
260
+
261
+ yield line.decode("utf-8", errors="replace").rstrip()
262
+
263
+ await self._process.wait()
264
+
265
+ except Exception as e:
266
+ yield f"[ERROR] {str(e)}"
267
+ finally:
268
+ self._running = False
269
+ self._process = None
270
+
271
+ @property
272
+ def is_running(self) -> bool:
273
+ return self._running
@@ -0,0 +1,244 @@
1
+ """
2
+ AIPT Nikto Scanner Integration
3
+
4
+ Web server vulnerability scanning using Nikto.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import logging
9
+ import re
10
+ from dataclasses import dataclass, field
11
+ from datetime import datetime
12
+ from typing import Optional
13
+
14
+ from .base import BaseScanner, ScanFinding, ScanResult, ScanSeverity
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class NiktoScanner(BaseScanner):
20
+ """
21
+ Nikto web server scanner integration.
22
+
23
+ Nikto scans for:
24
+ - Dangerous files/programs
25
+ - Outdated server versions
26
+ - Version-specific vulnerabilities
27
+ - Server configuration issues
28
+ - Default files
29
+
30
+ Example:
31
+ scanner = NiktoScanner()
32
+ result = await scanner.scan("https://target.com")
33
+
34
+ for finding in result.findings:
35
+ print(f"{finding.severity}: {finding.title}")
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ timeout: int = 10,
41
+ tuning: str = "", # Scan tuning: 1-9,a,b,c,x
42
+ plugins: list[str] = None,
43
+ no_ssl: bool = False,
44
+ ):
45
+ super().__init__()
46
+ self.timeout = timeout
47
+ self.tuning = tuning
48
+ self.plugins = plugins or []
49
+ self.no_ssl = no_ssl
50
+
51
+ def is_available(self) -> bool:
52
+ """Check if Nikto is installed"""
53
+ return self._check_tool("nikto")
54
+
55
+ async def scan(self, target: str, **kwargs) -> ScanResult:
56
+ """
57
+ Run Nikto scan on target.
58
+
59
+ Args:
60
+ target: URL to scan
61
+ **kwargs: Additional options
62
+
63
+ Returns:
64
+ ScanResult with findings
65
+ """
66
+ result = ScanResult(scanner="nikto", target=target)
67
+ result.start_time = datetime.utcnow()
68
+ result.status = "running"
69
+
70
+ if not self.is_available():
71
+ result.status = "failed"
72
+ result.errors.append("Nikto is not installed")
73
+ return result
74
+
75
+ # Build command
76
+ command = self._build_command(target, **kwargs)
77
+ logger.info(f"Running Nikto: {' '.join(command)}")
78
+
79
+ # Execute
80
+ exit_code, stdout, stderr = await self._run_command(
81
+ command,
82
+ timeout=kwargs.get("timeout", 1200.0), # 20 min default
83
+ )
84
+
85
+ result.end_time = datetime.utcnow()
86
+ result.duration_seconds = (result.end_time - result.start_time).total_seconds()
87
+ result.raw_output = stdout
88
+
89
+ if exit_code != 0 and "0 error(s)" not in stdout:
90
+ if "OSVDB" in stdout or "vulnerability" in stdout.lower():
91
+ # Nikto found something, not a failure
92
+ result.status = "completed"
93
+ else:
94
+ result.status = "failed"
95
+ result.errors.append(stderr)
96
+ else:
97
+ result.status = "completed"
98
+
99
+ # Parse output
100
+ result.findings = self.parse_output(stdout)
101
+
102
+ logger.info(
103
+ f"Nikto scan complete: {len(result.findings)} findings in {result.duration_seconds:.1f}s"
104
+ )
105
+
106
+ return result
107
+
108
+ def parse_output(self, output: str) -> list[ScanFinding]:
109
+ """Parse Nikto output"""
110
+ findings = []
111
+
112
+ # Pattern for Nikto findings
113
+ # Format: + OSVDB-XXXX: /path: Description
114
+ # Or: + /path: Description
115
+ finding_pattern = r"\+ (?:OSVDB-(\d+): )?([^:]+): (.+)"
116
+
117
+ for line in output.split("\n"):
118
+ line = line.strip()
119
+ if not line.startswith("+"):
120
+ continue
121
+
122
+ match = re.match(finding_pattern, line)
123
+ if match:
124
+ osvdb, path, description = match.groups()
125
+
126
+ # Determine severity
127
+ severity = self._determine_severity(description, path)
128
+
129
+ finding = ScanFinding(
130
+ title=self._clean_title(description),
131
+ severity=severity,
132
+ description=description,
133
+ url=path,
134
+ scanner="nikto",
135
+ )
136
+
137
+ if osvdb:
138
+ finding.template = f"OSVDB-{osvdb}"
139
+ finding.evidence = f"OSVDB-{osvdb}"
140
+
141
+ findings.append(finding)
142
+
143
+ return findings
144
+
145
+ def _determine_severity(self, description: str, path: str) -> ScanSeverity:
146
+ """Determine severity from finding description"""
147
+ desc_lower = description.lower()
148
+ path_lower = path.lower()
149
+
150
+ # Critical
151
+ if any(kw in desc_lower for kw in ["rce", "remote code execution", "backdoor", "shell"]):
152
+ return ScanSeverity.CRITICAL
153
+
154
+ # High
155
+ if any(kw in desc_lower for kw in [
156
+ "sql injection", "sqli",
157
+ "command injection",
158
+ "file inclusion", "lfi", "rfi",
159
+ "authentication bypass",
160
+ "default password",
161
+ "admin access",
162
+ ]):
163
+ return ScanSeverity.HIGH
164
+
165
+ # Medium
166
+ if any(kw in desc_lower for kw in [
167
+ "xss", "cross-site",
168
+ "information disclosure",
169
+ "directory listing",
170
+ "source code",
171
+ "backup file",
172
+ "config file",
173
+ "outdated",
174
+ ]):
175
+ return ScanSeverity.MEDIUM
176
+
177
+ # Path-based severity
178
+ if any(p in path_lower for p in [
179
+ "/admin", "/manager", "/phpmyadmin",
180
+ ".bak", ".old", ".sql", ".zip",
181
+ "phpinfo", "test.php",
182
+ ]):
183
+ return ScanSeverity.MEDIUM
184
+
185
+ # Low
186
+ if any(kw in desc_lower for kw in [
187
+ "cookie", "header",
188
+ "version", "banner",
189
+ "allowed method",
190
+ ]):
191
+ return ScanSeverity.LOW
192
+
193
+ return ScanSeverity.INFO
194
+
195
+ def _clean_title(self, description: str) -> str:
196
+ """Create clean title from description"""
197
+ # Truncate long descriptions
198
+ if len(description) > 100:
199
+ return description[:97] + "..."
200
+ return description
201
+
202
+ def _build_command(self, target: str, **kwargs) -> list[str]:
203
+ """Build Nikto command"""
204
+ command = ["nikto", "-h", target]
205
+
206
+ # Timeout
207
+ command.extend(["-timeout", str(self.timeout)])
208
+
209
+ # Output format
210
+ command.extend(["-Format", "txt"])
211
+
212
+ # Tuning
213
+ if self.tuning:
214
+ command.extend(["-Tuning", self.tuning])
215
+
216
+ # Plugins
217
+ if self.plugins:
218
+ command.extend(["-Plugins", ",".join(self.plugins)])
219
+
220
+ # SSL
221
+ if self.no_ssl:
222
+ command.append("-nossl")
223
+
224
+ # Disable interactive mode
225
+ command.append("-ask")
226
+ command.append("no")
227
+
228
+ return command
229
+
230
+
231
+ # Convenience functions
232
+ async def quick_nikto_scan(target: str) -> ScanResult:
233
+ """Quick Nikto scan"""
234
+ scanner = NiktoScanner(timeout=5)
235
+ return await scanner.scan(target, timeout=300.0)
236
+
237
+
238
+ async def full_nikto_scan(target: str) -> ScanResult:
239
+ """Comprehensive Nikto scan"""
240
+ scanner = NiktoScanner(
241
+ timeout=15,
242
+ tuning="123456789abc", # All checks
243
+ )
244
+ return await scanner.scan(target, timeout=3600.0)