gate fixes: opt-in pipe separator (#7331) + IPv6-safe Origin (#6772)

Two Codex adversarial-security-gate findings on the batch:

1. #7331 CORE (backward-compat break): making '|' an unconditional trusted-groups
   separator would re-split an existing deployment whose group NAME legitimately
   contains a literal '|' (e.g. 'unprivileged|admins' read as one group), silently
   changing its profile binding. Pipe splitting is now OPT-IN via
   HERMES_WEBUI_TRUSTED_GROUPS_PIPE_SEPARATOR; comma/newline stay the default.
   Tests: literal-pipe preserved by default, pipe splits only with the flag set.

2. #6772 follow-up (my own hardening introduced it): rebuilding the origin from
   parsed parts dropped the brackets around an IPv6 host, so 'http://[::1]:8787'
   became 'http://::1:8787' and never matched the browser's bracketed
   clientDataJSON.origin -> passkey login over IPv6 would fail. Re-bracket IPv6
   literals. Added rp_context regression tests (Origin-first, port preserved,
   IPv6 bracketed, malformed->Host fallback, userinfo stripped).
This commit is contained in:
n committed 2026-09-08 01:53:01 +00:00
1 parent 2e62c4f896
commit e7841a5988
4 files changed
+136 -17

No files matched your search

+5 -1
View File
@@ -208,7 +208,11 @@ def rp_context(handler) -> tuple[str, str]:
from urllib.parse import urlparse
parsed = urlparse(browser_origin.strip())
if parsed.scheme in ("http", "https") and parsed.hostname:
netloc = parsed.hostname if parsed.port is None else f"{parsed.hostname}:{parsed.port}"
# Re-bracket IPv6 literals: parsed.hostname strips the [] that the
# browser's clientDataJSON.origin carries, so an unbracketed
# "http://::1:8787" would never match the stored origin.
host_part = f"[{parsed.hostname}]" if ":" in parsed.hostname else parsed.hostname
netloc = host_part if parsed.port is None else f"{host_part}:{parsed.port}"
return parsed.hostname, f"{parsed.scheme}://{netloc}"
except Exception:
pass