diff --git a/__tests__/ssr.test.tsx b/__tests__/ssr.test.tsx index 54c0447..c8fe6a0 100644 --- a/__tests__/ssr.test.tsx +++ b/__tests__/ssr.test.tsx @@ -71,6 +71,24 @@ describe('ssr', () => { ]); }); + it('should escape meta-content', () => { + jest.useFakeTimers(); + const MyComponent = () => { + useMeta({ property: 'fb:admins', content: "''<>&" }); + useMeta({ property: 'fb:something', content: '""' }); + return

hi

; + }; + + render(); + jest.runAllTimers(); + const { metas } = toStatic(); + + expect(metas).toEqual([ + { content: '""', property: 'fb:something' }, + { content: '''<>&', property: 'fb:admins' }, + ]); + }); + it('should render to string (basic-useHead)', () => { jest.useFakeTimers(); const MyComponent = () => { diff --git a/src/dispatcher/index.ts b/src/dispatcher/index.ts index dc116b9..c83a454 100644 --- a/src/dispatcher/index.ts +++ b/src/dispatcher/index.ts @@ -201,6 +201,44 @@ export const createDispatcher = () => { : // istanbul ignore next undefined, toStatic: (): StaticPayload => { + const ESCAPED_CHARS = /['"&<>]/; + + function escape(str: string) { + if (str.length === 0 || ESCAPED_CHARS.test(str) === false) return str; + + let last = 0, + i = 0, + out = '', + ch = ''; + + for (; i < str.length; i++) { + switch (str.charCodeAt(i)) { + case 34: + ch = '"'; + break; + case 38: + ch = '&'; + break; + case 39: + ch = '''; + break; + case 60: + ch = '<'; + break; + case 62: + ch = '>'; + break; + default: + continue; + } + + if (i !== last) out += str.slice(last, i); + out += ch; + last = i + 1; + } + if (i !== last) out += str.slice(last, i); + return out; + } // Will process the two arrays, taking the first title in the array and returning {string} // Then do a similar for the meta's. (will also need to add links, and add a linkQueue). Note that both queues // will need a reset to prevent memory leaks. @@ -240,7 +278,7 @@ export const createDispatcher = () => { } : { [meta.keyword]: meta[meta.keyword], - content: meta.content, + content: escape(meta.content), } ), };