Files
navegator/pkg/stealth/flags.go
T
Developer 3253828fef
Tests / Lint (push) Has been cancelled
Tests / Unit Tests (push) Has been cancelled
Tests / E2E Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Initial commit: navegator - Chrome CDP automation for LLMs
Add complete navegator system for stealthy browser automation:
- CDP client with WebSocket communication
- Browser API with navigation, storage, network, runtime
- Stealth flags and anti-detection scripts
- Persistent profile support
- Examples and comprehensive documentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-24 23:33:07 +01:00

468 lines
13 KiB
Go

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:])
}