Skip to content

fix(sanitize): allow trusted-host iframe embeds in post/page body content - #369

Open
asachs01 wants to merge 1 commit into
CoreBunch:mainfrom
wyre-technology:fix/post-body-iframe-embeds-trusted-hosts
Open

fix(sanitize): allow trusted-host iframe embeds in post/page body content#369
asachs01 wants to merge 1 commit into
CoreBunch:mainfrom
wyre-technology:fix/post-body-iframe-embeds-trusted-hosts

Conversation

@asachs01

Copy link
Copy Markdown

Summary

base.outlet's html prop (the markdown-rendered post/page body) is typed richtext, so escapeProps() sanitizes it through RICHTEXT_CONFIG — an allowlist built for short marketing copy with no img/video/table/iframe. marked passes raw HTML blocks through untouched during markdown parsing, so a pasted YouTube <iframe> (or any other embed) survives parsing and then gets silently stripped at the later sanitize step — with no error or warning surfaced to the author.

Confirmed there's no existing zero-code path around this: the Tiptap post-body editor has no video/embed insertion feature ("Add Media" opens the media-library file picker, not a URL-embed), and base.video (the module that could otherwise host a YouTube embed) is page-builder-canvas-only — it has no bridge into markdown post-body fields.

Change

  • New richtextBody control type, distinct from richtext, used only by base.outlet.
  • New POST_BODY_CONFIG DOMPurify config: allows img/video/table/iframe, with iframe scoped to a trusted-host allowlist (youtube.com, youtube-nocookie.com, subdomain-aware) via an uponSanitizeElement hook.
  • Lookalike-host tests included (youtube.com.evil.com, evilyoutube.com both correctly rejected) to guard the allowlist logic itself.
  • Every other richtext-typed field (short-copy fields elsewhere in the module system) is unaffected — this only changes the config used by base.outlet's body prop.

Test plan

  • Full suite green (6611+ tests passing at the time this was built, no regressions)
  • tsc clean
  • eslint clean
  • Manually verified against a real broken post in a live deployment: iframe embed survives the write path (confirmed via API), was stripped at render time before this fix, renders correctly after.

Happy to adjust the trusted-host list or expose it as a config option if that's preferred upstream — went with a hardcoded YouTube-only allowlist since that's the immediate need, but a configurable allowlist would be a small follow-up if there's appetite for embeds from other providers.

…tent

base.outlet's `html` prop (the markdown-rendered entry body every post
uses) was declared type `richtext`, so escapeProps() ran it through
DOMPurify's RICHTEXT_CONFIG — an allowlist built for short-form fields
(p/strong/em/a/ul/li) with no img, video, table, or iframe. marked
passes raw HTML blocks through untouched, so a pasted YouTube <iframe>
survives markdown parsing fine and is then silently stripped at this
later sanitize step. Reported by WYRE (2 posts with embedded YouTube
videos not rendering).

Adds a new `richtextBody` control type, used only by base.outlet's
`html` prop, sanitized via a new POST_BODY_CONFIG: the same safe
formatting tags as RICHTEXT_CONFIG plus img/video/table (what a real
post body markdown-renders to) plus iframe — scoped to a trusted-host
allowlist (youtube.com, youtube-nocookie.com, subdomain-aware) via a
DOMPurify uponSanitizeElement hook that removes the whole element (not
just the src) on any non-match, including lookalike-host attempts
(youtube.com.evil.com, evilyoutube.com — tested).

Every other richtext-typed field in the CMS is unaffected — kept as a
distinct control type rather than widening RICHTEXT_CONFIG globally,
so short-form fields elsewhere don't gain a wider attack surface than
they need.

Full suite: 6611 tests passing (0 fail), tsc clean, lint clean.
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