Detects variable[key] written as an assignment target, or read and then invoked (prototype pollution, method injection). This rule is part of eslint-plugin-secure-coding and provides LLM-optimized error messages with fix suggestions.
🚨 Security rule | 💡 Provides LLM-optimized guidance | 🔧 Opt-in — not in recommended
Opt-in only. This rule is deliberately absent from recommended,
recommended-strict and owasp-top-10. It reports every computed member
access it cannot prove safe, so on ordinary application code it fires on
obj[key] throughout — a trade worth making deliberately, not one a preset
should make for you. It ships in strict, or enable it explicitly:
Vulnerability: Object injection (specifically Prototype Pollution) occurs when user input is used to access or modify properties of an object, particularly using bracket notation (e.g., obj[userInput]) without validation.
Risk: Attackers can modify critical properties like __proto__, constructor, or prototype, affecting the behavior of all objects in the application. This can lead to Denial of Service (DoS), bypass of security checks, or even Remote Code Execution (RCE) depending on how the polluted properties are used.
A key is cleared only by evidence about where its value comes from: a const
bound to a literal, an import, a for…of over a literal list, an Object.keys /
for…in key, a numeric expression, or a guard (Object.hasOwn, an allowlist
includes, k in o) on the branch that reaches the write.
A key's spelling never clears it.PATH_METADATA, userStatus and
eventType are reported exactly like k when nothing in the file proves their
value — a parameter spelled like a constant is still whatever the caller
passed:
const PATH_METADATA = 'path';obj[PATH_METADATA] = value; // ✅ cleared — the declaration is a literalfunction set(o, MODE, v) { o[MODE] = v; // ❌ reported — MODE is a parameter; its name proves nothing}
// Use Map for dynamic key-value storageconst config = new Map<string, any>();config.set(userKey, value);// Use Object.create(null) for clean objectsconst safeObj = Object.create(null);safeObj[userKey] = value; // Safe because no prototype// Property whitelistingconst ALLOWED_KEYS = ['name', 'age', 'email', 'role'];if (ALLOWED_KEYS.includes(userKey)) { obj[userKey] = value;}// A plain read cannot pollute a prototype, so it is not reported.// (Reading an attacker-chosen field is information exposure, CWE-200 —// a different weakness this rule does not claim.)const value = obj[userKey];
A read IS reported when its result is invoked — handlers[req.body.action](payload)
resolves "constructor" to the Object constructor and calls it (method injection).
// Attacker controls userInput = "__proto__"// This pollutes ALL objects in the applicationobj[userInput] = { malicious: () => console.log('HACKED') };// Now ALL objects have the malicious propertyconst innocent = {};console.log(innocent.malicious); // Function exists!
Property NAMES treated as dangerous where the name is visible in the source — obj['__proto__'] = v. It cannot apply to a dynamic key (obj[k] = v), because there is no name to compare, and it does not gate the CWE-1321 traversal check, which is a fact about the language rather than a vocabulary. Set it to [] to stop reporting literal dangerous-property writes.