Skip to content

fix: escape attribute values in template HTML - #265

Merged
luwes merged 5 commits into
mainfrom
fix/escape-attribute-values
Oct 19, 2026
Merged

luwes merged 5 commits into
mainfrom
fix/escape-attribute-values

Conversation

@javierarams

@javierarams javierarams commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

The media elements make their shadow DOM from an HTML string. The attribute values went into that string with no escape. A value with quotes or angle brackets changed the markup in the shadow root. Server rendered pages that use the React wrappers had the same problem.

Changes

  • custom-media-element escapes attribute values in the template HTML. All elements that use the default template get the fix. These include hls, dash, shaka and castable.
  • hls-video-element escapes the config JSON in its template script.
  • cloudflare-video-element and spotify-audio-element use the same attribute escape as the other iframe elements.

The native media element gets the same attribute values as before. The HTML from getTemplateHTML stays valid for server rendering. Subclasses that override getTemplateHTML need no change.

Tests

  • New browser tests in custom-media-element. They check that attribute values do not become markup, and that normal values go to the native element with no change.
  • A new test checks that the getTemplateHTML output keeps all attribute values.
  • A new hls test checks that the config stays in one script and that it parses to the same object.
  • Lint passes. The custom-media-element tests pass on the eager and the lazy upgrade pages.

Note

Medium Risk
Security fix in shadow-DOM/SSR template generation with broad impact on elements using the shared serializer; logic is localized and covered by new regression tests.

Overview
Fixes HTML injection when custom media elements build shadow DOM from string templates: attribute names and values are now passed through escapeHtml in serializeAttributes (custom-media-element, cloudflare-video-element, spotify-audio-element), so quotes and angle brackets in values cannot break out of attributes or inject extra markup.

For HLS, embedded config JSON in the template <script> now escapes < as \u003c so malicious config strings cannot close the script tag.

Behavior preserved: forwarded attributes on the native <video>/<audio>/iframe still get the original values; only the serialized template HTML is escaped. New browser tests cover malicious poster/crossorigin values, unchanged normal attributes, getTemplateHTML output, and HLS config staying in a single parseable script.

Reviewed by Cursor Bugbot for commit c8fd03c. Bugbot is set up for automated code reviews on this repo. Configure here.

The template builder wrote attribute values into the HTML string with no
escape. A value that has quotes or angle brackets changed the shadow DOM
markup. The builder now escapes the special HTML characters. The native
media element gets the same attribute values as before, and the HTML from
getTemplateHTML stays valid for server rendering.
getTemplateHTML writes the config as JSON in a script element. A config
string with a closing script tag ended that element too early. The "<"
character is now written as a JSON unicode escape, so JSON.parse gives the
same config.
Use the same attribute escape as the other iframe based elements.
Use the same attribute escape as the other iframe based elements.
@vercel

vercel Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
media-elements-nextjs Ready Ready Preview Oct 14, 2026 6:44pm UTC

Request Review

@luwes luwes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does key need escaping there?

Escape the attribute names the same way as the values, so the template
HTML stays correct for all input.

@luwes luwes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@javierarams javierarams self-assigned this Oct 19, 2026
@luwes
luwes merged commit 5421514 into main Oct 19, 2026
7 checks passed
@luwes
luwes deleted the fix/escape-attribute-values branch October 19, 2026 21:48
@github-actions github-actions Bot mentioned this pull request Oct 19, 2026

This branch was successfully deployed

1 active deployment
Preview — c8fd03c9 Deployed Oct 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants