VYPR
Moderate severityNVD Advisory· Published Jan 28, 2026· Updated Jan 29, 2026

CVE-2025-61140

CVE-2025-61140

Description

The value function in jsonpath 1.1.1 lib/index.js is vulnerable to Prototype Pollution.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
jsonpathnpm
< 1.2.01.2.0

Affected products

1

Patches

1
9631412641b7

CVE-2025-61140: Prevent prototype pollution in JSON path handling

https://github.com/dchester/jsonpathShubham SudameFeb 1, 2026via ghsa
2 files changed · +107 1
  • lib/index.js+56 1 modified
    @@ -23,6 +23,7 @@ JSONPath.prototype.parent = function(obj, string) {
       assert.ok(string, "we need a path");
     
       var node = this.nodes(obj, string)[0];
    +  if (node) this._assert_safe_path_keys(node.path);
       var key = node.path.pop(); /* jshint unused:false */
       return this.value(obj, node.path);
     }
    @@ -39,6 +40,7 @@ JSONPath.prototype.apply = function(obj, string, fn) {
       });
     
       nodes.forEach(function(node) {
    +    this._assert_safe_path_keys(node.path);
         var key = node.path.pop();
         var parent = this.value(obj, this.stringify(node.path));
         var val = node.value = fn.call(obj, parent[key]);
    @@ -56,6 +58,7 @@ JSONPath.prototype.value = function(obj, path, value) {
       if (arguments.length >= 3) {
         var node = this.nodes(obj, path).shift();
         if (!node) return this._vivify(obj, path, value);
    +    this._assert_safe_path_keys(node.path);
         var key = node.path.slice(-1).shift();
         var parent = this.parent(obj, this.stringify(node.path));
         parent[key] = value;
    @@ -73,13 +76,16 @@ JSONPath.prototype._vivify = function(obj, string, value) {
       var path = this.parser.parse(string)
         .map(function(component) { return component.expression.value });
     
    +  this._assert_safe_path_keys(path);
    +
       var setValue = function(path, value) {
         var key = path.pop();
         var node = self.value(obj, path);
         if (!node) {
           setValue(path.concat(), typeof key === 'string' ? {} : []);
           node = self.value(obj, path);
         }
    +    self._assert_safe_key(key);
         node[key] = value;
       }
       setValue(path, value);
    @@ -116,6 +122,7 @@ JSONPath.prototype.nodes = function(obj, string, count) {
       if (count === 0) return [];
     
       var path = this.parser.parse(string);
    +  this._assert_safe_components(path);
       var handlers = this.handlers;
     
       var partials = [ { path: ['$'], value: obj } ];
    @@ -206,6 +213,7 @@ JSONPath.prototype._normalize = function(path) {
           if (component == '$' && index === 0) return;
     
           if (typeof component == "string" && component.match("^" + dict.identifier + "$")) {
    +        this._assert_safe_key(component);
     
             _path.push({
               operation: 'member',
    @@ -218,13 +226,15 @@ JSONPath.prototype._normalize = function(path) {
             var type = typeof component == "number" ?
               'numeric_literal' : 'string_literal';
     
    +        if (type === 'string_literal') this._assert_safe_key(component);
    +
             _path.push({
               operation: 'subscript',
               scope: 'child',
               expression: { value: component, type: type }
             });
           }
    -    });
    +    }, this);
     
         return _path;
     
    @@ -236,10 +246,55 @@ JSONPath.prototype._normalize = function(path) {
       throw new Error("couldn't understand path " + path);
     }
     
    +JSONPath.prototype._assert_safe_key = function(key) {
    +  if (_is_unsafe_key(key)) {
    +    throw new Error("Unsafe key in JSONPath: " + key);
    +  }
    +}
    +
    +JSONPath.prototype._assert_safe_path_keys = function(path) {
    +  if (!path || !path.forEach) return;
    +  path.forEach(function(key) {
    +    if (key === '$') return;
    +    if (typeof key === 'string') this._assert_safe_key(key);
    +  }, this);
    +}
    +
    +JSONPath.prototype._assert_safe_components = function(components) {
    +  var self = this;
    +  if (!components || !components.forEach) return;
    +
    +  var checkExpression = function(expression) {
    +    if (!expression) return;
    +    if (expression.type === 'identifier' || expression.type === 'string_literal') {
    +      self._assert_safe_key(expression.value);
    +      return;
    +    }
    +
    +    if (expression.type === 'union' && Array.isArray(expression.value)) {
    +      expression.value.forEach(function(component) {
    +        if (component && component.expression) {
    +          checkExpression(component.expression);
    +        }
    +      });
    +    }
    +  };
    +
    +  components.forEach(function(component) {
    +    if (component && component.expression) {
    +      checkExpression(component.expression);
    +    }
    +  });
    +}
    +
     function _is_string(obj) {
       return Object.prototype.toString.call(obj) == '[object String]';
     }
     
    +function _is_unsafe_key(key) {
    +  return key === '__proto__' || key === 'prototype' || key === 'constructor';
    +}
    +
     JSONPath.Handlers = Handlers;
     JSONPath.Parser = Parser;
     
    
  • test/security.js+51 0 added
    @@ -0,0 +1,51 @@
    +var assert = require('assert');
    +var jp = require('../');
    +
    +suite('security', function() {
    +
    +  var cleanup = function() {
    +    if (Object.prototype.polluted) {
    +      delete Object.prototype.polluted;
    +    }
    +  };
    +
    +  teardown(function() {
    +    cleanup();
    +  });
    +
    +  test('blocks prototype pollution via value()', function() {
    +    cleanup();
    +    var data = {};
    +    assert.throws(function() {
    +      jp.value(data, '$.__proto__.polluted', 'yes');
    +    }, /Unsafe key/);
    +    assert.equal(({}).polluted, undefined);
    +  });
    +
    +  test('blocks prototype pollution via apply()', function() {
    +    cleanup();
    +    var data = { safe: { ok: true } };
    +    assert.throws(function() {
    +      jp.apply(data, '$.__proto__.polluted', function() { return 'yes'; });
    +    }, /Unsafe key/);
    +    assert.equal(({}).polluted, undefined);
    +  });
    +
    +  test('blocks unsafe subscript access', function() {
    +    cleanup();
    +    var data = {};
    +    assert.throws(function() {
    +      jp.query(data, '$["__proto__"]["polluted"]');
    +    }, /Unsafe key/);
    +    assert.equal(({}).polluted, undefined);
    +  });
    +
    +  test('blocks unsafe union access', function() {
    +    cleanup();
    +    var data = { safe: 1 };
    +    assert.throws(function() {
    +      jp.nodes(data, "$['safe','__proto__']");
    +    }, /Unsafe key/);
    +    assert.equal(({}).polluted, undefined);
    +  });
    +});
    

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

7

News mentions

0

No linked articles in our index yet.