diff --git a/craft/src/components/basic/HtmlBlock.test.ts b/craft/src/components/basic/HtmlBlock.test.ts index 86fa33d..4b9ebed 100644 --- a/craft/src/components/basic/HtmlBlock.test.ts +++ b/craft/src/components/basic/HtmlBlock.test.ts @@ -21,3 +21,46 @@ describe('purifyHtml', () => { expect(purifyHtml('
')).not.toContain(' { + test('forces a restrictive sandbox attribute onto every iframe', () => { + const out = purifyHtml(''); + expect(out).toMatch(/]*\bsandbox="[^"]+"/); + }); + + test('sandbox value omits allow-top-navigation (no top-level nav escape)', () => { + const out = purifyHtml(''); + const sandbox = out.match(/sandbox="([^"]*)"/)![1]; + expect(sandbox).not.toMatch(/allow-top-navigation/); + }); + + test('legitimate embeds (YouTube) still work and get sandboxed too', () => { + const out = purifyHtml(''); + expect(out).toContain('youtube.com/embed/abc'); + expect(out).toMatch(/]*\bsandbox="[^"]+"/); + }); + + test('adds referrerpolicy=no-referrer to iframes', () => { + const out = purifyHtml(''); + expect(out).toContain('referrerpolicy="no-referrer"'); + }); + + test('script/on* attributes are still stripped alongside the sandboxed iframe', () => { + const out = purifyHtml(''); + expect(out).not.toContain('onload'); + expect(out).not.toContain(' { + purifyHtml(''); + purifyHtml(''); + const out = purifyHtml(''); + const sandboxMatches = out.match(/sandbox="/g) || []; + expect(sandboxMatches.length).toBe(1); + }); + + test('a non-iframe element sanitized alongside an iframe is not touched by the hook', () => { + const out = purifyHtml('

hi

'); + expect(out).toContain('

hi

'); + }); +}); diff --git a/craft/src/components/basic/HtmlBlock.tsx b/craft/src/components/basic/HtmlBlock.tsx index 4fa7f8a..0f41cc4 100644 --- a/craft/src/components/basic/HtmlBlock.tsx +++ b/craft/src/components/basic/HtmlBlock.tsx @@ -24,14 +24,42 @@ const PURIFY_CONFIG = { 'href','src','alt','title','target','rel', 'width','height','class', 'allowfullscreen','allow','frameborder', + 'sandbox','referrerpolicy', ], ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel|data:image\/[a-z]+;base64,):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i, FORBID_TAGS: ['script','style','object','embed','link','meta','form','input','button','select','textarea'], FORBID_ATTR: [/^on/i], }; +// M-6: `