Skip to content

Sanitize merchant-managed HTML in the markets recipe - #4278

Merged
lucyxiang merged 1 commit into
2026-04from
lucy/markets-sanitize-html
Nov 2, 2026
Merged

lucyxiang merged 1 commit into
2026-04from
lucy/markets-sanitize-html

Conversation

@lucyxiang

@lucyxiang lucyxiang commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

The markets recipe renders merchant-managed HTML from the Storefront API with dangerouslySetInnerHTML and does not sanitize it. A <script> tag or an onerror handler in a page, article, policy or product description runs for every visitor. This PR adds an allowlist sanitizer and calls it in each loader.

// Before
return {page};
// After
return {page: {...page, body: sanitizeHtml(page.body)}};

What this changes

  • Adds the app/lib/sanitizeHtml.ts ingredient. It has no dependencies, so it runs in Oxygen, Node and the browser.
    • Keeps: formatting, lists, tables, details, images (http/https) and links (http, https, mailto, tel, relative).
    • Removes: script, style, iframe, object, embed, svg, math, form controls, on* handlers, style attributes and other URL schemes.
    • Decodes character references before it checks URLs, closes all elements that it opens, and runs in linear time.
  • Sanitizes Page.body, Article.contentHtml, ShopPolicy.body and Product.descriptionHtml in their loaders.
  • Adds step 1.6 to recipe.yaml, and gives the package.json step a description. The description was null.
  • Regenerates README.md and llms/markets.prompt.md, and adds __tests__/sanitizeHtml.test.ts.

No changeset. The cookbook does not ship in a package.

Risk

  • Merchants who use iframes (for example YouTube) or inline styles lose that markup.

@lucyxiang
lucyxiang requested a review from a team as a code owner October 28, 2026 19:52
@shopify

shopify Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Oxygen deployed a preview of your lucy/markets-sanitize-html branch. Details:

Storefront Status Preview link Deployment details Last update (UTC)
Skeleton (skeleton.hydrogen.shop) ✅ Successful (Logs) Preview deployment Inspect deployment November 2, 2026 5:05 PM

Learn more about Hydrogen's GitHub integration.

@lucyxiang
lucyxiang force-pushed the lucy/markets-sanitize-html branch 2 times, most recently from 60b90fa to 64cf707 Compare October 29, 2026 04:17

@frandiox frandiox left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We could also rely directly on sanitize-html package but I guess for the cookbook it's not so important 👍

Comment thread cookbook/recipes/markets/recipe.yaml Resolved
@lucyxiang
lucyxiang requested a review from fredericoo November 2, 2026 16:10
@lucyxiang
lucyxiang force-pushed the lucy/markets-sanitize-html branch from 17f10e1 to c4e6c0b Compare November 2, 2026 16:48
The page, article, policy and product routes in the markets recipe
render Storefront API HTML with dangerouslySetInnerHTML. Add an
allowlist sanitizeHtml() ingredient and call it in each loader.

- Keep formatting, lists, tables, images and safe links. Remove
  elements, attributes and URLs that can run script, and remove embeds,
  `style` and `id` attributes.
- Decode character references before URL checks, and escape text
  attributes such as `alt` and `title` like text.
- Keep the nofollow, sponsored, ugc, noopener and noreferrer rel
  tokens, and force noopener noreferrer on target="_blank" links.
- Write the end tags that the browser implies for li, dd, dt, p and
  a nested table, and drop table parts outside a table.
- Handle `/>` as the browser does: ignore it on non-void HTML elements,
  and treat it as a closed element in skipped SVG and MathML.
- Run in linear time on unmatched end tags, unterminated start tags and
  long whitespace runs in URLs.
@lucyxiang
lucyxiang force-pushed the lucy/markets-sanitize-html branch from c4e6c0b to e9e1ea1 Compare November 2, 2026 17:04
@lucyxiang
lucyxiang enabled auto-merge November 2, 2026 17:34
@lucyxiang
lucyxiang disabled auto-merge November 2, 2026 17:39
@lucyxiang
lucyxiang merged commit 68f62c4 into 2026-04 Nov 2, 2026
20 of 21 checks passed
@lucyxiang
lucyxiang deleted the lucy/markets-sanitize-html branch November 2, 2026 17:48
lucyxiang added a commit that referenced this pull request Nov 2, 2026
The markets recipe README and LLM prompt link to files at the commit
in recipe.yaml. That commit was older than #4278, so the links to
sanitizeHtml.ts returned 404. Set the commit to the #4278 merge commit
and render the README and prompt again.

The Deleted Files links also returned 404. The deletedFiles paths
already start with templates/skeleton/, and render.ts added that
prefix again. Remove the extra prefix, and fix the same links in the
express and multipass READMEs.
devteamaegis pushed a commit to devteamaegis/hydrogen that referenced this pull request Nov 3, 2026
…#4291)

The markets recipe README and LLM prompt link to files at the commit
in recipe.yaml. That commit was older than Shopify#4278, so the links to
sanitizeHtml.ts returned 404. Set the commit to the Shopify#4278 merge commit
and render the README and prompt again.

The Deleted Files links also returned 404. The deletedFiles paths
already start with templates/skeleton/, and render.ts added that
prefix again. Remove the extra prefix, and fix the same links in the
express and multipass READMEs.
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.

3 participants