-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Describe the bug
SvelteKit's form action handler uses bracket notation lookup on a plain JavaScript object without prototype safety checks. Submitting a form action with a prototype property name (like ?/toString) resolves to Object.prototype.toString, bypasses the 404 guard, and returns a 200 success response. Other prototype properties like constructor trigger 500 errors.
The issue is in packages/kit/src/runtime/server/page/actions.js at line 246, where actions[name] is used without Object.hasOwn() protection.
Reproduction
Any page with form actions is affected:
# toString bypasses guard and returns 200 success
curl -s -X POST "http://localhost:4173/test-actions?/toString" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Accept: application/json" \
-H "Origin: http://localhost:4173" \
-d "test=1"
# Returns: {"type":"success","status":200,"data":"[\"[object Undefined]\"]"}
# constructor bypasses guard but triggers 500
curl -s -X POST "http://localhost:4173/test-actions?/constructor" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Accept: application/json" \
-H "Origin: http://localhost:4173" \
-d "test=1"
# Returns: {"type":"error","error":{"message":"Internal Error"}}
# Non-existent action correctly returns 404
curl -s -X POST "http://localhost:4173/test-actions?/nonexistent" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Accept: application/json" \
-H "Origin: http://localhost:4173" \
-d "test=1"
# Returns: {"type":"error","error":{"message":"Not Found"}}Logs
No crash — server continues running. The constructor variant invokes the application's handleError hook.
System Info
System:
OS: macOS 26.2
CPU: (14) arm64 Apple M4 Pro
Binaries:
Node: 24.13.0
pnpm: 10.28.1
npmPackages:
@sveltejs/kit: ^2.50.2 => 2.53.4
svelte: ^5.51.0 => 5.53.7
vite: ^7.3.1 => 7.3.1
Severity
annoyance
Additional Information
Suggested fix — replace the bracket notation lookup with a prototype-safe check:
// Before (line 246):
const action = actions[name];
if (!action) {
throw new SvelteKitError(404, 'Not Found', `No action with name '${name}' found`);
}
// After:
if (!Object.hasOwn(actions, name)) {
throw new SvelteKitError(404, 'Not Found', `No action with name '${name}' found`);
}
const action = actions[name];The same pattern exists in packages/kit/src/runtime/server/remote.js at three hash lookup sites (lines 44, 207, 259) where remotes[hash] should also use Object.hasOwn(remotes, hash).