VYPR
High severity8.3NVD Advisory· Published Jun 17, 2024· Updated Apr 15, 2026

CVE-2024-36577

CVE-2024-36577

Description

apphp js-object-resolver < 3.1.1 is vulnerable to Prototype Pollution via Module.setNestedProperty.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@apphp/object-resolvernpm
< 3.1.13.1.1

Patches

1
7e347a26bf04

Fix for prototype pollution vulnerability

https://github.com/apphp/js-object-resolversamuelakopyanApr 6, 2024via ghsa
2 files changed · +59 7
  • dist/object-resolver.js+21 6 modified
    @@ -241,20 +241,33 @@ const fetchLastNestedProperty = function (obj, path) {
      * @param {*} value - The value to set for the nested property.
      */
     const setNestedProperty = function (obj, path, value) {
    -  const keys = Array.isArray(path) ? path : path.split('.');
    +  let keys = path;
       let current = obj;
     
    +  if (typeof keys === 'string') {
    +    keys = keys.split('.');
    +  }
    +
    +  if (!Array.isArray(keys)) {
    +    throw new Error('Path must be a string or an array');
    +  }
    +
       for (let i = 0; i < keys.length; i++) {
         const key = keys[i];
     
    +    // Prevent prototype pollution
    +    if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
    +      throw new Error('Invalid property key');
    +    }
    +
         // Check if the current property is an array and if the key has array notation
         const isArray = Array.isArray(current);
         const isArrayNotation = key.includes('[') && key.endsWith(']');
         const isNumericKey = /^\d+$/.test(key.replace(/\[.*\]/, ''));
     
         if (isArray && isArrayNotation && isNumericKey) {
           const [arrayKey, indexKey] = key.split(/\[|\]/).filter(Boolean);
    -      const index = parseInt(indexKey);
    +      const index = parseInt(indexKey, 10);
     
           while (current[arrayKey].length <= index) {
             current[arrayKey].push(null); // Ensure the array is long enough
    @@ -264,16 +277,18 @@ const setNestedProperty = function (obj, path, value) {
             // Last key in the path, set the value
             current[arrayKey][index] = value;
           } else {
    -        // Continue into the nested object
    -        current = current[arrayKey][index] = current[arrayKey][index] || {};
    +        // Prevent undefined objects in the path
    +        if (!current[arrayKey][index]) current[arrayKey][index] = {};
    +        current = current[arrayKey][index];
           }
         } else {
           if (i === keys.length - 1) {
             // Last key in the path, set the value
             current[key] = value;
           } else {
    -        // Continue into the nested object
    -        current = current[key] = current[key] || {};
    +        // Prevent undefined objects in the path
    +        if (!current[key]) current[key] = {};
    +        current = current[key];
           }
         }
       }
    
  • tests/setNestedProperty.test.js+38 1 modified
    @@ -1,8 +1,45 @@
     const {setNestedProperty: setNestedPropertyTest} = require('../dist/object-resolver');
     
     describe('Test function setNestedProperty', () => {
    +  // Test cases for setNestedProperty
    +
    +  let obj;
    +
    +  beforeEach(() => {
    +    obj = {
    +      existing: { property: 'value' },
    +      arrayProperty: [{ nestedArray: 'nestedValue' }]
    +    };
    +  });
    +
    +  test('Should throw an error when trying to modify __proto__', () => {
    +    const obj = {};
    +    expect(() => setNestedPropertyTest(obj, '__proto__.polluted', 'yes')).toThrow('Invalid property key');
    +  });
    +
    +  test('Should throw an error for non-string and non-array path', () => {
    +    expect(() => setNestedPropertyTest(obj, null, 'newValue')).toThrow('Path must be a string or an array');
    +    expect(() => setNestedPropertyTest(obj, 42, 'newValue')).toThrow('Path must be a string or an array');
    +    expect(() => setNestedPropertyTest(obj, {}, 'newValue')).toThrow('Path must be a string or an array');
    +  });
    +
    +  test('Should throw an error when trying to modify prototype', () => {
    +    expect(() => setNestedPropertyTest(obj, 'prototype.polluted', 'yes')).toThrow('Invalid property key');
    +  });
    +
    +  test('Should throw an error when trying to modify constructor', () => {
    +    expect(() => setNestedPropertyTest(obj, 'constructor.polluted', 'yes')).toThrow('Invalid property key');
    +  });
    +
    +  test('Sets a value at an existing index in an array', () => {
    +    const obj = {
    +      numbers: [1, 2, 3]
    +    };
    +
    +    setNestedPropertyTest(obj, 'numbers.1', 99);
    +    expect(obj.numbers[1]).toBe(99);
    +  });
     
    -// Test cases for setNestedProperty
       test('Should set a deeply nested property', () => {
         const obj = {};
         setNestedPropertyTest(obj, 'user.profile.name', 'John Doe');
    

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

4

News mentions

0

No linked articles in our index yet.