fix: send WS JWT as bare subprotocol name; CSSOM inline styles; open mgmt services on public zone
- websocket.js passes the raw JWT as the Sec-WebSocket-Protocol subprotocol (no 'Bearer ' prefix): subprotocol names must be valid RFC 6455 tokens, and the space in 'Bearer <token>' made the browser reject the constructor with a SyntaxError. - daemon accepts a JWT-shaped subprotocol plus the legacy 'Bearer <token>' form via _extract_ws_token; unit tests in tests/test_ws_auth.py. - vdom.js applies inline styles through el.style (CSSOM) instead of setAttribute, which the management-domain CSP (no 'unsafe-inline') blocks. - install.sh opens http/https/ssh on the public zone alongside WAN setup. - docs (hoover.md, security.md) updated to match.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
"""Tests for WebSocket subprotocol token extraction (daemon.server)."""
|
||||
|
||||
import pytest
|
||||
|
||||
from daemon.server import _extract_ws_token
|
||||
|
||||
# Realistic-looking access token (base64url header.payload.signature).
|
||||
TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.Y3WNO0CTxb4dlcVCPjUnQi"
|
||||
|
||||
|
||||
class TestExtractWsToken:
|
||||
def test_raw_jwt_subprotocol(self):
|
||||
"""Bundled client path: the JWT is sent as the subprotocol name itself."""
|
||||
assert _extract_ws_token([TOKEN]) == (TOKEN, TOKEN)
|
||||
|
||||
def test_raw_jwt_among_other_subprotocols(self):
|
||||
assert _extract_ws_token(["vacuum-wall", TOKEN]) == (TOKEN, TOKEN)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"raw",
|
||||
["none", "vacuum-wall", "a.b", "a.b.c.d"],
|
||||
)
|
||||
def test_no_auth_subprotocol(self, raw):
|
||||
assert _extract_ws_token([raw]) == (None, None)
|
||||
|
||||
def test_empty_list(self):
|
||||
assert _extract_ws_token([]) == (None, None)
|
||||
|
||||
def test_bearer_prefix_form(self):
|
||||
"""Legacy non-browser form: 'Bearer <token>' subprotocol."""
|
||||
assert _extract_ws_token([f"Bearer {TOKEN}"]) == (TOKEN, f"Bearer {TOKEN}")
|
||||
|
||||
def test_bearer_with_non_jwt_token(self):
|
||||
assert _extract_ws_token(["Bearer abc123"]) == ("abc123", "Bearer abc123")
|
||||
|
||||
def test_bearer_without_token_ignored(self):
|
||||
assert _extract_ws_token(["Bearer ", "Bearer"]) == (None, None)
|
||||
|
||||
def test_jwt_like_name_preferred_over_bearer(self):
|
||||
"""A JWT-shaped subprotocol wins even when listed after a Bearer one."""
|
||||
result = _extract_ws_token([f"Bearer {TOKEN}", TOKEN])
|
||||
assert result == (TOKEN, TOKEN)
|
||||
|
||||
def test_subprotocol_with_space_rejected(self):
|
||||
"""A space is not an RFC 6455 token character — 'Bearer <token>' must
|
||||
go through the Bearer branch, never the JWT-shape branch."""
|
||||
assert _extract_ws_token(["a b.c.d"]) == (None, None)
|
||||
Reference in New Issue
Block a user