Skip to content

Commit 67a0d86

Browse files
authored
fix: avoid running load function on invalid requests (#9752)
1 parent 42c9b93 commit 67a0d86

File tree

3 files changed

+59
-3
lines changed

3 files changed

+59
-3
lines changed

.changeset/giant-rockets-love.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: avoid running load function on invalid requests

packages/kit/src/runtime/server/respond.js

+33-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { render_page } from './page/index.js';
55
import { render_response } from './page/render.js';
66
import { respond_with_error } from './page/respond_with_error.js';
77
import { is_form_content_type } from '../../utils/http.js';
8-
import { handle_fatal_error, redirect_response } from './utils.js';
8+
import { handle_fatal_error, method_not_allowed, redirect_response } from './utils.js';
99
import {
1010
decode_pathname,
1111
decode_params,
@@ -42,6 +42,10 @@ const default_filter = () => false;
4242
/** @type {import('types').RequiredResolveOptions['preload']} */
4343
const default_preload = ({ type }) => type === 'js' || type === 'css';
4444

45+
const page_methods = new Set(['GET', 'HEAD', 'POST']);
46+
47+
const allowed_page_methods = new Set(['GET', 'HEAD', 'OPTIONS']);
48+
4549
/**
4650
* @param {Request} request
4751
* @param {import('types').SSROptions} options
@@ -343,7 +347,6 @@ export async function respond(request, options, manifest, state) {
343347
}
344348

345349
/**
346-
*
347350
* @param {import('@sveltejs/kit').RequestEvent} event
348351
* @param {import('@sveltejs/kit').ResolveOptions} [opts]
349352
*/
@@ -379,6 +382,8 @@ export async function respond(request, options, manifest, state) {
379382
}
380383

381384
if (route) {
385+
const method = /** @type {import('types').HttpMethod} */ (event.request.method);
386+
382387
/** @type {Response} */
383388
let response;
384389

@@ -395,7 +400,32 @@ export async function respond(request, options, manifest, state) {
395400
} else if (route.endpoint && (!route.page || is_endpoint_request(event))) {
396401
response = await render_endpoint(event, await route.endpoint(), state);
397402
} else if (route.page) {
398-
response = await render_page(event, route.page, options, manifest, state, resolve_opts);
403+
if (page_methods.has(method)) {
404+
response = await render_page(event, route.page, options, manifest, state, resolve_opts);
405+
} else {
406+
const allowed_methods = new Set(allowed_page_methods);
407+
const node = await manifest._.nodes[route.page.leaf]();
408+
if (node?.server?.actions) {
409+
allowed_methods.add('POST');
410+
}
411+
412+
if (method === 'OPTIONS') {
413+
// This will deny CORS preflight requests implicitly because we don't
414+
// add the required CORS headers to the response.
415+
response = new Response(null, {
416+
status: 204,
417+
headers: {
418+
allow: Array.from(allowed_methods.values()).join(', ')
419+
}
420+
});
421+
} else {
422+
const mod = [...allowed_methods].reduce((acc, curr) => {
423+
acc[curr] = true;
424+
return acc;
425+
}, /** @type {Record<string, any>} */ ({}));
426+
response = method_not_allowed(mod, method);
427+
}
428+
}
399429
} else {
400430
// a route will always have a page or an endpoint, but TypeScript
401431
// doesn't know that

packages/kit/test/apps/basics/test/server.test.js

+21
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,27 @@ test.describe('Load', () => {
412412
await page.goto(`/load/fetch-origin-external?port=${port}`);
413413
expect(await page.textContent('h1')).toBe(`origin: ${new URL(baseURL).origin}`);
414414
});
415+
416+
test('does not run when using invalid request methods', async ({ request }) => {
417+
const load_url = '/load';
418+
419+
let response = await request.fetch(load_url, {
420+
method: 'OPTIONS'
421+
});
422+
423+
expect(response.status()).toBe(204);
424+
expect(await response.text()).toBe('');
425+
expect(response.headers()['allow']).toBe('GET, HEAD, OPTIONS');
426+
427+
const actions_url = '/actions/enhance';
428+
response = await request.fetch(actions_url, {
429+
method: 'OPTIONS'
430+
});
431+
432+
expect(response.status()).toBe(204);
433+
expect(await response.text()).toBe('');
434+
expect(response.headers()['allow']).toBe('GET, HEAD, OPTIONS, POST');
435+
});
415436
});
416437

417438
test.describe('Routing', () => {

0 commit comments

Comments
 (0)