# 0037 — IoC regex extractor (cybersecurity)

## Metadata

| Campo | Valor |
|-------|-------|
| **ID** | 0037 |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | feature — Python (`python/functions/cybersecurity/`) |

## Dependencias

Ninguna. Es la pieza de mayor ROI del plan: regex puros, sin modelos, 100% precision para IoCs tecnicos. Complementa (no sustituye) a GLiNER, que es malo para identificadores tecnicos.

**Desbloquea:** mejor precision del pipeline hibrido (0040). Permite filtrar IoCs antes del paso LLM/NER en cualquier flujo OSINT.

---

## Objetivo

Familia de funciones puras que extraen IoCs (Indicators of Compromise) de texto via regex. Cada una retorna `list[dict]` con `value`, `start`, `end` y `type` para que el caller pueda construir `EntityCandidate` o `triple` sin reparsing.

## Funciones a crear

| Function ID | Que extrae |
|---|---|
| `extract_iocs_py_cybersecurity` | Pipeline pure: corre todos los extractors abajo y devuelve lista unificada con `type` |
| `extract_ip_addresses_py_cybersecurity` | IPv4 + IPv6, valida rangos (no `999.999.999.999`) |
| `extract_emails_py_cybersecurity` | RFC 5322 simplificado |
| `extract_domains_py_cybersecurity` | FQDNs con TLD valido (lista compilada) |
| `extract_file_hashes_py_cybersecurity` | MD5 (32 hex), SHA1 (40), SHA256 (64), SHA512 (128) — devuelve `algorithm` |
| `extract_crypto_wallets_py_cybersecurity` | BTC (legacy + bech32), ETH (0x + 40 hex con checksum opcional) |
| `extract_cve_ids_py_cybersecurity` | `CVE-YYYY-NNNN+` |
| `extract_mac_addresses_py_cybersecurity` | `xx:xx:xx:xx:xx:xx` y `xx-xx-xx-xx-xx-xx` |
| `extract_phone_numbers_py_cybersecurity` | E.164 + formatos comunes ES/EU |

`extract_urls_py_cybersecurity` ya existe — no duplicar.

## Contrato

```python
def extract_<ioc_type>(text: str) -> list[dict]:
    """
    Returns:
        [{"value": str, "start": int, "end": int, "type": "<ioc_type>"}, ...]
    """
```

`extract_iocs_py_cybersecurity` (pipeline) compone los anteriores y unifica resultados:
```python
def extract_iocs(
    text: str,
    types: list[str] | None = None,  # None = todos
) -> list[dict]
```

## Pureza

Todas `purity: pure`. Solo regex compilado y validacion estructural. Sin red, sin disco, deterministas.

## Deliverables

- 9 archivos `.py` + 9 `.md` en `python/functions/cybersecurity/`
- 1 archivo `.py` adicional para el pipeline `extract_iocs.py`
- Tests unitarios en `python/functions/cybersecurity/tests/test_extract_iocs.py` con corpus pequeño de positivos y negativos por tipo
- Frontmatter completo: `params`, `output`, `domain: cybersecurity`, `purity: pure`

## Validacion

```bash
./fn run extract_iocs_py_cybersecurity      # corre tests
./fn show extract_iocs_py_cybersecurity     # verifica frontmatter
```

Bench informal: 1 MB de texto < 100 ms por extractor en CPU.

## Notas

- No validar TLDs con DNS — solo lista estatica de TLDs validos (publicsuffix opcional pero no obligatorio).
- Devolver offsets `start`/`end` siempre — los necesitan los issues 0038-0040 para reconciliar con spans de GLiNER.
- IPs privadas (10.x, 192.168.x) se extraen igual; el filtrado de relevancia es del caller, no del extractor.
