fix(security): TrustedHostMiddleware (anti DNS-rebinding) + escape iCal en _build_vcalendar

- TrustedHostMiddleware (allowed_hosts 127.0.0.1/localhost/testserver): cierra el vector
  por el que una web maliciosa rebindea su dominio a 127.0.0.1 y alcanza /api/query desde
  el navegador del usuario (el service no tiene auth por ser local).
- _build_vcalendar escapaba nada: UID/SUMMARY/LOCATION/RRULE crudos permitían iCal
  injection. Ahora _ical_escape (summary/location) + _ical_sanitize (uid/rrule, quita
  saltos de línea sin tocar los separadores legítimos de la regla).

Auditoría de seguridad: el fallo CRÍTICO (LFI/escritura via /api/query) se cierra con el
sandbox de duckdb_query_readonly en el registry; este commit cubre los hallazgos ALTA
(DNS-rebinding) y MEDIA (iCal injection).
This commit is contained in:
2026-06-13 01:21:01 +02:00
parent 9cb2170262
commit 77728cda59
2 changed files with 40 additions and 6 deletions
+10
View File
@@ -154,6 +154,16 @@ class CalendarBody(BaseModel):
def create_app(cfg: Config) -> FastAPI:
"""Construye la app FastAPI con la configuración dada (inyectable en tests)."""
app = FastAPI(title="osint_db", docs_url=None, redoc_url=None)
# Anti DNS-rebinding: solo acepta requests cuyo Host sea localhost. Sin esto, una
# web maliciosa podría rebindear su dominio a 127.0.0.1 y, desde el navegador del
# usuario, alcanzar este service (que no tiene auth por ser de uso local) y abusar
# de /api/query. "testserver" permite el TestClient de los tests.
from starlette.middleware.trustedhost import TrustedHostMiddleware
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["127.0.0.1", "localhost", "testserver"],
)
def run_readonly(sql: str, params: list, max_rows: int) -> dict:
"""Ejecuta un SELECT con la conexión read_only del registry, acotado."""