package stealth // StealthFlags contiene todas las flags de Chrome para evasión de detección. // Ver docs/STEALTH_FLAGS.md para documentación completa. type StealthFlags struct { // UserDataDir es la ruta al perfil persistente de Chrome UserDataDir string // ProfileName es el nombre del perfil dentro de UserDataDir ProfileName string // WindowSize define el tamaño de la ventana (ancho,alto) WindowSize [2]int // UserAgent personalizado (vacío = usar default de Chrome) UserAgent string // Headless activa modo headless si es true Headless bool // NoSandbox desactiva sandbox (PELIGROSO - solo Docker/VMs) NoSandbox bool // DisableWebSecurity desactiva CORS (solo testing) DisableWebSecurity bool // EnableLogging activa logs de Chrome EnableLogging bool // LogLevel define nivel de logs (0=INFO, 1=WARNING, 2=ERROR) LogLevel int // RemoteDebuggingPort puerto para CDP (0 = aleatorio) RemoteDebuggingPort int // CustomFlags flags adicionales personalizadas CustomFlags []string } // DefaultStealthFlags retorna configuración stealth por defecto. func DefaultStealthFlags() *StealthFlags { return &StealthFlags{ ProfileName: "Default", WindowSize: [2]int{1920, 1080}, Headless: true, NoSandbox: false, DisableWebSecurity: false, EnableLogging: false, LogLevel: 2, // ERROR RemoteDebuggingPort: 0, // aleatorio } } // Build convierte StealthFlags a slice de argumentos para Chrome. func (sf *StealthFlags) Build() []string { flags := []string{ // ============================================ // CRÍTICAS - SIEMPRE ACTIVADAS // ============================================ // Elimina navigator.webdriver = true "--disable-blink-features=AutomationControlled", // Evita flag --enable-automation "--exclude-switches=enable-automation", // ============================================ // CONFIGURACIÓN DE PERFIL // ============================================ } // User data dir (perfil persistente) if sf.UserDataDir != "" { flags = append(flags, "--user-data-dir="+sf.UserDataDir) if sf.ProfileName != "" { flags = append(flags, "--profile-directory="+sf.ProfileName) } } // ============================================ // HEADLESS Y GPU // ============================================ if sf.Headless { // Nuevo modo headless estable flags = append(flags, "--headless=new") // Desactivar GPU en headless flags = append(flags, "--disable-gpu") // Ocultar scrollbars flags = append(flags, "--hide-scrollbars") // Silenciar audio flags = append(flags, "--mute-audio") } // ============================================ // CONFIGURACIÓN DE VENTANA // ============================================ if sf.WindowSize[0] > 0 && sf.WindowSize[1] > 0 { flags = append(flags, "--window-size="+intToString(sf.WindowSize[0])+","+intToString(sf.WindowSize[1]), ) } if !sf.Headless { flags = append(flags, "--start-maximized") } // ============================================ // DESACTIVAR PROMPTS Y POPUPS DE CHROME // ============================================ // No mostrar "¿Hacer Chrome tu navegador predeterminado?" flags = append(flags, "--no-default-browser-check") // No mostrar prompt de "restaurar sesión" flags = append(flags, "--disable-session-crashed-bubble") // No mostrar infobars (barras de información) flags = append(flags, "--disable-infobars") // No mostrar "Chrome está siendo controlado por software automatizado" // (ya cubierto por --exclude-switches=enable-automation) // Desactivar prompts de guardar contraseñas flags = append(flags, "--disable-save-password-bubble") // No mostrar primera experiencia de usuario flags = append(flags, "--no-first-run") // Desactivar componentes de sync flags = append(flags, "--disable-sync") // Desactivar ofertas de instalación de Chrome flags = append(flags, "--disable-component-update") // ============================================ // USER AGENT // ============================================ if sf.UserAgent != "" { flags = append(flags, "--user-agent="+sf.UserAgent) } // ============================================ // OPTIMIZACIÓN Y ESTABILIDAD // ============================================ // Evita problemas en Docker/containers flags = append(flags, "--disable-dev-shm-usage") // Reduce superficie de detección flags = append(flags, "--disable-extensions") // Mejora rendimiento flags = append(flags, "--disable-plugins") // Evita throttling de timers flags = append(flags, "--disable-background-timer-throttling") // Mantiene ventanas ocultas activas flags = append(flags, "--disable-backgrounding-occluded-windows") // Mantiene renderer activo flags = append(flags, "--disable-renderer-backgrounding") // Permite muchos comandos CDP rápidos flags = append(flags, "--disable-ipc-flooding-protection") // ============================================ // PRIVACIDAD Y UI // ============================================ // Bloquea notificaciones flags = append(flags, "--disable-notifications") // Permite popups flags = append(flags, "--disable-popup-blocking") // Desactiva UI de traducción flags = append(flags, "--disable-features=TranslateUI") // Desactiva Privacy Sandbox flags = append(flags, "--disable-features=PrivacySandboxSettings4") // ============================================ // FLAGS OPCIONALES (PELIGROSAS/DEBUG) // ============================================ // SANDBOX - Solo Docker/VMs confiables if sf.NoSandbox { flags = append(flags, "--no-sandbox", "--disable-setuid-sandbox") } // WEB SECURITY - Solo testing if sf.DisableWebSecurity { flags = append(flags, "--disable-web-security") flags = append(flags, "--disable-features=IsolateOrigins,site-per-process") flags = append(flags, "--disable-site-isolation-trials") } // LOGGING - Solo debugging if sf.EnableLogging { flags = append(flags, "--enable-logging") flags = append(flags, "--v=1") flags = append(flags, "--log-level="+intToString(sf.LogLevel)) } // ============================================ // REMOTE DEBUGGING (CDP) // ============================================ if sf.RemoteDebuggingPort > 0 { flags = append(flags, "--remote-debugging-port="+intToString(sf.RemoteDebuggingPort)) } else { // Puerto aleatorio, CDP asignará uno flags = append(flags, "--remote-debugging-port=0") } // Escuchar en todas las interfaces flags = append(flags, "--remote-debugging-address=0.0.0.0") // ============================================ // CUSTOM FLAGS // ============================================ if len(sf.CustomFlags) > 0 { flags = append(flags, sf.CustomFlags...) } return flags } // GetAntiDetectionScript retorna el código JavaScript para inyectar // en cada página y sobrescribir propiedades que delatan automatización. func GetAntiDetectionScript() string { return ` // ============================================ // ANTI-DETECTION SCRIPT // ============================================ // Sobrescribir navigator.webdriver Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); // Eliminar propiedades de Selenium/WebDriver delete window.navigator.__proto__.webdriver; delete window.navigator.webdriver; delete window._selenium; delete window._webdriver; delete window.callSelenium; delete window.callPhantom; delete window._phantom; // Mock chrome runtime window.chrome = { app: { isInstalled: false, InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' }, RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' } }, runtime: { OnInstalledReason: { CHROME_UPDATE: 'chrome_update', INSTALL: 'install', SHARED_MODULE_UPDATE: 'shared_module_update', UPDATE: 'update' }, OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' }, PlatformArch: { ARM: 'arm', ARM64: 'arm64', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' }, PlatformNaclArch: { ARM: 'arm', MIPS: 'mips', MIPS64: 'mips64', X86_32: 'x86-32', X86_64: 'x86-64' }, PlatformOs: { ANDROID: 'android', CROS: 'cros', LINUX: 'linux', MAC: 'mac', OPENBSD: 'openbsd', WIN: 'win' }, RequestUpdateCheckStatus: { NO_UPDATE: 'no_update', THROTTLED: 'throttled', UPDATE_AVAILABLE: 'update_available' } }, csi: function() {}, loadTimes: function() {} }; // Mock permissions.query const originalQuery = window.navigator.permissions.query; window.navigator.permissions.query = (parameters) => ( parameters.name === 'notifications' ? Promise.resolve({ state: Notification.permission }) : originalQuery(parameters) ); // Plugins array mock (navegadores reales tienen plugins) Object.defineProperty(navigator, 'plugins', { get: () => [ { 0: {type: "application/x-google-chrome-pdf", suffixes: "pdf", description: "Portable Document Format", enabledPlugin: Plugin}, description: "Portable Document Format", filename: "internal-pdf-viewer", length: 1, name: "Chrome PDF Plugin" }, { 0: {type: "application/pdf", suffixes: "pdf", description: "", enabledPlugin: Plugin}, description: "", filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai", length: 1, name: "Chrome PDF Viewer" }, { 0: {type: "application/x-nacl", suffixes: "", description: "Native Client Executable", enabledPlugin: Plugin}, 1: {type: "application/x-pnacl", suffixes: "", description: "Portable Native Client Executable", enabledPlugin: Plugin}, description: "", filename: "internal-nacl-plugin", length: 2, name: "Native Client" } ] }); // Languages array (debe tener contenido realista) Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en', 'es'] }); // Platform fix Object.defineProperty(navigator, 'platform', { get: () => 'Win32' }); // Vendor fix Object.defineProperty(navigator, 'vendor', { get: () => 'Google Inc.' }); // Connection mock Object.defineProperty(navigator, 'connection', { get: () => ({ effectiveType: '4g', rtt: 100, downlink: 10, saveData: false }) }); // Hardware concurrency (núcleos de CPU) Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8 }); // Device memory (GB RAM) Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 }); // Battery mock (solo si la API existe) if (navigator.getBattery) { const originalGetBattery = navigator.getBattery; navigator.getBattery = () => originalGetBattery().then(battery => { Object.defineProperty(battery, 'charging', { get: () => true }); Object.defineProperty(battery, 'chargingTime', { get: () => 0 }); Object.defineProperty(battery, 'dischargingTime', { get: () => Infinity }); Object.defineProperty(battery, 'level', { get: () => 1 }); return battery; }); } // Console debug signature removal const originalConsole = { log: console.log, warn: console.warn, error: console.error, debug: console.debug }; console.log = (...args) => originalConsole.log.apply(console, args); console.warn = (...args) => originalConsole.warn.apply(console, args); console.error = (...args) => originalConsole.error.apply(console, args); console.debug = (...args) => originalConsole.debug.apply(console, args); // Performance timing fix if (window.performance && window.performance.timing) { Object.defineProperty(window.performance.timing, 'navigationStart', { get: () => Date.now() - Math.floor(Math.random() * 10000) }); } // Screen dimensions mock (deben coincidir con window size) Object.defineProperty(window.screen, 'width', { get: () => 1920 }); Object.defineProperty(window.screen, 'height', { get: () => 1080 }); Object.defineProperty(window.screen, 'availWidth', { get: () => 1920 }); Object.defineProperty(window.screen, 'availHeight', { get: () => 1040 // Menos barra de tareas }); // ============================================ // FIN ANTI-DETECTION SCRIPT // ============================================ ` } // intToString convierte int a string (helper simple) func intToString(n int) string { if n == 0 { return "0" } var buf [20]byte i := len(buf) - 1 neg := n < 0 if neg { n = -n } for n > 0 { buf[i] = byte('0' + n%10) n /= 10 i-- } if neg { buf[i] = '-' i-- } return string(buf[i+1:]) }