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

186 lines
7.5 KiB
Markdown

---
id: 0039
title: Gestor de cookies y sesiones por profile
status: pending
priority: high
created: 2026-05-04
depends_on: [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`:
```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`
```sql
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.json``Network.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.