diff --git a/cookbook/llms/markets.prompt.md b/cookbook/llms/markets.prompt.md index d3aa684c84..b96a2efc4d 100644 --- a/cookbook/llms/markets.prompt.md +++ b/cookbook/llms/markets.prompt.md @@ -98,6 +98,7 @@ selected. - app/components/CountrySelector.tsx - app/components/Link.tsx - app/lib/i18n.ts +- app/lib/sanitizeHtml.ts - app/routes/($locale)._index.tsx - app/routes/($locale).account.$.tsx - app/routes/($locale).account._index.tsx @@ -571,7 +572,731 @@ export function useLocalizedPath( ~~~ -#### Step 1.6: Update ProductItem to use locale-aware Link +#### Step 1.6: Create an HTML sanitizer for merchant-managed rich text + +Pages, blog articles, policies and product descriptions come from the Storefront API +as HTML written by merchants, staff and apps, and the routes render that HTML with +`dangerouslySetInnerHTML`. Treat it as untrusted: create a `sanitizeHtml()` helper that +keeps formatting, tables, images and safe links, and removes anything that can run +script, such as `

Shop now

', + ), + ).toBe('

Welcome

Shop now

'); + }); + + it('removes inline scripts and their content', () => { + expect(sanitizeHtml('

a

b

')).toBe( + '

a

b

', + ); + }); + + it('removes scripts with mixed-case tag names', () => { + expect(sanitizeHtml('ok')).toBe('ok'); + }); + + it('treats script content as raw text', () => { + expect(sanitizeHtml('after')).toBe( + 'after', + ); + }); + + it('removes everything after an unclosed script tag', () => { + expect(sanitizeHtml('

a

xok', + ), + ).toBe('ok'); + }); + + it('removes nested SVG elements', () => { + expect(sanitizeHtml('xok')).toBe( + 'ok', + ); + }); + + it('removes a self-closing SVG element', () => { + expect(sanitizeHtml('

ok

')).toBe('

ok

'); + }); + + it('keeps content after an SVG element with a self-closing nested SVG', () => { + expect(sanitizeHtml('

ok

')).toBe('

ok

'); + }); + + it('keeps content after MathML with a self-closing nested math element', () => { + expect(sanitizeHtml('

ok

')).toBe('

ok

'); + }); + + it('treats as an open SVG element', () => { + expect(sanitizeHtml('ok')).toBe(''); + }); + + it('removes MathML and its content', () => { + expect( + sanitizeHtml( + '
ok', + ), + ).toBe('ok'); + }); + + it('removes noscript content that can mutate into markup', () => { + expect( + sanitizeHtml( + '