Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 82710c6457 | |||
| 9b9a2b5c9f | |||
| b023d72165 |
+5
-3
@@ -1,8 +1,10 @@
|
||||
## v0.2.7 (2026-05-19)
|
||||
## v0.3.1 (2026-05-25)
|
||||
|
||||
Hotfix for the v0.3.0 production-readiness release: the new WebSocket Origin allow-list rejected same-origin connections from any LAN IP, breaking the Web UI on `host: 0.0.0.0` deployments unless `cors_origins` was explicitly configured.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Display tab sliders + accent picker now respond to clicks/drags:** Both the brightness and contrast sliders on the Display tab, and the accent-color picker in the header, were rendering dynamic HTML with inline `oninput` / `onchange` / `onclick` attributes — every one of which the server's strict `script-src 'self'` CSP silently dropped. The result: brightness and contrast couldn't be changed from the WebUI at all, and picking a custom accent did nothing. Replaced the inline attributes with `data-*` markers and wired proper `addEventListener` calls (delegated on the slider container, direct on the accent dropdown), so the controls work under the strict CSP without any `unsafe-inline` / `unsafe-hashes` relaxation. ([e1c8474](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/e1c8474))
|
||||
- **WebSocket Origin check now accepts same-origin connections.** When `cors_origins` is unset, the default allow-list was hard-coded to `http://localhost:<port>` + `http://127.0.0.1:<port>`, so a browser opening the UI via the LAN IP (e.g. `http://192.168.2.100:8765`) had its WebSocket closed with code 4003 ("Origin not allowed") and never recovered. The endpoint now also accepts any `Origin` whose authority matches the request's `Host` header (with either `http://` or `https://` scheme) — same-origin connections are by definition not CSWSH, so the cross-origin defence introduced in v0.3.0 is preserved. ([9b9a2b5](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/9b9a2b5))
|
||||
|
||||
---
|
||||
|
||||
@@ -11,6 +13,6 @@
|
||||
|
||||
| Hash | Message | Author |
|
||||
|------|---------|--------|
|
||||
| [e1c8474](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/e1c8474) | fix(csp): wire display sliders and accent picker without inline on* | alexei.dolgolyov |
|
||||
| [9b9a2b5](https://git.dolgolyov-family.by/alexei.dolgolyov/media-player-server/commit/9b9a2b5) | fix(ws): accept same-origin WebSocket connections in default Origin allow-list | alexei.dolgolyov |
|
||||
|
||||
</details>
|
||||
|
||||
@@ -414,8 +414,13 @@ async def websocket_endpoint(
|
||||
accept_subprotocol = proto
|
||||
break
|
||||
effective_token = subprotocol_token or token
|
||||
# Origin check — block CSWSH from third-party LAN pages. We accept the same
|
||||
# set of origins as CORS plus the default localhost loopback.
|
||||
# Origin check — block CSWSH from third-party LAN pages. Accept the same
|
||||
# set of origins as CORS plus the default localhost loopback, AND any
|
||||
# same-origin connection (where Origin matches the request's Host header).
|
||||
# Same-origin is inherently safe from CSWSH because CSWSH is a *cross*-
|
||||
# origin attack — without this, binding to 0.0.0.0 and accessing the UI
|
||||
# via a LAN IP would have its WebSocket rejected by the browser-sent
|
||||
# Origin, which the static allowlist can't anticipate.
|
||||
allowed_origins = set(
|
||||
settings.cors_origins
|
||||
or [
|
||||
@@ -427,8 +432,17 @@ async def websocket_endpoint(
|
||||
# Same-origin connections from native apps may omit Origin entirely; only
|
||||
# reject when an Origin is present AND not in the allow-list.
|
||||
if origin is not None and origin not in allowed_origins:
|
||||
await websocket.close(code=4003, reason="Origin not allowed")
|
||||
return
|
||||
host_header = websocket.headers.get("host", "")
|
||||
# Origin uses http/https; match against both scheme variants of Host
|
||||
# so HTTPS deployments without an explicit cors_origins still work.
|
||||
same_origin_candidates = (
|
||||
{f"http://{host_header}", f"https://{host_header}"}
|
||||
if host_header
|
||||
else set()
|
||||
)
|
||||
if origin not in same_origin_candidates:
|
||||
await websocket.close(code=4003, reason="Origin not allowed")
|
||||
return
|
||||
|
||||
# Verify token
|
||||
from ..auth import auth_enabled, get_token_label, token_label_var
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "media-server-frontend",
|
||||
"version": "0.2.7",
|
||||
"version": "0.3.0",
|
||||
"private": true,
|
||||
"description": "Frontend build tooling for media server WebUI",
|
||||
"scripts": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "media-server"
|
||||
version = "0.2.7"
|
||||
version = "0.3.1"
|
||||
description = "REST API server for controlling system-wide media playback"
|
||||
readme = "README.md"
|
||||
license = { text = "MIT" }
|
||||
|
||||
Reference in New Issue
Block a user