From 5b95140ece54c9178f60a86574e65363fbcceb33 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 30 Aug 2021 09:45:43 -0400 Subject: [PATCH 1/3] Add CSP to Image Optimization API --- packages/next/server/image-optimizer.ts | 2 ++ .../integration/production/pages/svg-image.js | 14 ++++++++++++++ test/integration/production/public/xss.svg | 9 +++++++++ test/integration/production/test/security.js | 19 +++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 test/integration/production/pages/svg-image.js create mode 100644 test/integration/production/public/xss.svg diff --git a/packages/next/server/image-optimizer.ts b/packages/next/server/image-optimizer.ts index eabb4ee55a35cdd..a2d7c6a664d0c98 100644 --- a/packages/next/server/image-optimizer.ts +++ b/packages/next/server/image-optimizer.ts @@ -525,6 +525,8 @@ function setResponseHeaders( res.setHeader('Content-Disposition', `inline; filename="${fileName}"`) } + res.setHeader('Content-Security-Policy', `script-src 'none'; sandbox;`) + return { finished: false } } diff --git a/test/integration/production/pages/svg-image.js b/test/integration/production/pages/svg-image.js new file mode 100644 index 000000000000000..0a450471d7e7edd --- /dev/null +++ b/test/integration/production/pages/svg-image.js @@ -0,0 +1,14 @@ +import React from 'react' +import Image from 'next/image' + +const Page = () => { + return ( +
+

SVG with a script tag attempting XSS

+ +

safe

+
+ ) +} + +export default Page diff --git a/test/integration/production/public/xss.svg b/test/integration/production/public/xss.svg new file mode 100644 index 000000000000000..c3e80bb95a209fe --- /dev/null +++ b/test/integration/production/public/xss.svg @@ -0,0 +1,9 @@ + + + XSS + + +

safe

+ + + diff --git a/test/integration/production/test/security.js b/test/integration/production/test/security.js index afc501e88d950b7..76b15b72c2ca08c 100644 --- a/test/integration/production/test/security.js +++ b/test/integration/production/test/security.js @@ -342,5 +342,24 @@ module.exports = (context) => { expect(pathname).toBe('/%2fexample.com') expect(hostname).not.toBe('example.com') }) + + it('should not execute script embedded inside svg image', async () => { + let browser + try { + browser = await webdriver(context.appPort, '/svg-image') + await browser.eval(`document.getElementById("img").scrollIntoView()`) + expect(await browser.elementById('img').getAttribute('src')).toContain( + 'xss.svg' + ) + expect(await browser.elementById('msg').text()).toBe('safe') + browser = await webdriver( + context.appPort, + '/_next/image?url=%2Fxss.svg&w=256&q=75' + ) + expect(await browser.elementById('msg').text()).toBe('safe') + } finally { + if (browser) await browser.close() + } + }) }) } From 6cb1b389c506fcf621e4777175ac5dc3adb7b19e Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 30 Aug 2021 09:58:30 -0400 Subject: [PATCH 2/3] Increment page count --- test/integration/production/test/index.test.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/integration/production/test/index.test.js b/test/integration/production/test/index.test.js index 9d820808f3ca915..f648948e9054cd9 100644 --- a/test/integration/production/test/index.test.js +++ b/test/integration/production/test/index.test.js @@ -78,7 +78,7 @@ describe('Production Usage', () => { }) it('should contain generated page count in output', async () => { - const pageCount = process.env.NEXT_PRIVATE_TEST_WEBPACK4_MODE ? 37 : 38 + const pageCount = process.env.NEXT_PRIVATE_TEST_WEBPACK4_MODE ? 38 : 39 expect(output).toContain(`Generating static pages (0/${pageCount})`) expect(output).toContain( `Generating static pages (${pageCount}/${pageCount})` @@ -469,13 +469,12 @@ describe('Production Usage', () => { it('should set title by routeChangeComplete event', async () => { const browser = await webdriver(appPort, '/') await browser.eval(function setup() { - window.next.router.events.on( - 'routeChangeComplete', - function handler(url) { - window.routeChangeTitle = document.title - window.routeChangeUrl = url - } - ) + window.next.router.events.on('routeChangeComplete', function handler( + url + ) { + window.routeChangeTitle = document.title + window.routeChangeUrl = url + }) window.next.router.push('/with-title') }) await browser.waitForElementByCss('#with-title') From ee403b0ed4489ef66bcfbf37ae14d5da651f452c Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 30 Aug 2021 10:20:35 -0400 Subject: [PATCH 3/3] Fix lint --- test/integration/production/test/index.test.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/integration/production/test/index.test.js b/test/integration/production/test/index.test.js index f648948e9054cd9..0f96c946dcc0f69 100644 --- a/test/integration/production/test/index.test.js +++ b/test/integration/production/test/index.test.js @@ -469,12 +469,13 @@ describe('Production Usage', () => { it('should set title by routeChangeComplete event', async () => { const browser = await webdriver(appPort, '/') await browser.eval(function setup() { - window.next.router.events.on('routeChangeComplete', function handler( - url - ) { - window.routeChangeTitle = document.title - window.routeChangeUrl = url - }) + window.next.router.events.on( + 'routeChangeComplete', + function handler(url) { + window.routeChangeTitle = document.title + window.routeChangeUrl = url + } + ) window.next.router.push('/with-title') }) await browser.waitForElementByCss('#with-title')