Compare commits
3 Commits
d131ba461c
...
82710c6457
| 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
|
### 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 |
|
| 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>
|
</details>
|
||||||
|
|||||||
@@ -414,8 +414,13 @@ async def websocket_endpoint(
|
|||||||
accept_subprotocol = proto
|
accept_subprotocol = proto
|
||||||
break
|
break
|
||||||
effective_token = subprotocol_token or token
|
effective_token = subprotocol_token or token
|
||||||
# Origin check — block CSWSH from third-party LAN pages. We accept the same
|
# Origin check — block CSWSH from third-party LAN pages. Accept the same
|
||||||
# set of origins as CORS plus the default localhost loopback.
|
# 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(
|
allowed_origins = set(
|
||||||
settings.cors_origins
|
settings.cors_origins
|
||||||
or [
|
or [
|
||||||
@@ -427,8 +432,17 @@ async def websocket_endpoint(
|
|||||||
# Same-origin connections from native apps may omit Origin entirely; only
|
# Same-origin connections from native apps may omit Origin entirely; only
|
||||||
# reject when an Origin is present AND not in the allow-list.
|
# reject when an Origin is present AND not in the allow-list.
|
||||||
if origin is not None and origin not in allowed_origins:
|
if origin is not None and origin not in allowed_origins:
|
||||||
await websocket.close(code=4003, reason="Origin not allowed")
|
host_header = websocket.headers.get("host", "")
|
||||||
return
|
# 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
|
# Verify token
|
||||||
from ..auth import auth_enabled, get_token_label, token_label_var
|
from ..auth import auth_enabled, get_token_label, token_label_var
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "media-server-frontend",
|
"name": "media-server-frontend",
|
||||||
"version": "0.2.7",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Frontend build tooling for media server WebUI",
|
"description": "Frontend build tooling for media server WebUI",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "media-server"
|
name = "media-server"
|
||||||
version = "0.2.7"
|
version = "0.3.1"
|
||||||
description = "REST API server for controlling system-wide media playback"
|
description = "REST API server for controlling system-wide media playback"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = { text = "MIT" }
|
license = { text = "MIT" }
|
||||||
|
|||||||
Reference in New Issue
Block a user