---
name: har_filter_flows
kind: function
lang: py
domain: cybersecurity
version: "1.0.0"
purity: pure
signature: "def har_filter_flows(har: dict, *, hosts: list[str] | None = None, methods: list[str] | None = None, drop_static: bool = True, drop_analytics: bool = True) -> list[dict]"
description: "Filtra un HAR (formato W3C, el que exporta query_mitm_flows --har) dejando solo los flujos relevantes para reconstruir una accion HTTP: descarta recursos estaticos y dominios de analytics, y restringe por host/metodo. Primer paso del patron grabar->destilar->reproducir."
tags: [flow-replay, har, proxy, cybersecurity, web-proxy]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [urllib.parse]
params:
  - name: har
    desc: "HAR ya parseado como dict (formato W3C). Se leen los entries de har['log']['entries']; si la estructura no existe devuelve []"
  - name: hosts
    desc: "lista de hosts a conservar (match exacto del host de cada URL). None = no filtra por host"
  - name: methods
    desc: "lista de metodos HTTP a conservar (GET/POST/...). Se normaliza a mayusculas en ambos lados. None = no filtra por metodo"
  - name: drop_static
    desc: "si True (default) descarta recursos estaticos: mimeType image/*, font/*, text/css, application|text/javascript, o ruta terminada en .css .js .mjs .map .png .jpg .jpeg .gif .svg .webp .ico .woff .woff2 .ttf .eot"
  - name: drop_analytics
    desc: "si True (default) descarta entries cuyo host caiga en la blocklist heuristica de telemetria (google-analytics, googletagmanager, doubleclick, sentry.io, segment, mixpanel, hotjar, datadoghq, etc.) por substring sobre el host"
output: "lista de dicts: subconjunto de los entries HAR de entrada (sin mutarlos) que pasan todos los filtros, en su orden original"
tested: true
tests: ["test_golden_solo_sobrevive_el_post_de_api", "test_har_vacio_devuelve_lista_vacia", "test_har_sin_log_entries_devuelve_lista_vacia", "test_filtro_por_hosts", "test_filtro_por_methods"]
test_file_path: "python/functions/cybersecurity/har_filter_flows_test.py"
file_path: "python/functions/cybersecurity/har_filter_flows.py"
---

## Ejemplo

```python
har = {
    "log": {
        "entries": [
            {  # estatico: CSS -> se descarta
                "request": {"method": "GET", "url": "https://app.example.com/styles/main.css"},
                "response": {"content": {"mimeType": "text/css"}},
            },
            {  # analytics -> se descarta
                "request": {"method": "POST", "url": "https://www.google-analytics.com/collect"},
                "response": {"content": {"mimeType": "application/json"}},
            },
            {  # el POST de API que queremos reproducir -> sobrevive
                "request": {"method": "POST", "url": "https://api.example.com/v1/login"},
                "response": {"content": {"mimeType": "application/json"}},
            },
        ]
    }
}

flows = har_filter_flows(har)
print(len(flows))                       # 1
print(flows[0]["request"]["url"])       # https://api.example.com/v1/login

# Restringir aun mas al host y metodo de interes:
flows = har_filter_flows(har, hosts=["api.example.com"], methods=["POST"])
print(len(flows))                       # 1
```

## Cuando usarla

- Justo despues de capturar trafico con `web_proxy` / `query_mitm_flows --har`, cuando quieres **destilar** un HAR ruidoso a los pocos flujos que componen una accion (login, alta, transferencia) antes de convertirla en una funcion reproducible del registry.
- Cuando necesitas quedarte solo con las llamadas de API (`hosts=[...]`, `methods=["POST","PUT"]`) y descartar de un golpe estaticos y telemetria.
- Como primer paso del patron **grabar -> destilar -> reproducir** un flujo web: graba con el proxy, filtra con esta funcion, y reproduce los entries resultantes.

## Gotchas

- Funcion pura: no hace I/O. Recibe el HAR ya parseado (carga el `.har` con `json.load` antes de llamarla).
- La blocklist de analytics es **heuristica y ampliable** (substring sobre el host). Si un dominio de telemetria propio no esta en la lista, no se descarta; pasa `drop_analytics=False` y filtra a mano, o amplia la blocklist en el codigo.
- El filtro `hosts` es **match exacto de host** (no substring, no subdominios): `api.example.com` no captura `www.api.example.com`. Lista cada host que quieras conservar.
- El host se obtiene con `urllib.parse.urlsplit(...).hostname`; URLs sin host valido cuentan como host vacio `""`.
