unrealon 2.0.10__py3-none-any.whl → 2.0.12__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.
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/METADATA +1 -1
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/RECORD +21 -20
- unrealon_browser/core/browser_manager.py +58 -0
- unrealon_browser/managers/logger_bridge.py +3 -3
- unrealon_browser/managers/script_manager.py +45 -26
- unrealon_browser/stealth/{bypass_techniques.pyc → bypass_techniques.py} +0 -0
- unrealon_browser/stealth/manager.py +507 -0
- unrealon_browser/stealth/nodriver_stealth.py +432 -0
- unrealon_browser/stealth/playwright_stealth.py +179 -0
- unrealon_browser/stealth/scanner_tester.py +355 -0
- unrealon_browser/stealth/undetected_chrome.py +361 -0
- unrealon_driver/__init__.py +12 -2
- unrealon_driver/driver/factory/manager_factory.py +8 -1
- unrealon_driver/managers/browser.py +16 -1
- unrealon_driver/managers/proxy.py +11 -4
- unrealon_driver/utils/__init__.py +13 -4
- unrealon_driver/utils/platform_compatibility.py +205 -0
- unrealon_browser/stealth/manager.pyc +0 -0
- unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
- unrealon_browser/stealth/playwright_stealth.pyc +0 -0
- unrealon_browser/stealth/scanner_tester.pyc +0 -0
- unrealon_browser/stealth/undetected_chrome.pyc +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/LICENSE +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/WHEEL +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/entry_points.txt +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.12.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StealthManager - Main manager for all detection bypass techniques
|
|
3
|
+
|
|
4
|
+
Coordinates work of all stealth modules:
|
|
5
|
+
- PlaywrightStealth for playwright-stealth integration
|
|
6
|
+
- UndetectedChrome for undetected-chromedriver
|
|
7
|
+
- NoDriverStealth for NoDriver
|
|
8
|
+
- BypassTechniques for advanced BotD bypass techniques
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import asyncio
|
|
13
|
+
from typing import Dict, Any, Optional, List
|
|
14
|
+
from playwright.async_api import Page, BrowserContext
|
|
15
|
+
|
|
16
|
+
from unrealon_core.config.urls import get_url_config
|
|
17
|
+
from unrealon_browser.dto import BotDetectionResults, BotDetectionResponse
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
logger.setLevel(logging.DEBUG)
|
|
21
|
+
|
|
22
|
+
class StealthManager:
|
|
23
|
+
"""
|
|
24
|
+
Main manager for all detection bypass techniques
|
|
25
|
+
|
|
26
|
+
Coordinates application of various stealth techniques:
|
|
27
|
+
- Playwright-stealth 2.0.0
|
|
28
|
+
- Undetected ChromeDriver integration
|
|
29
|
+
- NoDriver support
|
|
30
|
+
- Advanced BotD bypass techniques
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, logger_bridge=None):
|
|
34
|
+
"""Initialize stealth manager"""
|
|
35
|
+
self.stealth_applied = False
|
|
36
|
+
self.test_results: Optional[Dict[str, Any]] = None
|
|
37
|
+
self.logger_bridge = logger_bridge
|
|
38
|
+
|
|
39
|
+
# Lazy import stealth modules to avoid circular imports
|
|
40
|
+
self._playwright_stealth = None
|
|
41
|
+
self._undetected_chrome = None
|
|
42
|
+
self._nodriver_stealth = None
|
|
43
|
+
self._bypass_techniques = None
|
|
44
|
+
self._scanner_tester = None
|
|
45
|
+
|
|
46
|
+
def _get_stealth_test_url(self) -> str:
|
|
47
|
+
"""Get stealth test URL from configuration."""
|
|
48
|
+
return get_url_config().stealth_test_url
|
|
49
|
+
|
|
50
|
+
def _logger(self, message: str, level: str = "info") -> None:
|
|
51
|
+
"""Private wrapper for logger with bridge support"""
|
|
52
|
+
if self.logger_bridge:
|
|
53
|
+
if level == "info":
|
|
54
|
+
self.logger_bridge.log_info(message)
|
|
55
|
+
elif level == "error":
|
|
56
|
+
self.logger_bridge.log_error(message)
|
|
57
|
+
elif level == "warning":
|
|
58
|
+
self.logger_bridge.log_warning(message)
|
|
59
|
+
else:
|
|
60
|
+
self.logger_bridge.log_info(message)
|
|
61
|
+
else:
|
|
62
|
+
if level == "info":
|
|
63
|
+
logger.info(message)
|
|
64
|
+
elif level == "error":
|
|
65
|
+
logger.error(message)
|
|
66
|
+
elif level == "warning":
|
|
67
|
+
logger.warning(message)
|
|
68
|
+
else:
|
|
69
|
+
logger.info(message)
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def playwright_stealth(self):
|
|
73
|
+
"""Lazy load PlaywrightStealth"""
|
|
74
|
+
if self._playwright_stealth is None:
|
|
75
|
+
from .playwright_stealth import PlaywrightStealth
|
|
76
|
+
self._playwright_stealth = PlaywrightStealth(self.logger_bridge)
|
|
77
|
+
return self._playwright_stealth
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def undetected_chrome(self):
|
|
81
|
+
"""Lazy load UndetectedChrome"""
|
|
82
|
+
if self._undetected_chrome is None:
|
|
83
|
+
from .undetected_chrome import UndetectedChrome
|
|
84
|
+
self._undetected_chrome = UndetectedChrome(self.logger_bridge)
|
|
85
|
+
return self._undetected_chrome
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def nodriver_stealth(self):
|
|
89
|
+
"""Lazy load NoDriverStealth"""
|
|
90
|
+
if self._nodriver_stealth is None:
|
|
91
|
+
from .nodriver_stealth import NoDriverStealth
|
|
92
|
+
self._nodriver_stealth = NoDriverStealth(self.logger_bridge)
|
|
93
|
+
return self._nodriver_stealth
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def bypass_techniques(self):
|
|
97
|
+
"""Lazy load BypassTechniques"""
|
|
98
|
+
if self._bypass_techniques is None:
|
|
99
|
+
from .bypass_techniques import BypassTechniques
|
|
100
|
+
self._bypass_techniques = BypassTechniques(self.logger_bridge)
|
|
101
|
+
return self._bypass_techniques
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def scanner_tester(self):
|
|
105
|
+
"""Lazy load ScannerTester"""
|
|
106
|
+
if not hasattr(self, '_scanner_tester') or self._scanner_tester is None:
|
|
107
|
+
from .scanner_tester import ScannerTester
|
|
108
|
+
self._scanner_tester = ScannerTester(self.logger_bridge)
|
|
109
|
+
return self._scanner_tester
|
|
110
|
+
|
|
111
|
+
def get_stealth_args(self) -> List[str]:
|
|
112
|
+
"""
|
|
113
|
+
Get comprehensive stealth arguments for browser launch
|
|
114
|
+
Combines best practices from all stealth techniques
|
|
115
|
+
"""
|
|
116
|
+
import platform
|
|
117
|
+
|
|
118
|
+
args = [
|
|
119
|
+
# 🔥 CRITICAL: Remove automation indicators
|
|
120
|
+
"--disable-blink-features=AutomationControlled",
|
|
121
|
+
"--disable-features=VizDisplayCompositor",
|
|
122
|
+
|
|
123
|
+
# 🥷 Security & Detection Bypass
|
|
124
|
+
"--disable-web-security",
|
|
125
|
+
"--disable-features=TranslateUI",
|
|
126
|
+
"--disable-ipc-flooding-protection",
|
|
127
|
+
"--disable-client-side-phishing-detection",
|
|
128
|
+
"--disable-component-extensions-with-background-pages",
|
|
129
|
+
"--disable-default-apps",
|
|
130
|
+
"--disable-domain-reliability",
|
|
131
|
+
"--disable-background-networking",
|
|
132
|
+
|
|
133
|
+
# 🎭 Mimic real user behavior
|
|
134
|
+
"--no-first-run",
|
|
135
|
+
"--no-default-browser-check",
|
|
136
|
+
"--no-sandbox",
|
|
137
|
+
"--disable-dev-shm-usage",
|
|
138
|
+
"--disable-translate",
|
|
139
|
+
|
|
140
|
+
# 📐 Window & Display - realistic settings
|
|
141
|
+
"--window-size=1366,768", # More common resolution
|
|
142
|
+
"--start-maximized",
|
|
143
|
+
"--hide-scrollbars",
|
|
144
|
+
|
|
145
|
+
# ⚡ Performance & Background
|
|
146
|
+
"--disable-background-timer-throttling",
|
|
147
|
+
"--disable-backgrounding-occluded-windows",
|
|
148
|
+
"--disable-renderer-backgrounding",
|
|
149
|
+
"--disable-field-trial-config",
|
|
150
|
+
"--disable-back-forward-cache",
|
|
151
|
+
|
|
152
|
+
# 🔧 System & Monitoring
|
|
153
|
+
"--disable-hang-monitor",
|
|
154
|
+
"--disable-prompt-on-repost",
|
|
155
|
+
"--disable-sync",
|
|
156
|
+
"--metrics-recording-only",
|
|
157
|
+
"--no-report-upload",
|
|
158
|
+
"--mute-audio",
|
|
159
|
+
|
|
160
|
+
# 🆕 Enhanced for BotD bypass (keep some extensions for realism)
|
|
161
|
+
"--disable-extensions-except=",
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
# 🍎 macOS specific fixes for "Mach rendezvous failed" error
|
|
165
|
+
if platform.system() == "Darwin":
|
|
166
|
+
args.extend([
|
|
167
|
+
"--disable-gpu", # Disable GPU acceleration on macOS
|
|
168
|
+
"--disable-software-rasterizer", # Disable software rasterizer
|
|
169
|
+
"--disable-background-mode", # Disable background mode
|
|
170
|
+
"--disable-features=VizDisplayCompositor,VizHitTestSurfaceLayer", # Disable problematic features
|
|
171
|
+
"--use-gl=swiftshader", # Use software GL implementation
|
|
172
|
+
"--disable-ipc-flooding-protection", # Disable IPC flooding protection
|
|
173
|
+
"--single-process", # Use single process mode to avoid IPC issues
|
|
174
|
+
])
|
|
175
|
+
|
|
176
|
+
return args
|
|
177
|
+
|
|
178
|
+
async def apply_stealth(self, page: Page, method: str = "comprehensive") -> bool:
|
|
179
|
+
"""
|
|
180
|
+
Apply stealth measures to page
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
page: Playwright page instance
|
|
184
|
+
method: Stealth method ("playwright", "comprehensive", "aggressive")
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
True if successful, False otherwise
|
|
188
|
+
"""
|
|
189
|
+
try:
|
|
190
|
+
self._logger(f"🥷 Applying stealth measures (method: {method})...", "info")
|
|
191
|
+
|
|
192
|
+
success = True
|
|
193
|
+
|
|
194
|
+
if method in ["playwright", "comprehensive"]:
|
|
195
|
+
# 1. Apply playwright-stealth 2.0.0
|
|
196
|
+
playwright_success = await self.playwright_stealth.apply_stealth(page)
|
|
197
|
+
success = success and playwright_success
|
|
198
|
+
|
|
199
|
+
if method in ["comprehensive", "aggressive"]:
|
|
200
|
+
# 2. Apply advanced BotD bypass techniques
|
|
201
|
+
bypass_success = await self.bypass_techniques.apply_all_bypasses(page)
|
|
202
|
+
success = success and bypass_success
|
|
203
|
+
|
|
204
|
+
# 3. Always apply basic webdriver removal
|
|
205
|
+
await self.bypass_techniques.apply_webdriver_removal(page)
|
|
206
|
+
|
|
207
|
+
self.stealth_applied = success
|
|
208
|
+
|
|
209
|
+
if success:
|
|
210
|
+
self._logger("✅ Stealth measures applied successfully", "info")
|
|
211
|
+
else:
|
|
212
|
+
self._logger("⚠️ Some stealth measures failed, but continuing", "warning")
|
|
213
|
+
|
|
214
|
+
return success
|
|
215
|
+
|
|
216
|
+
except Exception as e:
|
|
217
|
+
self._logger(f"❌ Failed to apply stealth measures: {e}", "error")
|
|
218
|
+
self.stealth_applied = False
|
|
219
|
+
return False
|
|
220
|
+
|
|
221
|
+
async def apply_stealth_to_context(self, context: BrowserContext) -> bool:
|
|
222
|
+
"""Apply stealth measures to entire browser context"""
|
|
223
|
+
try:
|
|
224
|
+
self._logger("🥷 Applying stealth to browser context...", "info")
|
|
225
|
+
|
|
226
|
+
# Apply context-level stealth scripts
|
|
227
|
+
success = await self.bypass_techniques.apply_context_stealth(context)
|
|
228
|
+
|
|
229
|
+
if success:
|
|
230
|
+
self._logger("✅ Context stealth applied successfully", "info")
|
|
231
|
+
else:
|
|
232
|
+
self._logger("⚠️ Context stealth partially failed", "warning")
|
|
233
|
+
|
|
234
|
+
return success
|
|
235
|
+
|
|
236
|
+
except Exception as e:
|
|
237
|
+
self._logger(f"❌ Failed to apply context stealth: {e}", "error")
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
async def apply_webdriver_removal(self, context: BrowserContext) -> bool:
|
|
241
|
+
"""
|
|
242
|
+
Apply webdriver removal to browser context
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
context: Playwright browser context
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
True if successful
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
self._logger("🛡️ Applying webdriver removal to context...", "info")
|
|
252
|
+
|
|
253
|
+
# Use bypass techniques for webdriver removal
|
|
254
|
+
success = await self.bypass_techniques.apply_context_stealth(context)
|
|
255
|
+
|
|
256
|
+
if success:
|
|
257
|
+
self._logger("✅ Webdriver removal applied successfully", "info")
|
|
258
|
+
else:
|
|
259
|
+
self._logger("⚠️ Webdriver removal partially failed", "warning")
|
|
260
|
+
|
|
261
|
+
return success
|
|
262
|
+
|
|
263
|
+
except Exception as e:
|
|
264
|
+
self._logger(f"❌ Failed to apply webdriver removal: {e}", "error")
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
async def test_stealth_effectiveness(self, browser_manager) -> Dict[str, Any]:
|
|
268
|
+
"""
|
|
269
|
+
Test stealth effectiveness using ScannerTester
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
browser_manager: Browser manager instance with page
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Dict with test results and detection metrics
|
|
276
|
+
"""
|
|
277
|
+
self._logger("🔍 DEBUG: test_stealth_effectiveness called", "info")
|
|
278
|
+
self._logger(f"🔍 DEBUG: browser_manager={browser_manager}, page={browser_manager.page if browser_manager else None}", "info")
|
|
279
|
+
|
|
280
|
+
if not browser_manager or not browser_manager.page:
|
|
281
|
+
self._logger("❌ DEBUG: Browser manager or page not available", "error")
|
|
282
|
+
return {
|
|
283
|
+
"success": False,
|
|
284
|
+
"error": "Browser manager or page not available",
|
|
285
|
+
"skipped": True,
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
self._logger("🧪 Testing stealth effectiveness using ScannerTester...", "info")
|
|
290
|
+
self._logger("🔍 DEBUG: About to call scanner_tester.test_stealth()", "info")
|
|
291
|
+
|
|
292
|
+
# Use ScannerTester for proper testing
|
|
293
|
+
response = await self.scanner_tester.test_stealth(browser_manager, self, "comprehensive")
|
|
294
|
+
|
|
295
|
+
self._logger(f"🔍 DEBUG: scanner_tester.test_stealth() returned: {response}", "info")
|
|
296
|
+
|
|
297
|
+
# Convert BotDetectionResponse to dict format for compatibility
|
|
298
|
+
if response.success and response.results:
|
|
299
|
+
results = response.results
|
|
300
|
+
|
|
301
|
+
# Store results for later access
|
|
302
|
+
self.test_results = results.model_dump()
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
"success": True,
|
|
306
|
+
"results": results.model_dump(),
|
|
307
|
+
"detection_score": results.overall_score,
|
|
308
|
+
"is_bot": results.is_bot,
|
|
309
|
+
"confidence": results.confidence,
|
|
310
|
+
"tests_passed": results.summary.passed,
|
|
311
|
+
"tests_failed": results.summary.failed,
|
|
312
|
+
"total_tests": results.summary.total,
|
|
313
|
+
"error": None,
|
|
314
|
+
"skipped": False,
|
|
315
|
+
}
|
|
316
|
+
else:
|
|
317
|
+
error_msg = response.error.error if response.error else "Unknown scanner error"
|
|
318
|
+
return {
|
|
319
|
+
"success": False,
|
|
320
|
+
"error": error_msg,
|
|
321
|
+
"skipped": False,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
except Exception as e:
|
|
325
|
+
self._logger(f"❌ Stealth test failed: {e}", "error")
|
|
326
|
+
return {
|
|
327
|
+
"success": False,
|
|
328
|
+
"error": str(e),
|
|
329
|
+
"skipped": False,
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
def get_stealth_status(self) -> Dict[str, Any]:
|
|
333
|
+
"""Get current stealth status and test results"""
|
|
334
|
+
return {
|
|
335
|
+
"stealth_applied": self.stealth_applied,
|
|
336
|
+
"test_results": self.test_results,
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
def print_stealth_status(self) -> None:
|
|
340
|
+
"""Print current stealth status"""
|
|
341
|
+
status = self.get_stealth_status()
|
|
342
|
+
|
|
343
|
+
self._logger("\n🥷 Stealth Status:", "info")
|
|
344
|
+
self._logger(f"Applied: {status['stealth_applied']}", "info")
|
|
345
|
+
|
|
346
|
+
if status["test_results"]:
|
|
347
|
+
results = status["test_results"]
|
|
348
|
+
self._logger(f" Last test score: {results.get('detection_score', 'Unknown')}", "info")
|
|
349
|
+
self._logger(f"Tests passed: {results.get('tests_passed', 0)}/{results.get('total_tests', 0)}")
|
|
350
|
+
|
|
351
|
+
async def test_with_scanner(self, browser_manager, method: str = "comprehensive") -> Dict[str, Any]:
|
|
352
|
+
"""
|
|
353
|
+
Convenient method to test stealth effectiveness with our scanner
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
browser_manager: Browser manager instance
|
|
357
|
+
method: Stealth method to test ("playwright", "comprehensive", "aggressive")
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Dict with test results from our scanner
|
|
361
|
+
"""
|
|
362
|
+
self._logger(f"🔬 Testing stealth with UnrealOn scanner (method: {method})", "info")
|
|
363
|
+
|
|
364
|
+
try:
|
|
365
|
+
# Apply stealth first
|
|
366
|
+
if browser_manager.page:
|
|
367
|
+
stealth_applied = await self.apply_stealth(browser_manager.page, method)
|
|
368
|
+
self._logger(f"🥷 Stealth applied: {stealth_applied}", "info")
|
|
369
|
+
|
|
370
|
+
# Use the existing test_stealth_effectiveness method
|
|
371
|
+
result = await self.test_stealth_effectiveness(browser_manager)
|
|
372
|
+
|
|
373
|
+
# Debug: Check result type (force print)
|
|
374
|
+
self._logger(f"🔍 test_stealth_effectiveness result type: {type(result)}, value: {result}", "info")
|
|
375
|
+
|
|
376
|
+
# Store results and add method info
|
|
377
|
+
if isinstance(result, dict) and result.get("success"):
|
|
378
|
+
result["method"] = method
|
|
379
|
+
result["test_type"] = "scanner_test"
|
|
380
|
+
|
|
381
|
+
# Log summary
|
|
382
|
+
self._logger(f"📊 Scanner Results ({method}):", "info")
|
|
383
|
+
self._logger(f" Detection Score: {result.get('detection_score', 'N/A')}%", "info")
|
|
384
|
+
self._logger(f" Bot Detected: {result.get('is_bot', 'N/A')}", "info")
|
|
385
|
+
self._logger(f" Confidence: {result.get('confidence', 'N/A')}", "info")
|
|
386
|
+
self._logger(f" Tests Passed: {result.get('tests_passed', 0)}/{result.get('total_tests', 0)}", "info")
|
|
387
|
+
|
|
388
|
+
# Show failed tests if any
|
|
389
|
+
if result.get("results") and "tests" in result["results"]:
|
|
390
|
+
failed_tests = [test for test in result["results"]["tests"] if test.get("status") == "failed"]
|
|
391
|
+
if failed_tests:
|
|
392
|
+
self._logger(f" ⚠️ Failed Tests: {len(failed_tests)}", "warning")
|
|
393
|
+
for test in failed_tests[:3]: # Show first 3
|
|
394
|
+
self._logger(f" • {test.get('name', 'Unknown')}", "warning")
|
|
395
|
+
|
|
396
|
+
return result
|
|
397
|
+
|
|
398
|
+
except Exception as e:
|
|
399
|
+
self._logger(f"❌ Scanner test failed: {e}", "error")
|
|
400
|
+
return {
|
|
401
|
+
"success": False,
|
|
402
|
+
"error": str(e),
|
|
403
|
+
"method": method
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async def test_all_methods_with_scanner(self, browser_manager) -> Dict[str, Any]:
|
|
407
|
+
"""
|
|
408
|
+
Test all stealth methods with our scanner and compare results
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
browser_manager: Browser manager instance
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
Dict with comprehensive comparison results
|
|
415
|
+
"""
|
|
416
|
+
self._logger("🔬 Testing all stealth methods with UnrealOn scanner", "info")
|
|
417
|
+
|
|
418
|
+
try:
|
|
419
|
+
# Use scanner tester for comprehensive test
|
|
420
|
+
results = await self.scanner_tester.test_stealth_comprehensive(browser_manager, self)
|
|
421
|
+
|
|
422
|
+
# Print formatted results
|
|
423
|
+
self.scanner_tester.print_test_results(results)
|
|
424
|
+
|
|
425
|
+
# Store best results
|
|
426
|
+
if results.get("best_config"):
|
|
427
|
+
self.test_results = results["best_config"].get("raw_results", results["best_config"])
|
|
428
|
+
|
|
429
|
+
return results
|
|
430
|
+
|
|
431
|
+
except Exception as e:
|
|
432
|
+
self._logger(f"❌ Comprehensive scanner test failed: {e}", "error")
|
|
433
|
+
return {
|
|
434
|
+
"success": False,
|
|
435
|
+
"error": str(e),
|
|
436
|
+
"test_type": "comprehensive"
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async def compare_stealth_methods_with_scanner(self, browser_manager) -> Dict[str, Any]:
|
|
440
|
+
"""
|
|
441
|
+
Compare different stealth methods using our scanner
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
browser_manager: Browser manager instance
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
Dict with comparison results
|
|
448
|
+
"""
|
|
449
|
+
self._logger("🔬 Comparing stealth methods with UnrealOn scanner", "info")
|
|
450
|
+
|
|
451
|
+
try:
|
|
452
|
+
# Use scanner tester for comparison
|
|
453
|
+
results = await self.scanner_tester.test_stealth_comparison(browser_manager, self)
|
|
454
|
+
|
|
455
|
+
# Print formatted results
|
|
456
|
+
self.scanner_tester.print_test_results(results)
|
|
457
|
+
|
|
458
|
+
# Store winner results
|
|
459
|
+
if results.get("winner"):
|
|
460
|
+
self.test_results = results["winner"].get("raw_results", results["winner"])
|
|
461
|
+
|
|
462
|
+
return results
|
|
463
|
+
|
|
464
|
+
except Exception as e:
|
|
465
|
+
self._logger(f"❌ Scanner comparison test failed: {e}", "error")
|
|
466
|
+
return {
|
|
467
|
+
"success": False,
|
|
468
|
+
"error": str(e),
|
|
469
|
+
"test_type": "comparison"
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
def get_scanner_recommendations(self) -> List[str]:
|
|
473
|
+
"""
|
|
474
|
+
Get recommendations based on last scanner test results
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
List of recommendations for improving stealth
|
|
478
|
+
"""
|
|
479
|
+
if not self.test_results:
|
|
480
|
+
return ["Run scanner test first to get recommendations"]
|
|
481
|
+
|
|
482
|
+
# Extract recommendations from test results
|
|
483
|
+
if isinstance(self.test_results, dict):
|
|
484
|
+
# Check if it's a comprehensive result with analysis
|
|
485
|
+
if "analysis" in self.test_results:
|
|
486
|
+
return self.test_results["analysis"].get("recommendations", [])
|
|
487
|
+
|
|
488
|
+
# Check if it's raw bot detection results
|
|
489
|
+
if "tests" in self.test_results:
|
|
490
|
+
recommendations = []
|
|
491
|
+
failed_tests = [test for test in self.test_results.get("tests", []) if test.get("status") == "failed"]
|
|
492
|
+
|
|
493
|
+
# Generate basic recommendations based on failed tests
|
|
494
|
+
for test in failed_tests:
|
|
495
|
+
test_name = test.get("name", "")
|
|
496
|
+
if "BotD" in test_name:
|
|
497
|
+
recommendations.append("Consider using headed mode for better BotD bypass")
|
|
498
|
+
elif "WebDriver" in test_name:
|
|
499
|
+
recommendations.append("Enhance webdriver property removal")
|
|
500
|
+
elif "Chrome Object" in test_name:
|
|
501
|
+
recommendations.append("Improve window.chrome object spoofing")
|
|
502
|
+
elif "Plugin" in test_name:
|
|
503
|
+
recommendations.append("Add more realistic browser plugins")
|
|
504
|
+
|
|
505
|
+
return recommendations[:5] # Limit to 5 recommendations
|
|
506
|
+
|
|
507
|
+
return ["No specific recommendations available"]
|