feat(browser): auto-commit con 60 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
---
|
||||
name: http_replay_sequence
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def http_replay_sequence(calls: list[dict], *, params: dict | None = None, extract: list[dict] | None = None, timeout_s: float = 30.0, verify_tls: bool = True, allow_redirects: bool = True, base_headers: dict | None = None) -> dict"
|
||||
description: "Motor de replay HTTP: ejecuta en orden una secuencia de call specs (las que produce har_extract_calls_py_cybersecurity) compartiendo una sesion (cookie jar) entre pasos, con substitucion de parametros {{param}} y extraccion de valores de una respuesta para usarlos en pasos siguientes (p.ej. token CSRF del GET inicial -> header del POST). Pieza reutilizable del Nivel 1 (HTTP puro) del patron grabar->destilar->reproducir."
|
||||
tags: [flow-replay, http, replay, client, infra]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [re, requests]
|
||||
tested: true
|
||||
tests: ["test_golden_extract_and_subst", "test_edge_missing_param", "test_error_path_request_exception"]
|
||||
test_file_path: "python/functions/infra/http_replay_sequence_test.py"
|
||||
file_path: "python/functions/infra/http_replay_sequence.py"
|
||||
params:
|
||||
- name: calls
|
||||
desc: "Lista ordenada de call specs. Cada spec: {method, url, headers(dict), cookies(dict opc), body(str|None), body_type:'json'|'form'|'raw'|None}. El body ya es texto (no se re-serializa). Es el formato de salida de har_extract_calls_py_cybersecurity."
|
||||
- name: params
|
||||
desc: "Dict inicial de contexto para la substitucion {{param}}. Se copia (no se muta el original). Pasa aqui secretos/tokens desde un vault/pass, nunca hardcodeados en los call specs."
|
||||
- name: extract
|
||||
desc: "Lista de reglas de extraccion {from: int|'last', type: 'json'|'regex'|'header'|'set_cookie', expr: str, as: str}. Se aplican justo tras ejecutar el step indicado en 'from' ('last' = el step recien ejecutado) y guardan el valor en ctx[as] para los pasos siguientes."
|
||||
- name: timeout_s
|
||||
desc: "Timeout por request en segundos (default: 30.0)."
|
||||
- name: verify_tls
|
||||
desc: "Verificar certificados TLS; se setea en la sesion (default: True). No desactivar salvo entorno de pruebas controlado."
|
||||
- name: allow_redirects
|
||||
desc: "Si los requests siguen redirects (default: True)."
|
||||
- name: base_headers
|
||||
desc: "Headers por defecto que se mezclan en la sesion (se aplican a todos los pasos). Util para User-Agent / Accept comunes."
|
||||
output: "Dict {status: 'ok'|'error', steps: [{idx, method, url, status_code, ok, extracted, missing_params, error}], params_final: dict (ctx tras todos los pasos), error: str}. status='error' solo ante excepcion de transporte (requests.RequestException) o entrada invalida; en ese caso corta y deja de ejecutar. Un 4xx/5xx NO corta: el step queda con ok=False y status global sigue 'ok'."
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from infra import http_replay_sequence
|
||||
|
||||
# Requiere red: usa httpbin.org (publico). 2 pasos:
|
||||
# 1) GET /uuid -> extrae el uuid del JSON como param "u"
|
||||
# 2) POST /anything -> manda header X-Token: {{u}} (el uuid del paso 1)
|
||||
calls = [
|
||||
{"method": "GET", "url": "https://httpbin.org/uuid",
|
||||
"headers": {"Accept": "application/json"}, "body": None, "body_type": None},
|
||||
{"method": "POST", "url": "https://httpbin.org/anything",
|
||||
"headers": {"X-Token": "{{u}}", "Content-Type": "application/json"},
|
||||
"body": '{"hello": "world"}', "body_type": "json"},
|
||||
]
|
||||
extract = [
|
||||
{"from": 0, "type": "json", "expr": "uuid", "as": "u"},
|
||||
]
|
||||
|
||||
result = http_replay_sequence(calls, extract=extract)
|
||||
|
||||
print(result["status"]) # "ok"
|
||||
token = result["params_final"]["u"] # el uuid extraido del paso 0
|
||||
print("token:", token)
|
||||
# httpbin /anything devuelve los headers que recibio; comprobamos que el
|
||||
# paso 2 llevo el valor substituido:
|
||||
print(result["steps"][1]["status_code"]) # 200
|
||||
print(result["steps"][1]["ok"]) # True
|
||||
# El header X-Token: {{u}} se substituyo por el uuid antes de enviarse.
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Usala tras `har_extract_calls_py_cybersecurity`, para validar que un flujo capturado se reproduce SIN navegador (Nivel 1 del patron grabar->destilar->reproducir). Es la base de las funciones-accion guardadas en el registry: cuando una secuencia HTTP demuestra reproducir un login + accion, se promueve a una funcion/pipeline dedicada. Tambien sirve para encadenar requests dependientes (token CSRF, session id, paginacion con cursor) compartiendo cookie jar y propagando valores entre pasos.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Seguridad — secretos via params, nunca hardcodeados.** Los call specs pueden contener cookies/tokens. El caller debe inyectarlos via `{{param}}` desde un vault/pass (`params={...}`), no escribirlos en los specs ni commitearlos.
|
||||
- **Seguridad — replay con efectos es PELIGROSO.** Reproducir una secuencia con efectos (POST que reinicia un server, borra, paga, envia) ejecuta esos efectos de verdad. El caller debe confirmar antes de lanzar una secuencia mutante.
|
||||
- **Seguridad — `verify_tls` default True.** No lo pongas en False salvo en un entorno de pruebas controlado; desactivar la verificacion TLS abre la puerta a MITM.
|
||||
- **Extraccion JSON es dot-path simple, NO jsonpath completo.** `"data.items.0.token"` funciona (claves + indices de lista por digito), pero no hay filtros, wildcards ni expresiones. Para casos complejos, usa `type: regex` o post-procesa.
|
||||
- **Sigue redirects por defecto** (`allow_redirects=True`). Si la secuencia capturada depende del 302 explicito (p.ej. para leer el Location o una cookie intermedia), pon `allow_redirects=False`.
|
||||
- **Params faltantes NO abortan.** Si un `{{nombre}}` no esta en ctx, se deja el literal `{{nombre}}` y se anade a `step.missing_params`. El request se envia igual; solo una excepcion de transporte corta la ejecucion.
|
||||
- **El body no se re-serializa.** `body_type: "json"` solo documenta el tipo; el body ya es texto y se manda como `data=body`. Asegurate de incluir el header `Content-Type` adecuado en el spec.
|
||||
- **4xx/5xx no es error global.** El step queda `ok=False` con su `status_code`, pero `status` global sigue `"ok"`. Solo `requests.RequestException` (DNS, conexion, timeout) marca `status="error"` y corta.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
v1.0.0 — version inicial.
|
||||
Reference in New Issue
Block a user