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.
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user