fix: skip Authorization header when federation key is empty
Empty remote_instances[].key produced 'Bearer ' (illegal header value) causing httpx to reject the request entirely. Fix: only include the Authorization header when the key is non-empty. Remotes without keys still work if their auth allows it (e.g., localhost bypass). Fixes all 5 sites: fetch_remote GET, connect POST, bell/clear POST, create-session POST, and WebSocket proxy additional_headers.
This commit is contained in:
+7
-5
@@ -760,7 +760,9 @@ async def federation_terminal_ws_proxy(websocket: WebSocket, remote_id: int) ->
|
||||
async with websockets.connect(
|
||||
ws_url,
|
||||
subprotocols=[Subprotocol("tty")],
|
||||
additional_headers={"Authorization": f"Bearer {remote_key}"},
|
||||
additional_headers={"Authorization": f"Bearer {remote_key}"}
|
||||
if remote_key
|
||||
else {},
|
||||
) as remote_ws:
|
||||
|
||||
async def client_to_remote() -> None:
|
||||
@@ -926,7 +928,7 @@ async def federation_sessions(request: Request) -> list[dict]:
|
||||
try:
|
||||
resp = await http_client.get(
|
||||
f"{url.rstrip('/')}/api/sessions",
|
||||
headers={"Authorization": f"Bearer {key}"},
|
||||
headers={"Authorization": f"Bearer {key}"} if key else {},
|
||||
)
|
||||
if resp.status_code in (401, 403):
|
||||
return [
|
||||
@@ -1026,7 +1028,7 @@ async def federation_connect(
|
||||
try:
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
headers={"Authorization": f"Bearer {remote_key}"},
|
||||
headers={"Authorization": f"Bearer {remote_key}"} if remote_key else {},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
@@ -1072,7 +1074,7 @@ async def federation_bell_clear(
|
||||
try:
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
headers={"Authorization": f"Bearer {remote_key}"},
|
||||
headers={"Authorization": f"Bearer {remote_key}"} if remote_key else {},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
@@ -1119,7 +1121,7 @@ async def federation_create_session(
|
||||
try:
|
||||
resp = await http_client.post(
|
||||
url,
|
||||
headers={"Authorization": f"Bearer {remote_key}"},
|
||||
headers={"Authorization": f"Bearer {remote_key}"} if remote_key else {},
|
||||
json={"name": payload.name},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
@@ -2624,3 +2624,57 @@ def test_federation_create_session_returns_502_when_remote_returns_error(
|
||||
|
||||
response = client.post("/api/federation/0/sessions", json={"name": "new-session"})
|
||||
assert response.status_code == 502
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Federation Authorization header safety — guard against empty key
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_federation_auth_headers_guard_empty_key():
|
||||
"""Every federation Authorization header construction must guard against empty key.
|
||||
|
||||
An empty remote_instances[].key produces 'Bearer ' (trailing space, empty
|
||||
token). httpx rejects that with "Illegal header value b'Bearer '" which
|
||||
silently makes the remote appear unreachable.
|
||||
|
||||
The fix: every site that constructs the header must use the pattern
|
||||
headers={"Authorization": f"Bearer {key}"} if key else {}
|
||||
or an equivalent conditional, so an empty key simply omits the header.
|
||||
|
||||
This is a source-inspection test — it catches regressions without spinning
|
||||
up a live server.
|
||||
"""
|
||||
import inspect
|
||||
|
||||
import muxplex.main as main_mod
|
||||
|
||||
source = inspect.getsource(main_mod)
|
||||
|
||||
# Collect every line that constructs an Authorization Bearer header (not
|
||||
# comment or docstring lines — we only care about executable code lines).
|
||||
offending: list[str] = []
|
||||
for line in source.splitlines():
|
||||
stripped = line.strip()
|
||||
# Skip comment and docstring lines
|
||||
if (
|
||||
stripped.startswith("#")
|
||||
or stripped.startswith('"""')
|
||||
or stripped.startswith("'\"'\"'")
|
||||
):
|
||||
continue
|
||||
# Match lines that build the header dict value
|
||||
if (
|
||||
'"Authorization"' in stripped
|
||||
and "Bearer" in stripped
|
||||
and 'f"Bearer' in stripped
|
||||
):
|
||||
# Must have an inline `if` guard — e.g. `{...} if key else {}`
|
||||
if " if " not in stripped:
|
||||
offending.append(stripped)
|
||||
|
||||
assert not offending, (
|
||||
"Unguarded federation Bearer header(s) found in main.py — "
|
||||
"use `{...} if key else {}` to skip the header when key is empty:\n"
|
||||
+ "\n".join(f" {line}" for line in offending)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user