Skip to content

Crear un check nuevo

Añadir un check a RedBench requiere dos archivos en la carpeta de la plataforma correspondiente.

Paso 1: Definición YAML

Crea un archivo .yml en la carpeta correcta. Ejemplo para un check de Magento 2:

# Archivo: src/redbench/checks/magento2/cron_exposed.yml
id: RB-M2-004
name: Cron endpoint publicly accessible
description: >
  Checks if the Magento 2 cron.php endpoint is accessible from the
  outside, which can be abused to trigger scheduled jobs.
platform: magento2
type: posture
severity: medium
safe_modes:
  - passive
  - safe-active
tags:
  - cron
  - hardening

Naming

El nombre del archivo YAML debe coincidir con el nombre del archivo Python (sin extensión). Si el YAML se llama cron_exposed.yml, el Python debe ser cron_exposed.py.

Paso 2: Lógica Python

Crea el .py con la función run():

# Archivo: src/redbench/checks/magento2/cron_exposed.py

"""RB-M2-004 — Cron endpoint publicly accessible."""

from __future__ import annotations

import httpx

from redbench.core.evidence import from_url
from redbench.core.models import (
    CheckDef, CheckResult, ExecMode, Finding, Fingerprint,
)


async def run(
    check: CheckDef,
    target: str,
    domain: str,
    fingerprint: Fingerprint | None = None,
    mode: ExecMode = ExecMode.PASSIVE,
) -> Finding:
    base = f"https://{domain}" if not domain.startswith("http") else domain
    url = base.rstrip("/") + "/cron.php"

    async with httpx.AsyncClient(
        timeout=10.0, follow_redirects=False, verify=False,
        headers={"User-Agent": "Mozilla/5.0 (compatible; RedBench/0.1)"},
    ) as c:
        try:
            r = await c.get(url)
        except httpx.HTTPError:
            return Finding(
                check_id=check.id, target=target, domain=domain,
                result=CheckResult.INCONCLUSIVE, severity=check.severity,
                title=check.name, detail="Could not connect.",
            )

    if r.status_code == 200:
        return Finding(
            check_id=check.id, target=target, domain=domain,
            result=CheckResult.AFFECTED, severity=check.severity,
            title=check.name,
            detail="cron.php is publicly accessible.",
            evidence=[from_url(url, r.status_code, r.text[:200])],
            remediation="Block access to cron.php from the web server. Use CLI cron instead.",
        )

    return Finding(
        check_id=check.id, target=target, domain=domain,
        result=CheckResult.NOT_AFFECTED, severity=check.severity,
        title=check.name, detail="cron.php not accessible.",
    )

Paso 3: Probar

redbench scan micliente -p magento2 --report test.html

El nuevo check aparecerá automáticamente. No hay que registrarlo ni modificar ningún otro archivo.

Helpers de evidencia

El módulo redbench.core.evidence tiene helpers para crear evidencia tipada:

from redbench.core.evidence import from_url, from_header, from_text, from_version

# URL + status + snippet
from_url("https://example.com/admin", 200, "Login page HTML...")

# Header capturado
from_header("X-Powered-By", "PHP/7.4")

# Texto libre
from_text("finding", "Admin path is /custom-admin")

# Versión detectada vs esperada
from_version("Magento", "2.4.3", "2.4.7-p3")

Cada evidencia se hashea automáticamente con SHA-256 para reproducibilidad.

Checklist para un buen check

  • [ ] ID único siguiendo convención (RB-{PLAT}-{NUM})
  • [ ] safe_modes correctos (no marques como passive algo que modifica estado)
  • [ ] Siempre devuelve un Finding (aunque sea NOT_AFFECTED)
  • [ ] Evidencia concreta (URLs, headers, snippets) — no solo "encontrado"
  • [ ] Remediación accionable cuando el resultado es AFFECTED
  • [ ] Timeout en las peticiones HTTP
  • [ ] Manejo de errores de conexión → INCONCLUSIVE, no crash