Prototype pollution¶
Prototype pollution occurs when untrusted property names reach operations that modify an object's prototype or a shared prototype. Other code may then read an inherited value as if the application had supplied it. Impact depends on that later use: changed options, incorrect authorization decisions, or access to a dangerous execution path.
Trust boundary: JSON, query parameters, or imported configuration must not choose arbitrary object paths. Parsing JSON alone does not pollute a prototype; a later assignment or recursive merge can introduce the weakness. OWASP's prevention guidance recommends structures such as Map and objects without prototypes where suitable.
Unsafe example: copying arbitrary preferences¶
This JavaScript example accepts parsed JSON. Assignment can invoke special setters, and it also accepts settings the caller should not control.
function preferences(input) {
const result = {};
for (const key of Object.keys(input)) result[key] = input[key];
return result;
}
Safer example: construct the supported shape¶
function preferences(input) {
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
throw new TypeError('Expected a preferences object');
}
const allowed = new Set(['theme', 'compact']);
if (Object.keys(input).some(key => !allowed.has(key))) {
throw new TypeError('Unsupported preference');
}
const theme = Object.hasOwn(input, 'theme') ? input.theme : 'light';
const compact = Object.hasOwn(input, 'compact') ? input.compact : false;
if (!['light', 'dark'].includes(theme) || typeof compact !== 'boolean') {
throw new TypeError('Invalid preference');
}
return Object.assign(Object.create(null), { theme, compact });
}
Only two known properties reach a fresh result, and defaults do not read inherited values. The schema rejects nested objects instead of recursively merging them. Keep this validation at runtime in TypeScript: a type assertion does not validate an HTTP body.
For genuine dictionaries, consider Map. For third-party merge utilities, keep dependencies patched and review how nested keys are handled. Filtering only __proto__ is insufficient for every merge algorithm; paths through constructor and prototype also matter. A Node.js prototype-disabling flag is additional protection, not a substitute for an input contract.
Regression test¶
In a local unit test, accept {theme: 'dark', compact: true} and default an empty object. Reject an array, an unknown key, a nested theme, and parsed JSON containing a reserved property name. Assert Object.getPrototypeOf(result) === null and that Object.prototype remains unchanged. These tests cover this preferences function, not all object operations in the application.
Related: mass assignment, dependency security, and JavaScript and other language guides.
Reference: CWE-1321 — improperly controlled modification of object prototype attributes.