Skip to content

fix: allow autoplay attributes on <video> in markdown - #261

Open
Ramon-Jimenez wants to merge 1 commit into
developmentfrom
fix/video-autoplay-sanitizer
Open

fix: allow autoplay attributes on <video> in markdown#261
Ramon-Jimenez wants to merge 1 commit into
developmentfrom
fix/video-autoplay-sanitizer

Conversation

@Ramon-Jimenez

Copy link
Copy Markdown

📌 References

  • Issue: n/a — found while preparing the MSF OCB training modules

📝 Implementation

The sanitizer allowlist for <video> spelled three attributes in lowercase HTML form, but
hast normalises attributes to their DOM property names before sanitizing. The comparison
never matched, so the attributes were silently stripped:

In markdown hast stores In the allowlist Result
autoplay autoPlay autoplay stripped
playsinline playsInline playsinline stripped
muted mute mute is not a real HTML attribute

Only src, controls and loop were getting through, so autoplay could not be enabled on a
<video> from markdown at all
. <video-gif> was unaffected — its autoPlay/muted/playsInline
are hardcoded props on the React component and never pass through the sanitizer.

One line, spelling them the way hast stores them:

- video: ["src", "playsinline", "controls", "autoplay", "loop", "mute"],
+ video: ["src", "playsInline", "controls", "autoPlay", "loop", "muted"],

🔥 How to test it?

In any module step, use:

<video src="../../documents/<uid>/data" autoplay muted playsinline controls></video>

The video should start on its own, muted, with controls available to unmute and replay.

muted is required — browsers refuse to autoplay video with sound, so autoplay on its own
will still be blocked by the browser (correctly). controls keeps the player usable, which
matters for narrated walkthroughs where muted autoplay alone would be useless.

Verified by running the real validHtml schema through hast-util-sanitize before and after:

BEFORE  {"src":"/d/X/data","loop":true,"controls":true}
AFTER   {"src":"/d/X/data","autoPlay":true,"muted":true,"playsInline":true,"loop":true,"controls":true}

Sanitizing is not weakened — <video src="x" onerror="alert(1)"></video><script>alert(2)</script>
still reduces to video {"src":"x"} with the <script> removed. Confirmed working on a live
instance with a real training video.

📑 Others

🤖 Generated with Claude Code

The sanitizer allowlist for <video> spelled three attributes in lowercase
HTML form, but hast normalises attributes to their DOM property names
before sanitizing. The comparison never matched, so the attributes were
silently stripped:

  autoplay    -> autoPlay      stripped
  playsinline -> playsInline   stripped
  mute        -> (not a real HTML attribute at all)

Only src, controls and loop were getting through, which meant autoplay
could not be enabled on a <video> from markdown at all. <video-gif> was
unaffected because its autoPlay/muted/playsInline are hardcoded props on
the React component and never pass through the sanitizer.

Spelling them the way hast stores them lets them through:

  <video src="..." autoplay muted playsinline controls></video>

Note that browsers only autoplay muted video, so `muted` is required
alongside `autoplay` for it to actually start.
@bundlemon

bundlemon Bot commented Aug 24, 2026

Copy link
Copy Markdown

BundleMon

No change in files bundle size

Unchanged groups (1)
Status Path Size Limits
Build Folder
./**/*
10.47MB +20%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

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.

1 participant