- 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.
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 |
|
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>.jsoncon TODAS las cookies + un manifest (browser,version,created_at,domains). - Import .json → escribe via
Network.setCookiesobre 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.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_checkpara LinkedIn y haciendo click en Check now, el app lanza CDP, navega, evalua el selector y muestra✓ Authenticateden menos de 4s. - Un enricher corriendo sobre
linkedinbloquea 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.