4bd4c374fd
- 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.
48 lines
1.8 KiB
Python
48 lines
1.8 KiB
Python
"""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)
|