screentinker/server
ScreenTinker b25bfaea57 Stop the player rendering black when the server hosts the display
A BrightSign hosting ScreenTinker shows a local page from file:///ssd:/node-server.html
that layers the player in an iframe — an iframe rather than a navigation, because
navigating replaces the document and kills the poller that notices the server dying.

helmet sets X-Frame-Options: SAMEORIGIN, and file:// is not the same origin as
http://127.0.0.1:8181, so the frame rendered BLACK. Every asset inside it returned 200
— the player page and all six of its scripts — and nothing appeared in any log. Only
the response headers said why, which is a miserable thing to debug on a device with no
console.

⚠️ AND IT IS NOT ONLY /player. Chrome evaluates SAMEORIGIN against the TOP-LEVEL
document rather than the immediate parent, so with a file:// page at the top, every
iframe the player itself uses — widget renders, kiosk views — is blocked by the same
rule one level deeper. Scoping this to /player would have cleared the black screen and
left every widget in the playlist black instead: the same bug, found later, on a
customer's wall. The test covers that case explicitly.

Scoped by CONTEXT, not by path: only when the process was started as a player host
(bs-server-boot.js sets ST_PLAYER_HOST; nothing else does) AND the request arrived on
loopback, i.e. from the box's own browser. An ordinary server keeps SAMEORIGIN, and so
does any request off the network — which is where clickjacking would have to come from,
since a remote page cannot reach another machine's 127.0.0.1. Where a CSP is set, only
its frame-ancestors directive is rewritten; the rest of the policy survives.

Verified on XT245 URD3C6000823: the player renders and shows its pairing code, while a
request to the same URL from the LAN still returns X-Frame-Options: SAMEORIGIN.
Full suite 1766 pass / 0 fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014kfhrUPit5MCqxeTQyqr56
2026-08-18 21:36:56 -05:00
..
config fix(logging): gate CF-Connecting-IP on a Cloudflare peer, not any trusted proxy 2026-07-26 10:23:04 -05:00
db Choose the SQLite driver at runtime, and ship the FFmpeg licence with the binaries 2026-08-18 20:57:05 -05:00
lib Choose the SQLite driver at runtime, and ship the FFmpeg licence with the binaries 2026-08-18 20:57:05 -05:00
middleware Merge branch 'fix/recovery-grants' into release/auth-campaign 2026-07-26 16:22:59 -05:00
player Run the ScreenTinker server on the player it serves (#288) 2026-08-18 15:16:09 -05:00
routes Run the ScreenTinker server on the player it serves (#288) 2026-08-18 15:16:09 -05:00
scripts Drop sharp: pure-JS image ops on a worker thread (#263) 2026-08-13 11:40:13 -05:00
services #240: stop the morning wave buying itself a blocking checkpoint 2026-08-06 20:22:21 -05:00
test Stop the player rendering black when the server hosts the display 2026-08-18 21:36:56 -05:00
ws Run the ScreenTinker server on the player it serves (#288) 2026-08-18 15:16:09 -05:00
.gitignore feat(email): Microsoft Graph send + alert spam protection + preferences UI 2026-05-12 18:16:40 -05:00
config.js SSO: prove domain ownership by DNS, and fix what the second review found 2026-08-10 19:23:46 -05:00
package-lock.json chore(release): v1.9.37 2026-08-18 20:58:01 -05:00
package.json chore(release): v1.9.37 2026-08-18 20:58:01 -05:00
server.js Stop the player rendering black when the server hosts the display 2026-08-18 21:36:56 -05:00
smoke-ui.js Keep the smoke test out of npm test, and update the lockfile 2026-07-28 20:34:34 -05:00
version.js chore(version): single-source VERSION, env-configurable data paths, bump tooling 2026-06-10 12:56:03 -05:00