From 0ad87cc889728f600ba7f5ae16d8fdbfbabe5c57 Mon Sep 17 00:00:00 2001 From: Valera Trubachev Date: Sat, 9 Sep 2023 14:20:48 -0500 Subject: [PATCH] fix(ssr): fix JSON innerHTML encoding (#219) --- packages/ssr/src/util/tagToString.ts | 7 ++++--- test/unhead/ssr/innerHTML.test.ts | 24 ++++++++++++++++++++++++ test/unhead/ssr/templateParams.test.ts | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/ssr/src/util/tagToString.ts b/packages/ssr/src/util/tagToString.ts index 5ffb26ae..6abdcc1b 100644 --- a/packages/ssr/src/util/tagToString.ts +++ b/packages/ssr/src/util/tagToString.ts @@ -43,13 +43,14 @@ export function tagToString(tag: T) { if (!TagsWithInnerContent.includes(tag.tag)) return SelfClosingTags.includes(tag.tag) ? openTag : `${openTag}` + if (tag.innerHTML && ['application/ld+json', 'application/json'].includes(tag.props.type)) + // ensure tags get encoded + tag.innerHTML = escapeJson(tag.innerHTML) + // dangerously using innerHTML, we don't encode this let content = String(tag.innerHTML || '') if (tag.textContent) // content needs to be encoded to avoid XSS, only for title content = escapeHtml(String(tag.textContent)) - if (tag.innerHTML && ['application/ld+json', 'application/json'].includes(tag.props.type)) - // ensure tags get encoded - tag.innerHTML = escapeJson(tag.innerHTML) return SelfClosingTags.includes(tag.tag) ? openTag : `${openTag}${content}` } diff --git a/test/unhead/ssr/innerHTML.test.ts b/test/unhead/ssr/innerHTML.test.ts index 8c54b3bf..54b0e601 100644 --- a/test/unhead/ssr/innerHTML.test.ts +++ b/test/unhead/ssr/innerHTML.test.ts @@ -29,6 +29,30 @@ describe('ssr innerHTML', () => { `) }) + it('json escaping', async () => { + const head = createHead() + head.push({ + script: [ + { + type: 'application/json', + innerHTML: { + escape: '', + }, + }, + ], + }) + const ctx = await renderSSRHead(head) + expect(ctx).toMatchInlineSnapshot(` + { + "bodyAttrs": "", + "bodyTags": "", + "bodyTagsOpen": "", + "headTags": "", + "htmlAttrs": "", + } + `) + }); + it('noscript', async () => { const head = createHead() head.push({ diff --git a/test/unhead/ssr/templateParams.test.ts b/test/unhead/ssr/templateParams.test.ts index 1b0caade..04666b7f 100644 --- a/test/unhead/ssr/templateParams.test.ts +++ b/test/unhead/ssr/templateParams.test.ts @@ -80,7 +80,7 @@ describe('ssr templateParams', () => { expect(headTags).toMatchInlineSnapshot(` "Home & //<"With Encoding">\\\\ - " + " `) })