Files
graph_explorer/issues/0039-cookie-session-manager.md
T
egutierrez 8733b7d175 docs(issues): browser externo + CDP + multi-profile (0038, 0039, 0040)
- 0038: lanzar Chrome/Edge/Brave externo con --remote-debugging-port +
  --user-data-dir por profile, control via CDP desde cdp-cli Go.
  Decision Go vs C++ in-process documentada; deja la puerta abierta a
  un cliente C++ minimo solo para streaming en el futuro. Supersedes 0032.
- 0039: gestor de cookies/sesiones por profile via CDP — list, export
  EditThisCookie, import, clear selectivo, health checks con selectores,
  locks cuando un enricher esta usando el profile.
- 0040: profiles como concepto de primera clase — metadata (color, icon,
  browser_preference, UA, project, template), templates anon/auth/work/
  investigation, ProfilePicker reusable, project default, tag en
  executions.metrics. Actualiza 0038 para apuntar a 0040 como duenio
  del UX de profiles.
2026-05-04 22:16:42 +02:00

7.5 KiB

id, title, status, priority, created, depends_on
id title status priority created depends_on
0039 Gestor de cookies y sesiones por profile pending high 2026-05-04
0038

Contexto y objetivo

Una vez que 0038 deja al app lanzar browsers externos con --user-data-dir por profile, las cookies y el localStorage quedan persistidas automaticamente por Chrome/Edge en disco. Este issue cubre la gestion explicita de esas sesiones desde graph_explorer: visualizarlas, exportarlas, importarlas, limpiarlas y monitorizar cuando caducan.

Sin esto, los profiles son cajas negras que solo se manipulan abriendo el browser. Un OSINT serio necesita ver "que sesiones tengo activas, en que dominios, cuando expiran, que hacer si alguna se rompe".

Alcance

1. Inventario de sesiones (CDP read-only)

Panel Sessions dentro o junto al panel Browsers:

┌─ Sessions in profile: linkedin ─────────────────────────┐
│ Domain               Cookies   Auth?   Expires          │
│ linkedin.com           42       Yes    2026-08-12       │
│ www.linkedin.com       18       Yes    session          │
│ static.licdn.com        6       No     2027-01-01       │
│ google-analytics.com    3       No     2026-11-01       │
│                                                          │
│ [Export profile]  [Import .json]  [Clear domain]        │
│ [Clear all cookies]  [Open browser]                     │
└──────────────────────────────────────────────────────────┘

Datos via Network.getAllCookies (CDP). Detectar "Auth?" heuristico: cookie con flag httpOnly=true o nombre matching /sess|auth|token|sid|li_at|c_user/i.

2. Export / import de profiles

  • Export profile<app>/local_files/exports/<profile>-<date>.json con TODAS las cookies + un manifest (browser, version, created_at, domains).
  • Import .json → escribe via Network.setCookie sobre un profile vivo. Si el profile no esta vivo, lanzarlo headless solo para inyectar y cerrarlo.
  • Formato JSON compatible con la extension EditThisCookie (estandar de facto), para que el usuario pueda importar/exportar fuera del app.

3. Limpieza selectiva

Accion Implementacion CDP
Clear all cookies Network.clearBrowserCookies
Clear domain iterar getAllCookies + deleteCookies filtrado
Clear cookie Network.deleteCookies con name+domain
Clear localStorage de un origin Storage.clearDataForOrigin

Antes de cualquier "clear all" → confirmacion modal con el numero de cookies que se van a perder.

4. Health check de sesiones autenticadas

Por profile, opcional, definir en <profile>/.fn_session.yaml:

auth_check:
  - name: linkedin
    url: https://www.linkedin.com/feed/
    success_selector: "main[role='main']"
    redirect_to_login_means_failed: true
  - name: google
    url: https://myaccount.google.com
    success_selector: "[data-email]"

El app puede lanzar un check por sesion (CDP navigate + check selector) y mostrar:

linkedin     ✓ Authenticated     (last check: 2 min ago)
google       ✗ Login expired     (redirected to /accounts/signin)
twitter      ? Never checked     [check now]

Health checks NO son automaticos por defecto — requieren click manual para no quemar sesiones con polls innecesarios. Opcion de "check on launch" por profile.

5. Lock / busy state

Cuando un enricher esta usando un profile (CDP busy), bloquear acciones destructivas (clear, import) en el panel — solo lectura permitida. Visual: candado al lado del profile + tooltip "in use by enricher fetch_webpage_browser".

Lock implementado en graph_explorer.db tabla browser_locks(profile, holder, acquired_at). TTL 60s por si el holder muere sin liberar.

Schema en graph_explorer.db

CREATE TABLE browser_profiles (
    name TEXT PRIMARY KEY,
    browser_path TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    notes TEXT
);

CREATE TABLE browser_locks (
    profile TEXT PRIMARY KEY REFERENCES browser_profiles(name),
    holder TEXT,                  -- enricher_id, panel, etc.
    acquired_at TIMESTAMP,
    expires_at TIMESTAMP
);

CREATE TABLE browser_session_checks (
    id INTEGER PRIMARY KEY,
    profile TEXT REFERENCES browser_profiles(name),
    check_name TEXT,              -- 'linkedin', 'google', ...
    status TEXT,                  -- 'authenticated' | 'expired' | 'error'
    detail TEXT,
    checked_at TIMESTAMP
);

Plan de implementacion

Fase Entregable
0039a cdp-cli cookies list --profile X — JSON con todas las cookies por dominio. Lectura via Network.getAllCookies.
0039b cdp-cli cookies export --profile X --out file.json — formato EditThisCookie.
0039c cdp-cli cookies import --profile X --in file.jsonNetwork.setCookie masivo.
0039d cdp-cli cookies clear --profile X [--domain Y] — clear selectivo.
0039e Panel Sessions en C++: tabla agregada por dominio, botones de export/import/clear.
0039f cdp-cli session-check --profile X --config <file>.yaml — health check con selectores.
0039g UI de health check en panel Sessions: status por sesion, boton "check now".
0039h Locks: implementar browser_locks + UI para mostrar profile busy.
0039i Migrar profiles preexistentes (creados manualmente) — boton "Register existing folder".

Riesgos y mitigaciones

Riesgo Mitigacion
Importar cookies sobreescribe datos validos Dialogo de import con preview + opcion "merge" vs "replace".
Export con cookies de auth = secret en disco local_files/exports/ con permisos 0600 en POSIX. Warning explicito al exportar. Documentar en panel.
Health check quema rate limit del sitio Caching: si ultimo check < 5 min, no re-checkear sin force.
Cookies httpOnly no se ven via JS pero si via CDP Documentar: el panel muestra MAS cookies que las visibles desde DevTools de la pagina.
Diferencias de schema entre Chrome / Edge / Brave CDP es estandar — mismo subset funciona en todos. Tests por browser en CI manual.

Definicion de hecho

  • En el panel Sessions del profile linkedin, veo 42 cookies agrupadas por dominio, con flag de auth detectado y fecha de expiracion.
  • Click en Export profile genera un JSON valido que la extension EditThisCookie puede importar en otro browser.
  • Click en Clear domain: google-analytics.com elimina solo esas 3 cookies sin tocar las demas.
  • Configurando un auth_check para LinkedIn y haciendo click en Check now, el app lanza CDP, navega, evalua el selector y muestra ✓ Authenticated en menos de 4s.
  • Un enricher corriendo sobre linkedin bloquea las acciones destructivas del panel (boton "Clear all" deshabilitado con tooltip).
  • Cerrar manualmente el browser y volver a lanzarlo desde el panel conserva todas las cookies del profile (verificado por el inventario pre/post).

Fuera de alcance

  • Cifrado at-rest del user-data-dir — Chrome ya cifra los secrets con DPAPI (Windows) / Keychain (Mac). En Linux Chrome usa cifrado debil por defecto, pero esta fuera de nuestro control.
  • Sync de profiles entre PCs — los local_files/browser_profiles/ estan gitignorados a proposito (secrets). Si en el futuro hace falta, un export manual + import en el otro PC es la via.
  • Auto-renovacion de tokens (cuando un sitio refresca cookies via refresh_token) — fuera de v1, demasiado especifico por sitio.