screentinker/frontend/js/views
ScreenTinker ec450929ce Escape user-controlled data at the HTML sinks it actually reaches
A QA sweep found unescaped interpolations outside the SSO work. Auditing them properly
turned up 34 genuine HTML sinks; 23 carry data a user, a device or an identity provider
controls, and those are escaped here.

The ones that mattered:

  - app.js renders `user.name` in the shell on EVERY page, and an identity provider's
    `name` claim is stored verbatim, so an IdP could script the whole dashboard
  - designer element `label`/`location` and widget `location`/`query` land inside
    value="" attributes, where a single quote breaks out
  - content `folder` lands in a data-folder="" attribute
  - device `name` is set by the operator OR reported by the panel itself
  - workspace-members renders a SERVER error string through t(), which interpolates raw

⚠️ My first attempt was a codemod over everything my scanner flagged, and it was wrong.
It wrapped `progressText.textContent`, `block.title` and `confirm(...)` — none of which
are HTML, so escaping there shows users literal `<`. Worse, it wrapped
`title: ev.title ? ... : null`, an API PAYLOAD, which would have written escaped markup
into the database. I reverted the whole thing and narrowed to interpolations that are
genuinely inside an HTML template, then read all 34 and chose 23.

Skipped deliberately: static app strings, i18n output, ternaries yielding `selected`,
`window.location.origin`, and sites already escaped.

Verified in Chrome, not by inspection: the payload was seeded into user.name,
device.name, content.filename/folder, widget.name/config and video_wall.name (the first
attempt's seeds silently failed on column names — the API responses are checked now),
then eleven views were loaded. Zero executions, zero live img tags — AND the payload is
visible as inert text in 6/6 views, which is what proves the views rendered it rather
than the test proving nothing.

1609 tests; every frontend module parses as an ES module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bvjey4FNam49MN7ybjcq6A
2026-08-11 11:44:10 -05:00
..
activity.js Stop eight dashboard views reporting success for requests the server refused 2026-07-30 21:21:56 -05:00
admin-player-debug.js Add player debug overlay and server-side error telemetry sink 2026-05-15 15:20:42 -05:00
admin.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
billing.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
content-library.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
dashboard.js QA: close four ways a control or an asset lied about itself 2026-08-06 16:12:29 -05:00
designer.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
device-detail.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
force-password-change.js feat(admin): admin-provisioned user creation + first-login gate (#10) 2026-06-05 11:03:56 -05:00
help.js Describe a portrait video wall as portrait, and stop a wall hiding its screens 2026-08-06 09:46:31 -05:00
kiosk.js Stop eight dashboard views reporting success for requests the server refused 2026-07-30 21:21:56 -05:00
layout-editor.js Portrait templates, a canvas that matches the layout, and a playlist mockup 2026-08-05 13:23:15 -05:00
login.js Fix: per-organization SSO was blocked by our own CSP and had never worked in a browser 2026-08-11 10:36:32 -05:00
no-workspace.js feat(signup): optional org-on-create for self-service signups (#12) 2026-06-05 11:16:27 -05:00
onboarding.js Default a video playlist item to the clip's own length (#237) 2026-08-06 09:36:53 -05:00
playlists.js Merge #239: let the playlist preview skip to any item 2026-08-06 09:52:20 -05:00
reports.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
schedule.js Stop eight dashboard views reporting success for requests the server refused 2026-07-30 21:21:56 -05:00
settings.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
teams.js Stop eight dashboard views reporting success for requests the server refused 2026-07-30 21:21:56 -05:00
video-wall.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
widgets.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00
workspace-members.js Escape user-controlled data at the HTML sinks it actually reaches 2026-08-11 11:44:10 -05:00