null
__proto__
Propertyconst parent = { a: 99 };
const child = Object.create(parent);
console.log(child.a); // 99
console.log(child.__proto__ === parent); // true
NOTE: Classes in JavaScript are just syntactic sugar; they create prototype chains under the hood.
for-in
loop manipulation: iterates over both own and inherited properties, so could result in processing additional properties added by an attackerconst user = { name: 'Full Name' }; // Regular user
const malicious = { isAdmin: true }; // isAdmin is true for administrators only
user['__proto__'] = malicious; // Pollution!
console.log(user.isAdmin); // true (Escalation of privilege!)
function safeProfile(req, res) {
const [user] = filter(users, 'email', req.body.email);
if (user) {
const updatedUser = merge({}, req.body);
Object.assign(user, updatedUser);
}
res.json([user]);
}
function merge(target, source) {
for (let prop in source) {
if (typeof target[prop] === 'object' && typeof source[prop] === 'object') {
merge(target[prop], source[prop]);
}
target[prop] = source[prop];
}
return target;
}
Object.freeze
Object.create(null, ...)
Map
instead of {}
Map
has richer API and better performance// utils.js
export function merge(target, source) {
for (let prop in source) {
if (typeof target[prop] === 'object' && typeof source[prop] === 'object') {
merge(target[prop], source[prop]);
}
target[prop] = source[prop];
}
return target;
}
// utils.test.js
import { merge } from './utils'
describe('merge', () => {
it('prevents prototype pollution', () => {
const malicious = JSON.parse('{"__proto__": {"injected": 0}}');
merge({}, malicious);
const o = {};
// Should fail if prototype is polluted
expect(o.injected).toStrictEqual(undefined);
})
})
npm audit