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:
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¶
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_modescorrectos (no marques comopassivealgo que modifica estado) - [ ] Siempre devuelve un
Finding(aunque seaNOT_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