VYPR
Critical severityNVD Advisory· Published Feb 11, 2026· Updated Feb 12, 2026

Prototype pollution in set-in

CVE-2026-26021

Description

set-in provides the set value of nested associative structure given array of keys. A prototype pollution vulnerability exists in the the npm package set-in (>=2.0.1, < 2.0.5). Despite a previous fix that attempted to mitigate prototype pollution by checking whether user input contained a forbidden key, it is still possible to pollute Object.prototype via a crafted input using Array.prototype. This has been fixed in version 2.0.5.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
set-innpm
>= 2.0.1, < 2.0.52.0.5

Affected products

1

Patches

4
b8e1dabfdbd3

Merge commit from fork

https://github.com/ahdinosaur/set-inKevin George LeoFeb 11, 2026via ghsa
3 files changed · +18 4
  • index.js+3 3 modified
    @@ -13,8 +13,6 @@ function setIn (object, path, value) {
       return recursivelySetIn(object, path, value, 0)
     }
     
    -const POLLUTED_KEYS = ['__proto__', 'constructor', 'prototype']
    -
     function recursivelySetIn (object, path, value, index) {
       if (index === path.length) {
         return value
    @@ -25,7 +23,9 @@ function recursivelySetIn (object, path, value, index) {
       var key = path[index]
     
       // CVE-2020-28273
    -  assert.ok(!POLLUTED_KEYS.includes(key), `setIn: ${key} is disallowed in path due to possible prototype pollution attack.`)
    +  if(key == "constructor" || key == "prototype" || key == "__proto__"){
    +    throw `setIn: ${key} is disallowed in path due to possible prototype pollution attack.`
    +  }
     
       if (key === '-') {
         assert.ok(Array.isArray(object), 'setIn: "-" in path must correspond to array.')
    
  • package.json+1 1 modified
    @@ -32,4 +32,4 @@
         "tape": "^4.6.0"
       },
       "dependencies": {}
    -}
    +}
    \ No newline at end of file
    
  • test/index.js+14 0 modified
    @@ -199,3 +199,17 @@ test('prototype pollution', function (t) {
       t.end()
     })
     
    +test('resilience against hijacked Array.prototype.includes', function (t) {
    +  const originalIncludes = Array.prototype.includes;  
    +  Array.prototype.includes = () => false;
    +  const obj = {};
    +
    +  t.throws(() => {
    +    setIn(obj, ['constructor', 'prototype', 'polluted'], 'yes');
    +  }, 'should still block prototype access even if Array.includes is hijacked');
    +
    +  t.notEqual({}.polluted, 'yes', 'global prototype should not be polluted');
    +
    +  Array.prototype.includes = originalIncludes;  
    +  t.end();
    +});
    \ No newline at end of file
    
34842cc02de3

clean up and add tests

https://github.com/ahdinosaur/set-inMichael WilliamsApr 21, 2022via ghsa
2 files changed · +17 8
  • index.js+8 7 modified
    @@ -13,6 +13,8 @@ function setIn (object, path, value) {
       return recursivelySetIn(object, path, value, 0)
     }
     
    +const POLLUTED_KEYS = ['__proto__', 'constructor', 'prototype']
    +
     function recursivelySetIn (object, path, value, index) {
       if (index === path.length) {
         return value
    @@ -21,7 +23,9 @@ function recursivelySetIn (object, path, value, index) {
       object = object || {}
     
       var key = path[index]
    -  assert.ok(!POLLUTED_KEYS.includes(key), `recursivelySetIn: ${key} is disallowed in path due to possible prototype pollution attack.`)
    +
    +  // CVE-2020-28273
    +  assert.ok(!POLLUTED_KEYS.includes(key), `setIn: ${key} is disallowed in path due to possible prototype pollution attack.`)
     
       if (key === '-') {
         assert.ok(Array.isArray(object), 'setIn: "-" in path must correspond to array.')
    @@ -30,15 +34,12 @@ function recursivelySetIn (object, path, value, index) {
     
       var next = recursivelySetIn(object[key], path, value, ++index)
     
    -  return set(object, key, next)
    -}
    +  set(object, key, next)
     
    -const POLLUTED_KEYS = ['__proto__', 'constructor', 'prototype']
    +  return object
    +}
     
     function set (object, key, value) {
    -  // CVE-2020-28273
    -  assert.ok(!POLLUTED_KEYS.includes(key), `setIn: ${key} is disallowed in path due to possible prototype pollution attack.`)
    -
       object[key] = value
       return object
     }
    
  • test/index.js+9 1 modified
    @@ -178,16 +178,24 @@ test('object with custom get function', function (t) {
     
     test('prototype pollution', function (t) {
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['__proto__'], { a: 'x' }))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, [['__proto__']], { a: 'x' }))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['__proto__', 'a'], 'x'))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, [['__proto__'], 'a'], 'x'))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['a', '__proto__'], 'x'))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['a', ['__proto__']], 'x'))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['constructor', 'prototype'], { a: 'x' }))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['constructor', 'prototype', 'a'], 'x'))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['prototype', 'a'], 'x'))
    +  t.notEqual({}.a, 'x')
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['constructor'], 'x'))
    -
       t.end()
     })
     
    
d87c1a09fa2e

check all the keys for prototype pollution (#6)

https://github.com/ahdinosaur/set-inErik Krogh KristensenApr 21, 2022via ghsa
1 file changed · +1 0
  • index.js+1 0 modified
    @@ -21,6 +21,7 @@ function recursivelySetIn (object, path, value, index) {
       object = object || {}
     
       var key = path[index]
    +  assert.ok(!POLLUTED_KEYS.includes(key), `recursivelySetIn: ${key} is disallowed in path due to possible prototype pollution attack.`)
     
       if (key === '-') {
         assert.ok(Array.isArray(object), 'setIn: "-" in path must correspond to array.')
    
6bad255961d3

better fix for prototype pollution vulnerability

https://github.com/ahdinosaur/set-inMichael WilliamsMar 7, 2022via ghsa
3 files changed · +3359 27
  • index.js+9 12 modified
    @@ -5,6 +5,10 @@ module.exports = setIn
     function setIn (object, path, value) {
       assert.equal(typeof object, 'object', 'setIn: expected object as first argument.')
       assert.ok(Array.isArray(path), 'setIn: expected array path as second argument.')
    +  assert.ok(
    +    path.every(p => typeof p === 'number' || typeof p === 'string'),
    +    'setIn: expected array path (of strings and numbers) as second argument.'
    +  )
     
       return recursivelySetIn(object, path, value, 0)
     }
    @@ -16,18 +20,6 @@ function recursivelySetIn (object, path, value, index) {
     
       object = object || {}
     
    -  // https://stackoverflow.com/a/60850027
    -  assert.ok(
    -    path[index] !== '__proto__',
    -    'setIn: "__proto__" is disallowed in path due to possible prototype pollution attack.'
    -  )
    -  if (index < path.length - 1) {
    -    assert.ok(
    -      path[index] !== 'constructor' && path[index + 1] !== 'prototype',
    -      'setIn: ["constructor", "prototype"] is disallowed in path due to possible prototype pollution attack.'
    -    )
    -  }
    -
       var key = path[index]
     
       if (key === '-') {
    @@ -40,7 +32,12 @@ function recursivelySetIn (object, path, value, index) {
       return set(object, key, next)
     }
     
    +const POLLUTED_KEYS = ['__proto__', 'constructor', 'prototype']
    +
     function set (object, key, value) {
    +  // CVE-2020-28273
    +  assert.ok(!POLLUTED_KEYS.includes(key), `setIn: ${key} is disallowed in path due to possible prototype pollution attack.`)
    +
       object[key] = value
       return object
     }
    
  • package-lock.json+3344 13 modified
  • test/index.js+6 2 modified
    @@ -178,12 +178,16 @@ test('object with custom get function', function (t) {
     
     test('prototype pollution', function (t) {
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['__proto__'], { a: 'x' }))
    +  t.throws(() => setIn({ 'a': { 'b': 'c' }}, [['__proto__']], { a: 'x' }))
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['__proto__', 'a'], 'x'))
    +  t.throws(() => setIn({ 'a': { 'b': 'c' }}, [['__proto__'], 'a'], 'x'))
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['a', '__proto__'], 'x'))
    +  t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['a', ['__proto__']], 'x'))
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['constructor', 'prototype'], { a: 'x' }))
       t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['constructor', 'prototype', 'a'], 'x'))
    -  setIn({ 'a': { 'b': 'c' }}, ['prototype', 'a'], 'x')
    -  setIn({ 'a': { 'b': 'c' }}, ['constructor'], 'x')
    +  t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['prototype', 'a'], 'x'))
    +  t.throws(() => setIn({ 'a': { 'b': 'c' }}, ['constructor'], 'x'))
    +
       t.end()
     })
     
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.