From 4526653bbb0ae17ce7038ae4f885b6d778b5b995 Mon Sep 17 00:00:00 2001 From: tuanaiseo Date: Sun, 5 Apr 2026 18:09:54 +0700 Subject: [PATCH] fix(security): prototype pollution risk in recursive object merge `deepMerge` only blocks the `__proto__` key, but still recursively merges attacker-controlled nested objects and uses `Object.assign(a, b)` on non-object branches. This can allow pollution through `constructor.prototype`-style payloads or unsafe key propagation when untrusted input reaches merge paths. Affected files: utils.js Signed-off-by: tuanaiseo <221258316+tuanaiseo@users.noreply.github.com> --- lib/utils.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 4e7a205..b613f55 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -156,10 +156,17 @@ exports.parseS3 = (path) => { // Deep Merge exports.deepMerge = (a, b) => { + const blockedKeys = ['__proto__', 'prototype', 'constructor']; Object.keys(b).forEach((key) => { - if (key === '__proto__') return; - if (typeof b[key] !== 'object') return Object.assign(a, b); - return key in a ? this.deepMerge(a[key], b[key]) : Object.assign(a, b); + if (blockedKeys.includes(key)) return; + if (b[key] && typeof b[key] === 'object' && !Array.isArray(b[key])) { + if (!a[key] || typeof a[key] !== 'object' || Array.isArray(a[key])) { + a[key] = {}; + } + this.deepMerge(a[key], b[key]); + } else { + a[key] = b[key]; + } }); return a; };