fix(security): CORS preflight echoes only same-origin or allowlisted origins

do_OPTIONS answered every CORS preflight with Access-Control-Allow-Origin: *,
advertising broader cross-origin access than a real request is granted — on a
password-less deployment that lets any site read authenticated responses. It now
echoes the request Origin only when _check_same_origin_browser_request approves
it (the exact same-origin / HERMES_WEBUI_ALLOWED_ORIGINS policy the CSRF gate
enforces for real requests), adds Vary: Origin, and never emits *. A disallowed
origin gets a 200 with no CORS headers (browser treats as preflight denial).
Default deployments (no allowlist) are unaffected.

Contributor stage; gate-pass. Co-authored-by: mo7al876any <mo7al876any@users.noreply.github.com>
This commit is contained in:
nesquena-hermes
2026-07-05 02:18:51 +00:00
parent 49a9560c45
commit 4e8978f04c
3 changed files with 133 additions and 5 deletions
+16
View File
@@ -5127,6 +5127,22 @@ def _check_same_origin_browser_request(handler, *, require_provenance: bool = Fa
return True
def preflight_allow_origin(handler) -> str:
"""CORS preflight: return the Origin value to echo back, or '' to omit.
Echo the request Origin only when it is same-origin or explicitly
allowlisted via HERMES_WEBUI_ALLOWED_ORIGINS the exact policy the CSRF
gate enforces for real requests. Reuses _check_same_origin_browser_request
so the preflight can never advertise wider access (`*`) than an actual
request would be granted. A wildcard here would let any site read
authenticated responses on a deployment with no password set.
"""
origin = handler.headers.get("Origin", "").strip()
if not origin:
return ""
return origin if _check_same_origin_browser_request(handler) else ""
def _csrf_exempt_path(path: str) -> bool:
"""Paths that cannot or must not carry a session CSRF token."""
return path in {