Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.9 KiB
Flow Replay — Guardar un flujo web como función reproducible
Tag: flow-replay. Grupo de funciones para convertir un flujo de navegador que se hizo
una vez a mano (login en un panel, reiniciar un servidor, rellenar un formulario) en una
función del registry reproducible sin intervención. Materializa la doctrina del issue
0087: el registry crece promoviendo secuencias repetidas a operaciones de un solo paso.
Filtro MCP: mcp__registry__fn_search query="" tag="flow-replay".
Complementa al grupo web-proxy: web-proxy graba el tráfico,
flow-replay lo destila y reproduce.
El patrón: grabar → destilar → reproducir
Tres fases, con una jerarquía de reproducción de más barato a más caro:
Fase 0 — GRABAR (una vez, siempre con browser + proxy)
web_proxy ON → haces la acción a mano en el navegador → exportas el tramo a HAR
(funciones del grupo web-proxy: start_mitm_capture, launch_chromium_proxy, query_mitm_flows --har)
Fase 1 — DESTILAR (del HAR a una secuencia de requests)
har_filter_flows → descarta estáticos/analytics, deja los flujos que importan
har_extract_calls → normaliza cada flujo a una "call spec" reproducible (método, url,
headers, cookies, body), aislando los datos de auth
Fase 2 — REPRODUCIR, en orden de preferencia:
Nivel 1 HTTP puro http_replay_sequence — rápido, headless, scriptable. PREFERIDO.
Nivel 2 headless chromium (fallback) — cuando hay token dinámico firmado en cliente,
challenge JS o WAF con fingerprint de navegador. Reutiliza
cdp_extract_recipe + cdp_save_storage_state (ver Fronteras).
Nivel 3 chromium visible + acciones humanizadas — último recurso si headless es detectado
(cdp_click_xy_human, cdp_move_mouse_human del dominio browser).
La función-acción concreta que guardas en el registry (reboot_<panel>_server,
login_<panel>, etc.) envuelve el nivel que funcione: idealmente una llamada a
http_replay_sequence con su secuencia + parámetros, y los secretos resueltos desde
pass/vault.
Funciones del grupo
| ID | Firma corta | Qué hace |
|---|---|---|
| har_filter_flows_py_cybersecurity | har_filter_flows(har, *, hosts, methods, drop_static, drop_analytics) -> list[dict] |
Filtra un HAR: descarta recursos estáticos y hosts de telemetría, deja los flujos candidatos a "acción". Pura. |
| har_extract_calls_py_cybersecurity | har_extract_calls(entries, *, drop_headers) -> list[dict] |
Convierte entries HAR en "call specs" normalizadas (método/url/headers/cookies/body/body_type), aislando cookies de auth y descartando headers hop-by-hop. Pura. |
| http_replay_sequence_py_infra | http_replay_sequence(calls, *, params, extract, timeout_s, verify_tls, allow_redirects, base_headers) -> dict |
Motor de replay HTTP: ejecuta la secuencia compartiendo cookie jar, substituye {{param}} y extrae valores de una respuesta para inyectarlos en pasos siguientes (flujo CSRF-like). Impura. |
Ejemplo canónico end-to-end
Destilar un HAR capturado y reproducir el flujo sin navegador. Las tres funciones se encadenan; la extracción del paso 1 (un token) se inyecta en el paso 2:
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from cybersecurity.har_filter_flows import har_filter_flows
from cybersecurity.har_extract_calls import har_extract_calls
from infra.http_replay_sequence import http_replay_sequence
# 1. HAR exportado por: query_mitm_flows ~/captures/traffic-*.mitm --har ~/sesion.har
import json
har = json.load(open(os.path.expanduser("~/sesion.har")))
# 2. Destilar: del ruido a la secuencia mínima
flows = har_filter_flows(har, hosts=["panel.midominio.com"]) # solo el host del panel
calls = har_extract_calls(flows) # call specs reproducibles
# 3. Reproducir (Nivel 1, HTTP puro). El token del GET inicial se inyecta en el POST.
res = http_replay_sequence(
calls,
params={"server_id": "vps-42"}, # parametrizado por el caller
extract=[{"from": 0, "type": "json", "expr": "csrf", "as": "csrf"}],
verify_tls=True,
)
print(res["status"], [s["status_code"] for s in res["steps"]])
Una vez validado, el flujo se promueve a una función-acción nombrada del registry
(p. ej. reboot_vps_server_<panel>) que internamente llama a http_replay_sequence
con su secuencia fija, recibe los parámetros del caller y resuelve los secretos desde
pass. Esa función-acción es lo que el agente invoca en un solo paso a partir de entonces.
Fronteras
- No graba: la captura es del grupo
web-proxy. Este grupo empieza con un HAR ya existente. - No auto-parametriza (todavía).
har_extract_callsnormaliza pero NO detecta solo qué valor es un token dinámico ni dónde se reinyecta. La parametrización ({{param}}) y las reglas deextractlas decide el humano/agente leyendo el HAR. La detección automática de tokens/CSRF sería una función nueva del grupo, no una ampliación. - No incluye el runner de Nivel 2/3 (browser fallback). Está especificado en el
patrón pero no implementado: cuando un flujo real falle en HTTP puro, se construye un
"action recipe" reutilizando casi entero
cdp_extract_recipe_py_pipelines(mismo formato YAML, steps de acción en vez de extracción) +cdp_save_storage_state_go_browserpara saltarse el login. No se construye por adelantado (KISS / registry-first). - No gestiona secretos: los secretos viajan como
{{param}}desdepass/vault. El grupo nunca los hardcodea ni los persiste.
Gotchas (seguridad — leer antes de usar)
- El HAR es sensible: contiene cookies y tokens en crudo. Trátalo como un secreto —
gitignored, no subir a Gitea, no indexar, borrar tras destilar. El output de
har_extract_callstambién lleva esos valores hasta que los sustituyes por{{param}}. - Secretos a
pass/vault, nunca en el código de la función-acción. - Replay con efectos = peligroso: reproducir un POST que reinicia, borra o paga es
destructivo. La función-acción debe pedir confirmación o exponer un flag explícito
(
--yes/confirm=True) antes de disparar. Nunca replay ciego de una acción irreversible. - HTTP puro no siempre reproduce: token firmado en cliente, challenge JS, o WAF que exige fingerprint de navegador → cae a Nivel 2 (headless) o 3 (visible humanizado).
http_replay_sequencesigue redirects por defecto yverify_tls=True. La extracción JSON es dot-path simple (a.b.0.c), no JSONPath completo.
Prerequisitos
- Fase 0 (grabar): grupo
web-proxyoperativo (mitmproxy + chromium). Ver su página. - Fase 1-2:
requestsenpython/.venv(ya presente). Sin dependencias nuevas.