mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-18 14:52:04 +00:00
fix(dashboard): accept HA ingress prefix paths
Allow mainstream reverse-proxy path mounts to keep their X-Forwarded-Prefix when Home Assistant Supervisor ingress already consumes nearly the old 64-character budget. Keep validation bounded and keep rejected non-empty prefixes diagnosable with a deduplicated warning. Constraint: HA Supervisor ingress prefixes are 63 chars before add-on subpaths, so the old 64-char cap dropped valid dashboard deployments. Rejected: remove the length cap entirely | a bounded header budget is still a conservative validation guard. Confidence: high Scope-risk: narrow Directive: Keep prefix validation centralized in hermes_cli.dashboard_auth.prefix so auth routes, cookies, and SPA asset rewriting agree. Tested: python probe for the 73-char HA ingress prefix; scripts/run_tests.sh tests/hermes_cli/test_dashboard_auth_prefix.py -q; .venv/bin/python -m pytest tests/hermes_cli/test_web_server.py -k 'spa_assets_are_read_as_utf8' -q; python -m ruff check hermes_cli/dashboard_auth/prefix.py tests/hermes_cli/test_dashboard_auth_prefix.py; git diff --check Not-tested: full test suite
This commit is contained in:
parent
3b5c645433
commit
ef79ad014d
2 changed files with 88 additions and 1 deletions
|
|
@ -26,6 +26,11 @@ from typing import Optional
|
|||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
# Home Assistant Supervisor ingress prefixes are already 63 chars before
|
||||
# deployments add their own sub-path. Keep a bounded header budget, but leave
|
||||
# room for mainstream reverse-proxy path mounts.
|
||||
_MAX_PREFIX_LENGTH = 256
|
||||
|
||||
# Characters that, if present in a public_url or prefix value, indicate
|
||||
# either a typo or a header-injection attempt. Reject the whole value
|
||||
# rather than try to sanitise — the operator can fix their config.
|
||||
|
|
@ -37,6 +42,7 @@ _REJECT_CHARS = frozenset(('"', "'", "<", ">", " ", "\n", "\r", "\t"))
|
|||
# misconfigured deploy. Keyed on the raw value too, so changing the
|
||||
# config and reloading surfaces a fresh warning.
|
||||
_warned_malformed_public_urls: set = set()
|
||||
_warned_malformed_prefixes: set = set()
|
||||
|
||||
|
||||
def _warn_if_malformed(source: str, raw: str) -> None:
|
||||
|
|
@ -72,6 +78,23 @@ def _warn_if_malformed(source: str, raw: str) -> None:
|
|||
)
|
||||
|
||||
|
||||
def _warn_if_malformed_prefix(raw: Optional[str], reason: str) -> None:
|
||||
"""Warn once when a non-empty X-Forwarded-Prefix value is rejected."""
|
||||
cleaned = raw.strip() if raw else ""
|
||||
if not cleaned:
|
||||
return
|
||||
key = (cleaned, reason)
|
||||
if key in _warned_malformed_prefixes:
|
||||
return
|
||||
_warned_malformed_prefixes.add(key)
|
||||
_log.warning(
|
||||
"X-Forwarded-Prefix header %r was ignored because %s. "
|
||||
"Dashboard URLs will be generated without a reverse-proxy path prefix.",
|
||||
cleaned,
|
||||
reason,
|
||||
)
|
||||
|
||||
|
||||
def normalise_prefix(raw: Optional[str]) -> str:
|
||||
"""Normalise an X-Forwarded-Prefix header value.
|
||||
|
||||
|
|
@ -94,8 +117,16 @@ def normalise_prefix(raw: Optional[str]) -> str:
|
|||
or ".." in p
|
||||
or any(c in p for c in _REJECT_CHARS)
|
||||
):
|
||||
_warn_if_malformed_prefix(
|
||||
raw,
|
||||
"it contains a disallowed character or path sequence",
|
||||
)
|
||||
return ""
|
||||
if len(p) > 64:
|
||||
if len(p) > _MAX_PREFIX_LENGTH:
|
||||
_warn_if_malformed_prefix(
|
||||
raw,
|
||||
f"it is longer than {_MAX_PREFIX_LENGTH} characters",
|
||||
)
|
||||
return ""
|
||||
return p
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue