-
Notifications
You must be signed in to change notification settings - Fork 148
Add prototype pollution protection to parseJsonWithRepair with stack-based algorithm #15142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d53ff3
54de157
2d2d57c
d91b696
693338c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,101 @@ | ||
| // @ts-check | ||
|
|
||
| /** | ||
| * Sanitizes an object to remove dangerous prototype pollution keys using a stack-based algorithm. | ||
| * This function removes keys that could be used for prototype pollution attacks: | ||
| * - __proto__: JavaScript's prototype chain accessor | ||
| * - constructor: Object constructor property | ||
| * - prototype: Function prototype property | ||
| * | ||
| * Uses an iterative approach with a stack to handle deeply nested structures and | ||
| * protect against stack overflow from malicious recursive object trees. | ||
| * | ||
| * @param {any} obj - The object to sanitize (can be any type) | ||
| * @returns {any} The sanitized object with dangerous keys removed | ||
| * | ||
| * @example | ||
| * // Removes __proto__ key | ||
| * sanitizePrototypePollution({name: "test", __proto__: {isAdmin: true}}) | ||
| * // Returns: {name: "test"} | ||
| * | ||
| * @example | ||
| * // Handles nested objects | ||
| * sanitizePrototypePollution({outer: {__proto__: {bad: true}, safe: "value"}}) | ||
| * // Returns: {outer: {safe: "value"}} | ||
| */ | ||
| function sanitizePrototypePollution(obj) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot add more tests for this function
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added 9 new tests covering:
Total: 76 tests (up from 67). Commit 693338c. |
||
| // Handle non-objects (primitives, null, undefined) | ||
| if (obj === null || typeof obj !== "object") { | ||
| return obj; | ||
| } | ||
|
|
||
| // Dangerous keys that can be used for prototype pollution | ||
| const dangerousKeys = ["__proto__", "constructor", "prototype"]; | ||
|
|
||
| // Track visited objects to handle circular references | ||
| const seen = new WeakMap(); | ||
|
|
||
| // Stack-based traversal to avoid recursion and stack overflow | ||
| // Each entry: { source: original object, target: sanitized object, parent: parent target, key: property key } | ||
| const stack = []; | ||
| const root = Array.isArray(obj) ? [] : {}; | ||
| seen.set(obj, root); | ||
| stack.push({ source: obj, target: root, parent: null, key: null }); | ||
|
|
||
| while (stack.length > 0) { | ||
| const item = stack.pop(); | ||
| if (!item) continue; | ||
| const { source, target } = item; | ||
|
|
||
| if (Array.isArray(source)) { | ||
| // Process array elements | ||
| for (let i = 0; i < source.length; i++) { | ||
| const value = source[i]; | ||
| if (value === null || typeof value !== "object") { | ||
| // Primitive value - copy directly | ||
| target[i] = value; | ||
| } else if (seen.has(value)) { | ||
| // Circular reference detected - use existing sanitized object | ||
| target[i] = seen.get(value); | ||
| } else { | ||
| // New object or array - create sanitized version and add to stack | ||
| const newTarget = Array.isArray(value) ? [] : {}; | ||
| target[i] = newTarget; | ||
| seen.set(value, newTarget); | ||
| stack.push({ source: value, target: newTarget, parent: target, key: i }); | ||
| } | ||
| } | ||
| } else { | ||
| // Process object properties | ||
| for (const key in source) { | ||
| // Skip dangerous keys | ||
| if (dangerousKeys.includes(key)) { | ||
| continue; | ||
| } | ||
| // Only process own properties (not inherited) | ||
| if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
| const value = source[key]; | ||
| if (value === null || typeof value !== "object") { | ||
| // Primitive value - copy directly | ||
| target[key] = value; | ||
| } else if (seen.has(value)) { | ||
| // Circular reference detected - use existing sanitized object | ||
| target[key] = seen.get(value); | ||
| } else { | ||
| // New object or array - create sanitized version and add to stack | ||
| const newTarget = Array.isArray(value) ? [] : {}; | ||
| target[key] = newTarget; | ||
| seen.set(value, newTarget); | ||
| stack.push({ source: value, target: newTarget, parent: target, key: key }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return root; | ||
| } | ||
|
|
||
| /** | ||
| * Attempts to repair malformed JSON strings using various heuristics. | ||
| * This function applies multiple repair strategies to fix common JSON formatting issues: | ||
|
|
@@ -76,4 +172,4 @@ function repairJson(jsonStr) { | |
| return repaired; | ||
| } | ||
|
|
||
| module.exports = { repairJson }; | ||
| module.exports = { repairJson, sanitizePrototypePollution }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot use stack based algo instead of recursion, protect against recursive object tree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Converted to stack-based algorithm with WeakMap for circular reference tracking. This prevents stack overflow from deeply nested structures and handles circular references safely. Commit 693338c.