Skip to content

Commit

Permalink
fix(ssr): fix JSON innerHTML encoding (#219)
Browse files Browse the repository at this point in the history
  • Loading branch information
vetruvet committed Sep 9, 2023
1 parent dc51865 commit 0ad87cc
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 4 deletions.
7 changes: 4 additions & 3 deletions packages/ssr/src/util/tagToString.ts
Expand Up @@ -43,13 +43,14 @@ export function tagToString<T extends HeadTag>(tag: T) {
if (!TagsWithInnerContent.includes(tag.tag))
return SelfClosingTags.includes(tag.tag) ? openTag : `${openTag}</${tag.tag}>`

if (tag.innerHTML && ['application/ld+json', 'application/json'].includes(tag.props.type))
// ensure </script> 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 </script> tags get encoded
tag.innerHTML = escapeJson(tag.innerHTML)
return SelfClosingTags.includes(tag.tag) ? openTag : `${openTag}${content}</${tag.tag}>`
}
24 changes: 24 additions & 0 deletions test/unhead/ssr/innerHTML.test.ts
Expand Up @@ -29,6 +29,30 @@ describe('ssr innerHTML', () => {
`)
})

it('json escaping', async () => {
const head = createHead()
head.push({
script: [
{
type: 'application/json',
innerHTML: {
escape: '</script>',
},
},
],
})
const ctx = await renderSSRHead(head)
expect(ctx).toMatchInlineSnapshot(`
{
"bodyAttrs": "",
"bodyTags": "",
"bodyTagsOpen": "",
"headTags": "<script type=\\"application/json\\">{\\"escape\\":\\"\\\\u003C/script>\\"}</script>",
"htmlAttrs": "",
}
`)
});

it('noscript', async () => {
const head = createHead()
head.push({
Expand Down
2 changes: 1 addition & 1 deletion test/unhead/ssr/templateParams.test.ts
Expand Up @@ -80,7 +80,7 @@ describe('ssr templateParams', () => {

expect(headTags).toMatchInlineSnapshot(`
"<title>Home &amp; &#x2F;&#x2F;&lt;&quot;With Encoding&quot;&gt;\\\\</title>
<script type=\\"application/json\\">{\\"title\\":\\"Home & //<\\\\\\"With Encoding\\\\\\">\\\\\\"}</script>"
<script type=\\"application/json\\">{\\"title\\":\\"Home & //\\\\u003C\\\\\\"With Encoding\\\\\\">\\\\\\"}</script>"
`)
})

Expand Down

0 comments on commit 0ad87cc

Please sign in to comment.