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,372 @@
1
+ """
2
+ AIPT Subdomain Enumeration
3
+
4
+ Subdomain discovery using multiple sources and tools.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import logging
10
+ import re
11
+ import socket
12
+ from dataclasses import dataclass, field
13
+ from datetime import datetime
14
+ from typing import Optional, Set
15
+
16
+ import httpx
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ @dataclass
22
+ class Subdomain:
23
+ """Discovered subdomain"""
24
+ domain: str
25
+ ip: str = ""
26
+ status: str = "unknown" # alive, dead, unknown
27
+ http_status: int = 0
28
+ https_status: int = 0
29
+ title: str = ""
30
+ source: str = ""
31
+ discovered_at: datetime = field(default_factory=datetime.utcnow)
32
+
33
+ def to_dict(self) -> dict:
34
+ return {
35
+ "domain": self.domain,
36
+ "ip": self.ip,
37
+ "status": self.status,
38
+ "http_status": self.http_status,
39
+ "https_status": self.https_status,
40
+ "title": self.title,
41
+ "source": self.source,
42
+ }
43
+
44
+
45
+ @dataclass
46
+ class SubdomainConfig:
47
+ """Subdomain enumeration configuration"""
48
+ # Tools to use
49
+ use_subfinder: bool = True
50
+ use_amass: bool = False
51
+ use_crtsh: bool = True
52
+ use_dns_bruteforce: bool = False
53
+
54
+ # Verification
55
+ resolve_dns: bool = True
56
+ check_http: bool = True
57
+ timeout: float = 5.0
58
+ concurrent_requests: int = 50
59
+
60
+ # Wordlist for bruteforce
61
+ wordlist: list[str] = field(default_factory=lambda: [
62
+ "www", "mail", "ftp", "localhost", "webmail", "smtp", "pop", "ns1", "ns2",
63
+ "dns", "dns1", "dns2", "vpn", "gateway", "router", "admin", "administrator",
64
+ "api", "app", "apps", "dev", "development", "staging", "test", "testing",
65
+ "prod", "production", "web", "portal", "secure", "ssl", "cdn", "static",
66
+ "assets", "img", "images", "media", "files", "download", "downloads",
67
+ "blog", "forum", "shop", "store", "support", "help", "docs", "wiki",
68
+ "git", "svn", "repo", "repository", "jenkins", "ci", "build",
69
+ "monitor", "status", "health", "metrics", "grafana", "kibana",
70
+ "db", "database", "mysql", "postgres", "redis", "elastic", "elasticsearch",
71
+ "auth", "login", "sso", "oauth", "identity",
72
+ ])
73
+
74
+
75
+ @dataclass
76
+ class SubdomainResult:
77
+ """Subdomain enumeration results"""
78
+ target: str
79
+ subdomains: list[Subdomain] = field(default_factory=list)
80
+ alive_count: int = 0
81
+ total_found: int = 0
82
+ sources_used: list[str] = field(default_factory=list)
83
+ start_time: Optional[datetime] = None
84
+ end_time: Optional[datetime] = None
85
+ duration_seconds: float = 0.0
86
+
87
+ def get_alive(self) -> list[Subdomain]:
88
+ """Get only alive subdomains"""
89
+ return [s for s in self.subdomains if s.status == "alive"]
90
+
91
+ def get_by_source(self, source: str) -> list[Subdomain]:
92
+ """Get subdomains from specific source"""
93
+ return [s for s in self.subdomains if s.source == source]
94
+
95
+ def to_dict(self) -> dict:
96
+ return {
97
+ "target": self.target,
98
+ "total_found": self.total_found,
99
+ "alive_count": self.alive_count,
100
+ "sources_used": self.sources_used,
101
+ "duration_seconds": self.duration_seconds,
102
+ "subdomains": [s.to_dict() for s in self.subdomains],
103
+ }
104
+
105
+
106
+ class SubdomainEnumerator:
107
+ """
108
+ Subdomain enumeration from multiple sources.
109
+
110
+ Sources:
111
+ - Subfinder (if installed)
112
+ - Amass (if installed)
113
+ - crt.sh (Certificate Transparency)
114
+ - DNS bruteforce (optional)
115
+
116
+ Example:
117
+ enumerator = SubdomainEnumerator(SubdomainConfig(
118
+ use_crtsh=True,
119
+ check_http=True,
120
+ ))
121
+ result = await enumerator.enumerate("example.com")
122
+
123
+ for subdomain in result.get_alive():
124
+ print(f"{subdomain.domain} -> {subdomain.ip}")
125
+ """
126
+
127
+ def __init__(self, config: Optional[SubdomainConfig] = None):
128
+ self.config = config or SubdomainConfig()
129
+ self._found: Set[str] = set()
130
+
131
+ async def enumerate(self, domain: str) -> SubdomainResult:
132
+ """
133
+ Enumerate subdomains for a domain.
134
+
135
+ Args:
136
+ domain: Target domain (e.g., example.com)
137
+
138
+ Returns:
139
+ SubdomainResult with discovered subdomains
140
+ """
141
+ result = SubdomainResult(target=domain)
142
+ result.start_time = datetime.utcnow()
143
+ self._found.clear()
144
+
145
+ # Collect from all sources
146
+ tasks = []
147
+
148
+ if self.config.use_crtsh:
149
+ tasks.append(self._from_crtsh(domain))
150
+ result.sources_used.append("crt.sh")
151
+
152
+ if self.config.use_subfinder and self._tool_available("subfinder"):
153
+ tasks.append(self._from_subfinder(domain))
154
+ result.sources_used.append("subfinder")
155
+
156
+ if self.config.use_amass and self._tool_available("amass"):
157
+ tasks.append(self._from_amass(domain))
158
+ result.sources_used.append("amass")
159
+
160
+ if self.config.use_dns_bruteforce:
161
+ tasks.append(self._dns_bruteforce(domain))
162
+ result.sources_used.append("dns_bruteforce")
163
+
164
+ # Gather results
165
+ results = await asyncio.gather(*tasks, return_exceptions=True)
166
+
167
+ for source_result in results:
168
+ if isinstance(source_result, list):
169
+ for subdomain in source_result:
170
+ if subdomain.domain not in self._found:
171
+ self._found.add(subdomain.domain)
172
+ result.subdomains.append(subdomain)
173
+
174
+ result.total_found = len(result.subdomains)
175
+
176
+ # Verify subdomains
177
+ if self.config.resolve_dns or self.config.check_http:
178
+ await self._verify_subdomains(result.subdomains)
179
+
180
+ result.alive_count = len([s for s in result.subdomains if s.status == "alive"])
181
+ result.end_time = datetime.utcnow()
182
+ result.duration_seconds = (result.end_time - result.start_time).total_seconds()
183
+
184
+ logger.info(
185
+ f"Enumeration complete: {result.total_found} found, {result.alive_count} alive"
186
+ )
187
+
188
+ return result
189
+
190
+ async def _from_crtsh(self, domain: str) -> list[Subdomain]:
191
+ """Query crt.sh certificate transparency logs"""
192
+ subdomains = []
193
+
194
+ try:
195
+ async with httpx.AsyncClient(timeout=30.0) as client:
196
+ response = await client.get(
197
+ f"https://crt.sh/?q=%.{domain}&output=json"
198
+ )
199
+
200
+ if response.status_code == 200:
201
+ data = response.json()
202
+
203
+ for entry in data:
204
+ name = entry.get("name_value", "")
205
+ # Split by newlines (can have multiple names)
206
+ for sub in name.split("\n"):
207
+ sub = sub.strip().lower()
208
+ # Clean wildcards
209
+ sub = sub.replace("*.", "")
210
+ if sub and sub.endswith(domain) and sub not in self._found:
211
+ subdomains.append(Subdomain(
212
+ domain=sub,
213
+ source="crt.sh",
214
+ ))
215
+
216
+ except Exception as e:
217
+ logger.debug(f"crt.sh error: {e}")
218
+
219
+ return subdomains
220
+
221
+ async def _from_subfinder(self, domain: str) -> list[Subdomain]:
222
+ """Run subfinder"""
223
+ subdomains = []
224
+
225
+ try:
226
+ process = await asyncio.create_subprocess_exec(
227
+ "subfinder", "-d", domain, "-silent",
228
+ stdout=asyncio.subprocess.PIPE,
229
+ stderr=asyncio.subprocess.PIPE,
230
+ )
231
+ stdout, _ = await asyncio.wait_for(process.communicate(), timeout=120.0)
232
+
233
+ for line in stdout.decode().strip().split("\n"):
234
+ sub = line.strip().lower()
235
+ if sub and sub not in self._found:
236
+ subdomains.append(Subdomain(
237
+ domain=sub,
238
+ source="subfinder",
239
+ ))
240
+
241
+ except Exception as e:
242
+ logger.debug(f"subfinder error: {e}")
243
+
244
+ return subdomains
245
+
246
+ async def _from_amass(self, domain: str) -> list[Subdomain]:
247
+ """Run amass"""
248
+ subdomains = []
249
+
250
+ try:
251
+ process = await asyncio.create_subprocess_exec(
252
+ "amass", "enum", "-passive", "-d", domain,
253
+ stdout=asyncio.subprocess.PIPE,
254
+ stderr=asyncio.subprocess.PIPE,
255
+ )
256
+ stdout, _ = await asyncio.wait_for(process.communicate(), timeout=300.0)
257
+
258
+ for line in stdout.decode().strip().split("\n"):
259
+ sub = line.strip().lower()
260
+ if sub and sub not in self._found:
261
+ subdomains.append(Subdomain(
262
+ domain=sub,
263
+ source="amass",
264
+ ))
265
+
266
+ except Exception as e:
267
+ logger.debug(f"amass error: {e}")
268
+
269
+ return subdomains
270
+
271
+ async def _dns_bruteforce(self, domain: str) -> list[Subdomain]:
272
+ """Bruteforce subdomains using wordlist"""
273
+ subdomains = []
274
+ semaphore = asyncio.Semaphore(self.config.concurrent_requests)
275
+
276
+ async def check_subdomain(word: str) -> Optional[Subdomain]:
277
+ async with semaphore:
278
+ subdomain = f"{word}.{domain}"
279
+ try:
280
+ socket.setdefaulttimeout(self.config.timeout)
281
+ ip = socket.gethostbyname(subdomain)
282
+ return Subdomain(
283
+ domain=subdomain,
284
+ ip=ip,
285
+ status="alive",
286
+ source="dns_bruteforce",
287
+ )
288
+ except socket.gaierror:
289
+ return None
290
+
291
+ tasks = [check_subdomain(word) for word in self.config.wordlist]
292
+ results = await asyncio.gather(*tasks)
293
+
294
+ for result in results:
295
+ if result and result.domain not in self._found:
296
+ subdomains.append(result)
297
+
298
+ return subdomains
299
+
300
+ async def _verify_subdomains(self, subdomains: list[Subdomain]) -> None:
301
+ """Verify subdomains are alive"""
302
+ semaphore = asyncio.Semaphore(self.config.concurrent_requests)
303
+
304
+ async def verify(subdomain: Subdomain) -> None:
305
+ async with semaphore:
306
+ # DNS resolution
307
+ if self.config.resolve_dns and not subdomain.ip:
308
+ try:
309
+ subdomain.ip = socket.gethostbyname(subdomain.domain)
310
+ except socket.gaierror:
311
+ subdomain.status = "dead"
312
+ return
313
+
314
+ # HTTP check
315
+ if self.config.check_http:
316
+ try:
317
+ async with httpx.AsyncClient(
318
+ timeout=self.config.timeout,
319
+ follow_redirects=True,
320
+ verify=False,
321
+ ) as client:
322
+ # Try HTTPS first
323
+ try:
324
+ response = await client.get(f"https://{subdomain.domain}")
325
+ subdomain.https_status = response.status_code
326
+ subdomain.status = "alive"
327
+
328
+ # Extract title
329
+ title_match = re.search(
330
+ r"<title[^>]*>([^<]+)</title>",
331
+ response.text,
332
+ re.IGNORECASE,
333
+ )
334
+ if title_match:
335
+ subdomain.title = title_match.group(1).strip()[:100]
336
+
337
+ except Exception:
338
+ # Try HTTP
339
+ try:
340
+ response = await client.get(f"http://{subdomain.domain}")
341
+ subdomain.http_status = response.status_code
342
+ subdomain.status = "alive"
343
+ except Exception:
344
+ pass
345
+
346
+ except Exception:
347
+ pass
348
+
349
+ # Mark as alive if we got an IP
350
+ if subdomain.ip and subdomain.status == "unknown":
351
+ subdomain.status = "alive"
352
+
353
+ await asyncio.gather(*[verify(s) for s in subdomains])
354
+
355
+ def _tool_available(self, tool: str) -> bool:
356
+ """Check if tool is available"""
357
+ import shutil
358
+ return shutil.which(tool) is not None
359
+
360
+
361
+ # Convenience function
362
+ async def enumerate_subdomains(domain: str, quick: bool = True) -> SubdomainResult:
363
+ """Quick subdomain enumeration"""
364
+ config = SubdomainConfig(
365
+ use_subfinder=not quick,
366
+ use_amass=False,
367
+ use_crtsh=True,
368
+ use_dns_bruteforce=not quick,
369
+ check_http=True,
370
+ )
371
+ enumerator = SubdomainEnumerator(config)
372
+ return await enumerator.enumerate(domain)
@@ -0,0 +1,311 @@
1
+ """
2
+ AIPT Technology Detection
3
+
4
+ Web technology fingerprinting and stack detection.
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
+ import httpx
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ @dataclass
20
+ class Technology:
21
+ """Detected technology"""
22
+ name: str
23
+ category: str # frontend, backend, framework, cms, server, etc.
24
+ version: str = ""
25
+ confidence: int = 100 # 0-100
26
+ evidence: str = ""
27
+
28
+ def to_dict(self) -> dict:
29
+ return {
30
+ "name": self.name,
31
+ "category": self.category,
32
+ "version": self.version,
33
+ "confidence": self.confidence,
34
+ }
35
+
36
+
37
+ @dataclass
38
+ class TechStack:
39
+ """Complete technology stack"""
40
+ url: str
41
+ technologies: list[Technology] = field(default_factory=list)
42
+ headers: dict[str, str] = field(default_factory=dict)
43
+ cookies: list[str] = field(default_factory=list)
44
+ detected_at: datetime = field(default_factory=datetime.utcnow)
45
+
46
+ def get_by_category(self, category: str) -> list[Technology]:
47
+ """Get technologies by category"""
48
+ return [t for t in self.technologies if t.category == category]
49
+
50
+ def has_tech(self, name: str) -> bool:
51
+ """Check if specific technology is present"""
52
+ return any(t.name.lower() == name.lower() for t in self.technologies)
53
+
54
+ def to_dict(self) -> dict:
55
+ return {
56
+ "url": self.url,
57
+ "technologies": [t.to_dict() for t in self.technologies],
58
+ "categories": list(set(t.category for t in self.technologies)),
59
+ }
60
+
61
+
62
+ class TechDetector:
63
+ """
64
+ Web technology fingerprinting.
65
+
66
+ Detects:
67
+ - Web servers (nginx, Apache, IIS)
68
+ - Frameworks (React, Vue, Angular, Django, Rails)
69
+ - CMS (WordPress, Drupal, Joomla)
70
+ - JavaScript libraries
71
+ - Security tools (WAF, CDN)
72
+
73
+ Example:
74
+ detector = TechDetector()
75
+ stack = await detector.detect("https://example.com")
76
+
77
+ for tech in stack.technologies:
78
+ print(f"{tech.category}: {tech.name} {tech.version}")
79
+ """
80
+
81
+ # Technology fingerprints
82
+ FINGERPRINTS = {
83
+ # Headers
84
+ "headers": {
85
+ "Server": {
86
+ "nginx": ("nginx", "server"),
87
+ "Apache": ("Apache", "server"),
88
+ "Microsoft-IIS": ("IIS", "server"),
89
+ "cloudflare": ("Cloudflare", "cdn"),
90
+ "AmazonS3": ("Amazon S3", "storage"),
91
+ "Varnish": ("Varnish", "cache"),
92
+ "gunicorn": ("Gunicorn", "server"),
93
+ "uvicorn": ("Uvicorn", "server"),
94
+ },
95
+ "X-Powered-By": {
96
+ "PHP": ("PHP", "language"),
97
+ "ASP.NET": ("ASP.NET", "framework"),
98
+ "Express": ("Express.js", "framework"),
99
+ "Next.js": ("Next.js", "framework"),
100
+ "Nuxt": ("Nuxt.js", "framework"),
101
+ },
102
+ "X-Generator": {
103
+ "WordPress": ("WordPress", "cms"),
104
+ "Drupal": ("Drupal", "cms"),
105
+ "Joomla": ("Joomla", "cms"),
106
+ },
107
+ },
108
+ # HTML patterns
109
+ "html": [
110
+ # Frameworks
111
+ (r"react", "React", "frontend"),
112
+ (r"ng-app|angular", "Angular", "frontend"),
113
+ (r"vue\.js|v-cloak|v-bind", "Vue.js", "frontend"),
114
+ (r"svelte", "Svelte", "frontend"),
115
+ (r"ember", "Ember.js", "frontend"),
116
+
117
+ # CMS
118
+ (r"wp-content|wp-includes", "WordPress", "cms"),
119
+ (r"drupal\.js|drupal\.settings", "Drupal", "cms"),
120
+ (r"joomla", "Joomla", "cms"),
121
+ (r"shopify", "Shopify", "ecommerce"),
122
+ (r"magento", "Magento", "ecommerce"),
123
+ (r"woocommerce", "WooCommerce", "ecommerce"),
124
+
125
+ # JavaScript libraries
126
+ (r"jquery[\.-]?\d|jquery\.min\.js", "jQuery", "javascript"),
127
+ (r"bootstrap[\.-]?\d|bootstrap\.min", "Bootstrap", "css"),
128
+ (r"tailwind", "Tailwind CSS", "css"),
129
+ (r"lodash", "Lodash", "javascript"),
130
+ (r"moment\.js|moment\.min", "Moment.js", "javascript"),
131
+ (r"axios", "Axios", "javascript"),
132
+
133
+ # Analytics
134
+ (r"google-analytics|gtag|ga\.js", "Google Analytics", "analytics"),
135
+ (r"googletagmanager", "Google Tag Manager", "analytics"),
136
+ (r"facebook.*pixel|fbq\(", "Facebook Pixel", "analytics"),
137
+ (r"hotjar", "Hotjar", "analytics"),
138
+ (r"segment\.io|analytics\.js", "Segment", "analytics"),
139
+
140
+ # Security
141
+ (r"recaptcha", "reCAPTCHA", "security"),
142
+ (r"hcaptcha", "hCaptcha", "security"),
143
+ (r"cloudflare", "Cloudflare", "cdn"),
144
+ (r"akamai", "Akamai", "cdn"),
145
+ (r"fastly", "Fastly", "cdn"),
146
+
147
+ # Other
148
+ (r"webpack", "Webpack", "build"),
149
+ (r"vite", "Vite", "build"),
150
+ (r"graphql", "GraphQL", "api"),
151
+ (r"socket\.io", "Socket.IO", "websocket"),
152
+ ],
153
+ # Cookie patterns
154
+ "cookies": {
155
+ "PHPSESSID": ("PHP", "language"),
156
+ "ASP.NET_SessionId": ("ASP.NET", "framework"),
157
+ "JSESSIONID": ("Java", "language"),
158
+ "rack.session": ("Ruby/Rack", "framework"),
159
+ "express.sid": ("Express.js", "framework"),
160
+ "connect.sid": ("Connect.js", "framework"),
161
+ "laravel_session": ("Laravel", "framework"),
162
+ "django": ("Django", "framework"),
163
+ "wordpress": ("WordPress", "cms"),
164
+ "wp-settings": ("WordPress", "cms"),
165
+ "__cf_bm": ("Cloudflare", "cdn"),
166
+ },
167
+ }
168
+
169
+ def __init__(self, timeout: float = 10.0):
170
+ self.timeout = timeout
171
+
172
+ async def detect(self, url: str) -> TechStack:
173
+ """
174
+ Detect technologies used by a website.
175
+
176
+ Args:
177
+ url: Target URL
178
+
179
+ Returns:
180
+ TechStack with detected technologies
181
+ """
182
+ stack = TechStack(url=url)
183
+
184
+ try:
185
+ async with httpx.AsyncClient(
186
+ timeout=self.timeout,
187
+ follow_redirects=True,
188
+ verify=False,
189
+ ) as client:
190
+ response = await client.get(url)
191
+
192
+ # Store headers
193
+ stack.headers = dict(response.headers)
194
+
195
+ # Store cookies
196
+ stack.cookies = [c for c in response.cookies.keys()]
197
+
198
+ # Detect from headers
199
+ self._detect_from_headers(response.headers, stack)
200
+
201
+ # Detect from cookies
202
+ self._detect_from_cookies(response.cookies, stack)
203
+
204
+ # Detect from HTML
205
+ self._detect_from_html(response.text, stack)
206
+
207
+ # Extract versions where possible
208
+ self._extract_versions(response.text, stack)
209
+
210
+ except Exception as e:
211
+ logger.error(f"Tech detection error: {e}")
212
+
213
+ # Deduplicate
214
+ seen = set()
215
+ unique = []
216
+ for tech in stack.technologies:
217
+ key = (tech.name, tech.category)
218
+ if key not in seen:
219
+ seen.add(key)
220
+ unique.append(tech)
221
+ stack.technologies = unique
222
+
223
+ logger.info(f"Detected {len(stack.technologies)} technologies")
224
+ return stack
225
+
226
+ def _detect_from_headers(self, headers: httpx.Headers, stack: TechStack) -> None:
227
+ """Detect technologies from HTTP headers"""
228
+ for header, patterns in self.FINGERPRINTS["headers"].items():
229
+ value = headers.get(header, "")
230
+ if value:
231
+ for pattern, (name, category) in patterns.items():
232
+ if pattern.lower() in value.lower():
233
+ # Extract version if present
234
+ version = ""
235
+ version_match = re.search(rf"{pattern}[/\s]*([\d.]+)", value, re.I)
236
+ if version_match:
237
+ version = version_match.group(1)
238
+
239
+ stack.technologies.append(Technology(
240
+ name=name,
241
+ category=category,
242
+ version=version,
243
+ confidence=100,
244
+ evidence=f"Header: {header}: {value}",
245
+ ))
246
+
247
+ def _detect_from_cookies(self, cookies: httpx.Cookies, stack: TechStack) -> None:
248
+ """Detect technologies from cookies"""
249
+ for cookie_name in cookies.keys():
250
+ for pattern, (name, category) in self.FINGERPRINTS["cookies"].items():
251
+ if pattern.lower() in cookie_name.lower():
252
+ stack.technologies.append(Technology(
253
+ name=name,
254
+ category=category,
255
+ confidence=90,
256
+ evidence=f"Cookie: {cookie_name}",
257
+ ))
258
+
259
+ def _detect_from_html(self, html: str, stack: TechStack) -> None:
260
+ """Detect technologies from HTML content"""
261
+ html_lower = html.lower()
262
+
263
+ for pattern, name, category in self.FINGERPRINTS["html"]:
264
+ if re.search(pattern, html_lower):
265
+ stack.technologies.append(Technology(
266
+ name=name,
267
+ category=category,
268
+ confidence=80,
269
+ evidence=f"HTML pattern: {pattern}",
270
+ ))
271
+
272
+ # Check meta generator
273
+ generator_match = re.search(
274
+ r'<meta[^>]*name=["\']generator["\'][^>]*content=["\']([^"\']+)["\']',
275
+ html,
276
+ re.I,
277
+ )
278
+ if generator_match:
279
+ generator = generator_match.group(1)
280
+ stack.technologies.append(Technology(
281
+ name=generator.split()[0],
282
+ category="cms",
283
+ version=generator.split()[1] if len(generator.split()) > 1 else "",
284
+ confidence=100,
285
+ evidence=f"Meta generator: {generator}",
286
+ ))
287
+
288
+ def _extract_versions(self, html: str, stack: TechStack) -> None:
289
+ """Try to extract version numbers"""
290
+ # Version patterns for common technologies
291
+ version_patterns = {
292
+ "jQuery": r"jquery[.-]?(\d+\.\d+(?:\.\d+)?)",
293
+ "Bootstrap": r"bootstrap[.-]?(\d+\.\d+(?:\.\d+)?)",
294
+ "React": r"react[.-]?(\d+\.\d+(?:\.\d+)?)",
295
+ "Vue.js": r"vue[.-]?(\d+\.\d+(?:\.\d+)?)",
296
+ "Angular": r"angular[.-]?(\d+\.\d+(?:\.\d+)?)",
297
+ }
298
+
299
+ for tech in stack.technologies:
300
+ if not tech.version and tech.name in version_patterns:
301
+ pattern = version_patterns[tech.name]
302
+ match = re.search(pattern, html, re.I)
303
+ if match:
304
+ tech.version = match.group(1)
305
+
306
+
307
+ # Convenience function
308
+ async def detect_tech(url: str) -> TechStack:
309
+ """Quick technology detection"""
310
+ detector = TechDetector()
311
+ return await detector.detect(url)
@@ -0,0 +1,17 @@
1
+ """
2
+ AIPT Report Generation
3
+
4
+ Generates professional pentest reports in multiple formats:
5
+ - HTML (standalone, styled)
6
+ - Markdown (for documentation)
7
+ - JSON (for integration)
8
+ """
9
+
10
+ from .generator import ReportGenerator, ReportConfig
11
+ from .html_report import generate_html_report
12
+
13
+ __all__ = [
14
+ "ReportGenerator",
15
+ "ReportConfig",
16
+ "generate_html_report",
17
+ ]