-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Reproduction
Any Vue Router app using the default parseQuery implementation. Minimal test case:
import { parseQuery } from 'vue-router'
const q = parseQuery('__proto__=evil&foo=bar')
console.log(q) // unexpected ghost keys from prototype chain
console.log(q.__proto__) // 'evil' shadows Object.prototypeSteps to reproduce the bug
- Create a Vue app with Vue Router (or Nuxt, which uses Vue Router's default
parseQuery) - Send a request with
__proto__orconstructoras a query parameter:curl 'http://localhost:3000/?__proto__=evil&foo=bar' - Observe the rendered
route.fullPath— it contains ghost parameters:/?foo=bar&0=[object+Object]&1=evil - The
constructorvariant also leaks internal references:curl 'http://localhost:3000/?constructor=test' # Renders: /?constructor=function+Object()+{+[native+code]+}&constructor=test
Expected behavior
parseQuery should return a null-prototype object (via Object.create(null)) so that inherited properties like __proto__ and constructor cannot be accidentally read or overwritten. Query keys like __proto__ should be stored as plain data without affecting the object's prototype chain.
Actual behavior
parseQuery creates query objects with const query = {}. When ?__proto__=evil is parsed, "__proto__" in query evaluates to true (inherited property), and the subsequent query[key] = [currentValue] corrupts the object. This causes stringifyQuery to emit ghost parameters from the corrupted prototype chain.
The corruption is scoped to that specific object instance — it does not achieve persistent cross-request Object.prototype pollution. But it does cause unexpected query parameters to appear in route.fullPath, which could interfere with middleware or logic that inspects route.query.
Additional information
The root cause is in parseQuery (around line 1433-1449 in vue-router.cjs). Changing const query = {} to const query = Object.create(null) would fix this. Libraries like ufo already use Object.create(null) in their parseQuery implementation for this reason.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status