fix(security): TrustedHostMiddleware + escape UID/RRULE iCal + tests deterministas del flag
- TrustedHostMiddleware (127.0.0.1/localhost/testserver): anti DNS-rebinding, cierra el vector por el que una web maliciosa alcanza el service local desde el navegador. - _build_vcalendar: sanitiza UID y RRULE (quita saltos de línea) para evitar iCal injection (summary/location/description ya escapaban con _vcard_escape). - tests: fixture autouse que fuerza OSINT_DB_BACKEND OFF por defecto, así la suite es determinista sin depender del estado real de dev/feature_flags.json (los tests del camino ON sobrescriben _FLAGS_FILE). Corrige 14 fallos que aparecían con el flag activado. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,4 +7,4 @@
|
|||||||
"enabled_at": "2026-06-13"
|
"enabled_at": "2026-06-13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+16
-2
@@ -2199,7 +2199,9 @@ def _build_vcalendar(data: "EventIn", uid: str) -> str:
|
|||||||
body.append(vtz)
|
body.append(vtz)
|
||||||
vevent = [
|
vevent = [
|
||||||
"BEGIN:VEVENT",
|
"BEGIN:VEVENT",
|
||||||
"UID:%s" % uid,
|
# Sanitizamos el UID (quitamos saltos de línea) para que no pueda inyectar
|
||||||
|
# propiedades/componentes iCal nuevos en el VEVENT.
|
||||||
|
"UID:%s" % str(uid).replace("\r", "").replace("\n", ""),
|
||||||
"DTSTAMP:%s" % dtstamp,
|
"DTSTAMP:%s" % dtstamp,
|
||||||
_ical_dt_property("DTSTART", data.dtstart, tz, data.all_day),
|
_ical_dt_property("DTSTART", data.dtstart, tz, data.all_day),
|
||||||
]
|
]
|
||||||
@@ -2219,7 +2221,10 @@ def _build_vcalendar(data: "EventIn", uid: str) -> str:
|
|||||||
# canónica "RRULE:<cuerpo>" que entienden Xandikos y los clientes (DAVx5).
|
# canónica "RRULE:<cuerpo>" que entienden Xandikos y los clientes (DAVx5).
|
||||||
if rrule.upper().startswith("RRULE:"):
|
if rrule.upper().startswith("RRULE:"):
|
||||||
rrule = rrule[len("RRULE:"):].strip()
|
rrule = rrule[len("RRULE:"):].strip()
|
||||||
vevent.append("RRULE:%s" % rrule)
|
# Sanitizar: quitar saltos de línea para que el valor de la RRULE no
|
||||||
|
# inyecte propiedades/componentes nuevos (los `;`/`,` son separadores
|
||||||
|
# legítimos de la regla, así que no se escapan).
|
||||||
|
vevent.append("RRULE:%s" % rrule.replace("\r", "").replace("\n", ""))
|
||||||
vevent.append("END:VEVENT")
|
vevent.append("END:VEVENT")
|
||||||
body.append("\r\n".join(vevent))
|
body.append("\r\n".join(vevent))
|
||||||
body.append("END:VCALENDAR")
|
body.append("END:VCALENDAR")
|
||||||
@@ -2240,6 +2245,15 @@ def create_app(vault_dir: str) -> FastAPI:
|
|||||||
"""
|
"""
|
||||||
state = VaultState(vault_dir)
|
state = VaultState(vault_dir)
|
||||||
app = FastAPI(title="osint_web", version="0.1.0")
|
app = FastAPI(title="osint_web", version="0.1.0")
|
||||||
|
# Anti DNS-rebinding: solo acepta requests cuyo Host sea localhost. Cierra el
|
||||||
|
# vector por el que una web maliciosa rebindea su dominio a 127.0.0.1 y, desde
|
||||||
|
# el navegador del usuario, alcanza este service local (sin auth) o el de DuckDB.
|
||||||
|
from starlette.middleware.trustedhost import TrustedHostMiddleware
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
TrustedHostMiddleware,
|
||||||
|
allowed_hosts=["127.0.0.1", "localhost", "testserver"],
|
||||||
|
)
|
||||||
app.state.vault = state
|
app.state.vault = state
|
||||||
|
|
||||||
# -- Vault --
|
# -- Vault --
|
||||||
|
|||||||
@@ -25,6 +25,17 @@ sys.path.insert(0, os.path.join(_HERE, "..", "server"))
|
|||||||
import main as srv # noqa: E402
|
import main as srv # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _flag_off_por_defecto(monkeypatch, tmp_path):
|
||||||
|
"""Por defecto los tests corren con OSINT_DB_BACKEND OFF (camino histórico
|
||||||
|
vault + Xandikos), independientemente del estado real de
|
||||||
|
``dev/feature_flags.json`` en disco. Apunta ``_FLAGS_FILE`` a un archivo
|
||||||
|
inexistente (→ False). Los tests que prueban el camino ON sobrescriben
|
||||||
|
``srv._FLAGS_FILE`` dentro del propio test, ganando sobre este default.
|
||||||
|
"""
|
||||||
|
monkeypatch.setattr(srv, "_FLAGS_FILE", str(tmp_path / "_no_flags.json"))
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Fixtures: vault sintético mínimo
|
# Fixtures: vault sintético mínimo
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user