# JavaScript / TypeScript Sharp Edges ## Loose Equality Coercion ```javascript // DANGEROUS: == coerces types unpredictably "0" == false // true "" == false // true "" == 0 // true [] == false // true [] == ![] // true (wat) null == undefined // true // Security implications: if (userRole == "admin") { // What if userRole is 0? grantAdmin(); } 0 == "admin" // false, but... 0 == "" // true ``` **Fix**: Always use `===` for strict equality. ## Prototype Pollution ```javascript // DANGEROUS: Merging untrusted objects function merge(target, source) { for (let key in source) { target[key] = source[key]; // Includes __proto__! } } // Attacker sends: {"__proto__": {"isAdmin": true}} merge({}, JSON.parse(userInput)); // Now ALL objects have isAdmin ({}).isAdmin // true const user = {}; user.isAdmin // true - authentication bypassed! // Also via constructor.prototype // {"constructor": {"prototype": {"isAdmin": true}}} ``` **Fix**: ```javascript // Check for dangerous keys const dangerous = ['__proto__', 'constructor', 'prototype']; if (dangerous.includes(key)) continue; // Or use Object.create(null) for dictionary objects const dict = Object.create(null); // No prototype chain // Or use Map instead of objects const map = new Map(); ``` ## Regular Expression DoS (ReDoS) ```javascript // DANGEROUS: Catastrophic backtracking const regex = /^(a+)+$/; regex.test("aaaaaaaaaaaaaaaaaaaaaaaaaaaa!"); // Exponential time - freezes the event loop // Dangerous patterns: // - Nested quantifiers: (a+)+, (a*)* // - Overlapping alternatives: (a|a)+ // - Greedy quantifiers with overlap: .*.* // Real example from ua-parser-js CVE: /\s*(;|\s)\s*/ // Fine /(a|aa)+/ // ReDoS! ``` **Detection**: Look for nested quantifiers or overlapping alternatives in regex. ## parseInt Without Radix ```javascript // DANGEROUS: Behavior varies parseInt("08"); // 8 (modern JS), was 0 in ES3 (octal) parseInt("0x10"); // 16 - hex prefix always recognized parseInt("10", 0); // 10 or error depending on engine parseInt("10", 1); // NaN - radix 1 invalid // DANGEROUS: Unexpected results parseInt("123abc"); // 123 - stops at first non-digit parseInt("abc123"); // NaN - starts with non-digit ``` **Fix**: Always specify radix: `parseInt("08", 10)` ## This Binding ```javascript // DANGEROUS: 'this' depends on how function is called const obj = { value: 42, getValue: function() { return this.value; } }; obj.getValue(); // 42 const fn = obj.getValue; fn(); // undefined - 'this' is global/undefined // DANGEROUS: In callbacks setTimeout(obj.getValue, 100); // 'this' is global/undefined // DANGEROUS: In event handlers button.addEventListener('click', obj.getValue); // 'this' is button ``` **Fix**: Use arrow functions or `.bind()`. ## Array Methods That Mutate ```javascript // These MUTATE the original array: arr.push(x); // Adds to end arr.pop(); // Removes from end arr.shift(); // Removes from start arr.unshift(x); // Adds to start arr.splice(i, n); // Removes/inserts arr.sort(); // Sorts IN PLACE arr.reverse(); // Reverses IN PLACE arr.fill(x); // Fills IN PLACE // These return NEW arrays: arr.slice(); arr.concat(); arr.map(); arr.filter(); // DANGEROUS: Sorting numbers [1, 10, 2].sort(); // [1, 10, 2] - string comparison! // Fix: [1, 10, 2].sort((a, b) => a - b); // [1, 2, 10] ``` ## Type Coercion in Operations ```javascript // DANGEROUS: + is overloaded for concatenation "5" + 3 // "53" (string) 5 + "3" // "53" (string) 5 - "3" // 2 (number) "5" - 3 // 2 (number) // DANGEROUS: Comparison with type coercion "10" > "9" // false (string comparison: "1" < "9") "10" > 9 // true (numeric comparison) ``` ## eval and Dynamic Code ```javascript // DANGEROUS: eval executes arbitrary code eval(userInput); // DANGEROUS: Function constructor new Function(userInput)(); // DANGEROUS: setTimeout/setInterval with string setTimeout(userInput, 1000); // Executes as code! // DANGEROUS: Template injection const template = userInput; // "${process.exit()}" eval(`\`${template}\``); ``` ## Object Property Access ```javascript // DANGEROUS: Bracket notation with user input const obj = { admin: false }; const key = userInput; // Could be "__proto__", "constructor", etc. obj[key] = true; // Prototype pollution! // DANGEROUS: in operator checks prototype chain "toString" in {} // true - inherited from Object.prototype // Fix: Use hasOwnProperty ({}).hasOwnProperty("toString") // false Object.hasOwn({}, "toString") // false (ES2022) ``` ## Async/Await Pitfalls ```javascript // DANGEROUS: Unhandled promise rejection async function riskyOperation() { throw new Error("oops"); } riskyOperation(); // Unhandled rejection - may crash Node.js // DANGEROUS: Missing await async function process() { validateInput(); // Forgot await - validation not complete! doSensitiveOperation(); } // DANGEROUS: Sequential when parallel is possible async function slow() { const a = await fetchA(); // Waits const b = await fetchB(); // Then waits return a + b; } // Better: parallel async function fast() { const [a, b] = await Promise.all([fetchA(), fetchB()]); return a + b; } ``` ## JSON Parse Issues ```javascript // DANGEROUS: __proto__ in JSON JSON.parse('{"__proto__": {"isAdmin": true}}'); // Creates object with __proto__ key, but doesn't pollute // However, if merged into another object: Object.assign({}, JSON.parse(userInput)); // Can pollute if userInput has __proto__ // DANGEROUS: Large numbers lose precision JSON.parse('{"id": 9007199254740993}'); // id becomes 9007199254740992 (precision loss) ``` ## TypeScript-Specific ```typescript // DANGEROUS: Type assertions bypass checking const user = userData as Admin; // No runtime check! user.adminMethod(); // Runtime error if not actually Admin // DANGEROUS: any escapes type system function process(data: any) { data.whatever(); // No type checking } // DANGEROUS: Non-null assertion function greet(name: string | null) { console.log(name!.toUpperCase()); // Crash if null! } // DANGEROUS: Type guards can lie function isAdmin(user: User): user is Admin { return true; // Wrong! TypeScript trusts this } ``` ## Detection Patterns | Pattern | Risk | |---------|------| | `==` instead of `===` | Type coercion bugs | | `obj[userInput]` | Prototype pollution | | `/__proto__|constructor|prototype/` in merge | Pollution vectors | | `(a+)+`, `(.*)+` in regex | ReDoS | | `parseInt(x)` without radix | Parsing inconsistency | | `eval(`, `Function(`, `setTimeout(string` | Code execution | | `.sort()` on numbers without comparator | String sort | | `as Type` assertions | Runtime type mismatch | | `!` non-null assertion | Null pointer crash | | Missing `await` before async call | Race condition |