feat(browser): funciones anti-deteccion + perfiles para web_scraping

Funciones nuevas del dominio browser (grupo navegator):
- cdp_move_mouse_human / cdp_click_human: movimiento de raton con curva
  de Bezier cubica, easing y micro-jitter para imitar comportamiento
  humano y reducir deteccion de automatizacion.
- cdp_wait_idle: espera network-idle contando requests en vuelo via
  eventos CDP Network.*; inmune a extensiones que mutan el DOM
  (Dark Reader, uBlock) y a animaciones JS.
- list_chrome_profiles: lista perfiles de un user-data-dir (extensiones,
  nombre legible, preferencias).
- prepare_chrome_profile (bash): clona un user-data-dir conservando solo
  una whitelist de extensiones (default uBlock Origin Lite).

Modificadas:
- chrome_launch: Linux-first (chromium/google-chrome/brave antes que
  chrome.exe), KeepExtensions y Setpgid para matar el arbol con cdp_close.
- cdp_close: kill por grupo de proceso.

Todas con tests verdes (go test ./functions/browser ok).
This commit is contained in:
Egutierrez
2026-06-05 16:25:11 +02:00
parent 729921e16e
commit ccfa5bc78b
17 changed files with 1603 additions and 45 deletions
@@ -0,0 +1,74 @@
---
name: prepare_chrome_profile
kind: function
lang: bash
domain: browser
version: "1.0.0"
purity: impure
signature: "prepare_chrome_profile --src <user-data-dir> --dst <user-data-dir> [--keep <ext_id>]... [--force]"
description: "Clona un user-data-dir de Chrome/Chromium creando un perfil de scraping limpio: conserva solo las extensiones de una lista blanca (por defecto uBlock Origin Lite) y excluye caché, locks y sesiones antiguas."
tags: [chrome, browser, profile, scraping, extensions, navegator]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/browser/prepare_chrome_profile.sh"
params:
- name: --src
desc: "user-data-dir origen con un perfil Chrome/Chromium ya configurado (debe existir --src/Default)"
- name: --dst
desc: "Ruta de destino del nuevo perfil; no debe existir salvo que se pase --force"
- name: --keep
desc: "ID de extensión Chrome a conservar (repetible). Si no se pasa ninguno el default es ddkjiahejlhfcafbddmgiahcphecmpfh (uBlock Origin Lite)"
- name: --force
desc: "Borra --dst si existe antes de recrearlo. Sin este flag la función aborta si --dst ya existe"
output: "JSON en stdout: {dst, kept: [id...], removed: [id...]}. Exit 0 en éxito."
---
## Ejemplo
```bash
source $HOME/fn_registry/bash/functions/browser/prepare_chrome_profile.sh
prepare_chrome_profile \
--src "$HOME/.config/chromium" \
--dst "$HOME/.local/share/web_scraping/chrome-profile"
# Con extensión adicional conservada
prepare_chrome_profile \
--src "$HOME/.config/chromium" \
--dst "$HOME/.local/share/web_scraping/chrome-profile" \
--keep "ddkjiahejlhfcafbddmgiahcphecmpfh" \
--keep "cjpalhdlnbpafiamejdnhcphjbkeiagm" \
--force
# Salida esperada (ejemplo):
# {"dst":"/home/enmanuel/.local/share/web_scraping/chrome-profile","kept":["ddkjiahejlhfcafbddmgiahcphecmpfh"],"removed":["abcdefghijklmnopabcdefghijklmnop","dark-reader-id"]}
```
## Cuando usarla
Úsala antes de lanzar una sesión de scraping/automatización para partir de un perfil aislado: con uBlock Origin Lite activo (menos anuncios/trackers = DOM más limpio, respuestas más rápidas) pero sin extensiones que interfieren (Dark Reader muta colores del DOM, NoScript bloquea JS, OneTab modifica tabs). También sirve para aislar sesiones de diferentes proyectos de scraping sin contaminar el perfil personal.
## Gotchas
- **Chrome debe estar CERRADO sobre `--src`** antes de ejecutar. Los archivos SQLite (`Cookies`, `History`, `Login Data`, etc.) estarán bloqueados si Chrome está abierto, y `rsync` copiará versiones inconsistentes. Verificar con `pgrep -x chromium` o `pgrep -x chrome`.
- **HMAC de Secure Preferences**: el archivo `Local State` contiene la semilla HMAC que Chrome usa para verificar `Preferences` y `Secure Preferences`. Si no se copia (o se copia entre máquinas distintas con distinto binding), Chrome puede invalidar las extensiones al arrancar y resetear configuraciones. La función copia `Local State` automáticamente, pero la copia entre máquinas puede seguir produciendo resets de extensiones — esto es comportamiento esperado de Chrome, no un bug de esta función.
- **Purga de referencias en Preferences**: tras borrar las carpetas de extensiones fuera de la whitelist, la función también elimina con `python3` las entradas `extensions.settings.<id>` de `Default/Preferences` y `Default/Secure Preferences`, los IDs de `extensions.pinned_extensions` y las claves `protection.macs.extensions.settings.<id>`. Sin esta limpieza Chrome detecta las entradas en Preferences (con `from_webstore`/install_source) y **vuelve a descargar la extensión del Web Store al arrancar**, deshaciendo el filtrado (caso real: Dark Reader reaparece y oscurece páginas rompiendo screenshots). Si `python3` falla al procesar un Preferences concreto se emite un warning a stderr pero la función no aborta — el borrado de carpetas ya es el efecto principal.
- **`--force` borra `--dst` completamente**: si `--dst` es un perfil con datos que quieres conservar, no uses `--force` sin antes hacer backup.
- **Extensiones instaladas desde Web Store vs unpacked**: esta función opera sobre la carpeta `Extensions/` física. Las extensiones instaladas desde la Web Store tienen IDs de 32 caracteres en minúsculas. Las extensiones unpacked (`--load-extension`) no viven en `Extensions/` y no se ven afectadas.
## Exit codes
| Código | Significado |
|--------|------------|
| 0 | Éxito |
| 1 | Argumento inválido o `--src/Default` no existe |
| 2 | `--dst` ya existe y no se pasó `--force` |
| 3 | `--src` y `--dst` resuelven al mismo path real |
| 4 | Error durante `rsync` |