From b6db6d41b265b2add59b2ec6bf956168ae31d2c8 Mon Sep 17 00:00:00 2001 From: Brian Willows Date: Wed, 9 Sep 2026 15:34:57 +0100 Subject: [PATCH] fix: reject __proto__/constructor/prototype filter keys (prototype pollution) The filter reducer assigns result[key][op] = value where key comes straight from the query-string parameter name. For key '__proto__', result['__proto__'] resolves via the getter to Object.prototype, so the assignment writes an attacker-controlled, enumerable property onto Object.prototype for the whole process (CWE-1321). aqp('__proto__>5') was enough to set Object.prototype.$gt = 5, and the library's intended input is exactly an untrusted query string. Reject the three prototype-walking key names in the same filter that already applies blacklist/whitelist. They are not usable Mongo field names, so no legitimate query is affected. Co-Authored-By: Claude Opus 4.8 --- src/index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/index.js b/src/index.js index 6b26861..0bbadec 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,9 @@ +// Query-string keys that must never be used as an object key, because writing +// through them mutates Object.prototype for the whole process. +const FORBIDDEN_KEYS = ['__proto__', 'constructor', 'prototype']; + const builtInCasters = { boolean: (val) => val === 'true', date: (val) => new Date(val), @@ -214,6 +218,11 @@ const getFilter = (filter, params, options) => { }) .filter( ({ key }) => + // Keys that would walk into Object.prototype are always rejected: + // `result['__proto__'][op] = value` writes onto the prototype of every + // object in the process (prototype pollution), and neither name is a + // usable Mongo field anyway. + !FORBIDDEN_KEYS.includes(key) && !options.blacklist.includes(key) && (!options.whitelist || options.whitelist.includes(key)) )