From 12ee141bfbc90a04612fc35bed0962f17b2f9494 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 20 Sep 2022 14:23:57 -0400 Subject: [PATCH] only forward cookies for internal fetches - closes #6604 --- .changeset/witty-shoes-know.md | 5 ++++ packages/kit/src/runtime/server/page/fetch.js | 22 ++++++++-------- .../load/fetch-external-no-cookies/+page.js | 7 ++++++ .../fetch-external-no-cookies/+page.svelte | 1 + .../kit/test/apps/basics/test/server.test.js | 25 ++++++++++++++++++- 5 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 .changeset/witty-shoes-know.md create mode 100644 packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.js create mode 100644 packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.svelte diff --git a/.changeset/witty-shoes-know.md b/.changeset/witty-shoes-know.md new file mode 100644 index 000000000000..045cf51391d8 --- /dev/null +++ b/.changeset/witty-shoes-know.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Only forward set-cookie headers for internal fetches diff --git a/packages/kit/src/runtime/server/page/fetch.js b/packages/kit/src/runtime/server/page/fetch.js index ec728254afa0..fdd0a4658f66 100644 --- a/packages/kit/src/runtime/server/page/fetch.js +++ b/packages/kit/src/runtime/server/page/fetch.js @@ -172,21 +172,21 @@ export function create_fetch({ event, options, state, route, prerender_default, state.prerendering.dependencies.set(url.pathname, dependency); } + const set_cookie = response.headers.get('set-cookie'); + if (set_cookie) { + set_cookies.push( + ...set_cookie_parser.splitCookiesString(set_cookie).map((str) => { + const { name, value, ...options } = set_cookie_parser.parseString(str); + // options.sameSite is string, something more specific is required - type cast is safe + return /** @type{import('./types').Cookie} */ ({ name, value, options }); + }) + ); + } + return response; } }); - const set_cookie = response.headers.get('set-cookie'); - if (set_cookie) { - set_cookies.push( - ...set_cookie_parser.splitCookiesString(set_cookie).map((str) => { - const { name, value, ...options } = set_cookie_parser.parseString(str); - // options.sameSite is string, something more specific is required - type cast is safe - return /** @type{import('./types').Cookie} */ ({ name, value, options }); - }) - ); - } - const proxy = new Proxy(response, { get(response, key, _receiver) { async function text() { diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.js new file mode 100644 index 000000000000..a968114e23a3 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.js @@ -0,0 +1,7 @@ +/** @type {import('./$types').PageLoad} */ +export async function load({ fetch, url }) { + const port = url.searchParams.get('port'); + const res = await fetch(`http://localhost:${port}`); + + return await res.json(); +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.svelte new file mode 100644 index 000000000000..6e7c1e6ac83f --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-external-no-cookies/+page.svelte @@ -0,0 +1 @@ +

hello

diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index 0404859e5fd7..44d68b553846 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { test } from '../../../utils.js'; +import { start_server, test } from '../../../utils.js'; import { fetch } from 'undici'; import { createHash, randomBytes } from 'node:crypto'; @@ -23,6 +23,29 @@ test.describe('Content-Type', () => { }); }); +test.describe('Cookies', () => { + test('does not forward cookies from external domains', async ({ request }) => { + const { close, port } = await start_server(async (req, res) => { + if (req.url === '/') { + res.writeHead(200, { + 'set-cookie': 'external=true', + 'access-control-allow-origin': '*' + }); + + res.end('ok'); + } else { + res.writeHead(404); + res.end('not found'); + } + }); + + const response = await request.get(`/load/fetch-external-no-cookies?port=${port}`); + expect(response.headers()['set-cookie']).not.toContain('external=true'); + + close(); + }); +}); + test.describe('CSRF', () => { test('Blocks requests with incorrect origin', async ({ baseURL }) => { const res = await fetch(`${baseURL}/csrf`, {