Skip to content

Commit 5b8b271

Browse files
author
David Chester
committed
version 1.3.0
1 parent b61111f commit 5b8b271

3 files changed

Lines changed: 156 additions & 7 deletions

File tree

jsonpath.js

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*! jsonpath 1.2.1 */
1+
/*! jsonpath 1.3.0 */
22

33
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jsonpath = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({"./aesprim":[function(require,module,exports){
44
/*
@@ -4621,6 +4621,152 @@ var slice = require('./slice');
46214621
var _evaluate = require('static-eval');
46224622
var _uniq = require('underscore').uniq;
46234623

4624+
// Property names that must never be accessible in expressions.
4625+
// Mitigates prototype pollution and constructor escape attacks.
4626+
var UNSAFE_PROPERTY_NAMES = Object.create(null);
4627+
4628+
/* jshint -W069: true */
4629+
UNSAFE_PROPERTY_NAMES['constructor'] = true;
4630+
UNSAFE_PROPERTY_NAMES['__proto__'] = true;
4631+
UNSAFE_PROPERTY_NAMES['prototype'] = true;
4632+
/* jshint -W069: false */
4633+
4634+
function isUnsafePropertyName(name) {
4635+
return typeof name === 'string' && UNSAFE_PROPERTY_NAMES[name] === true;
4636+
}
4637+
4638+
function isSafeAst(ast) {
4639+
if (!ast || typeof ast !== 'object') return false;
4640+
4641+
function walk(node) {
4642+
if (!node || typeof node !== 'object' || !node.type) {
4643+
return false;
4644+
}
4645+
4646+
switch (node.type) {
4647+
4648+
// ===== SAFE TERMINALS =====
4649+
4650+
case 'Literal':
4651+
return true;
4652+
4653+
case 'Identifier':
4654+
// Only allow the special scope identifier
4655+
return node.name === '@';
4656+
4657+
4658+
// ===== PROPERTY ACCESS =====
4659+
4660+
case 'MemberExpression': {
4661+
if (!walk(node.object)) {
4662+
return false;
4663+
}
4664+
4665+
// Non-computed: obj.property
4666+
if (!node.computed && node.property.type === 'Identifier') {
4667+
if (isUnsafePropertyName(node.property.name)) {
4668+
return false;
4669+
}
4670+
return true;
4671+
}
4672+
4673+
// Computed: obj["property"]
4674+
if (node.computed) {
4675+
if (!walk(node.property)) {
4676+
return false;
4677+
}
4678+
4679+
if (
4680+
node.property.type === 'Literal' &&
4681+
isUnsafePropertyName(String(node.property.value))
4682+
) {
4683+
return false;
4684+
}
4685+
4686+
return true;
4687+
}
4688+
4689+
return false;
4690+
}
4691+
4692+
4693+
// ===== EXPRESSIONS =====
4694+
4695+
case 'UnaryExpression':
4696+
return walk(node.argument);
4697+
4698+
case 'BinaryExpression':
4699+
case 'LogicalExpression':
4700+
return walk(node.left) && walk(node.right);
4701+
4702+
case 'ConditionalExpression':
4703+
return (
4704+
walk(node.test) &&
4705+
walk(node.consequent) &&
4706+
walk(node.alternate)
4707+
);
4708+
4709+
case 'ArrayExpression':
4710+
for (var i = 0; i < node.elements.length; i++) {
4711+
if (!walk(node.elements[i])) {
4712+
return false;
4713+
}
4714+
}
4715+
return true;
4716+
4717+
case 'ObjectExpression':
4718+
for (var j = 0; j < node.properties.length; j++) {
4719+
var prop = node.properties[j];
4720+
4721+
// Reject unsafe keys
4722+
if (
4723+
prop.key &&
4724+
(
4725+
(prop.key.type === 'Identifier' &&
4726+
isUnsafePropertyName(prop.key.name)) ||
4727+
(prop.key.type === 'Literal' &&
4728+
isUnsafePropertyName(String(prop.key.value)))
4729+
)
4730+
) {
4731+
return false;
4732+
}
4733+
4734+
if (!walk(prop.value)) {
4735+
return false;
4736+
}
4737+
}
4738+
return true;
4739+
4740+
4741+
// ===== EXPLICITLY REJECT DANGEROUS TYPES =====
4742+
// Security: do not rely on default deny; list each code-execution / escape vector.
4743+
4744+
case 'CallExpression':
4745+
case 'NewExpression':
4746+
case 'FunctionExpression':
4747+
case 'ArrowFunctionExpression':
4748+
case 'ThisExpression':
4749+
case 'AssignmentExpression':
4750+
case 'UpdateExpression':
4751+
case 'SequenceExpression':
4752+
case 'TemplateLiteral':
4753+
case 'TemplateElement':
4754+
case 'TaggedTemplateExpression':
4755+
case 'ReturnStatement':
4756+
case 'ExpressionStatement':
4757+
return false;
4758+
4759+
4760+
// ===== DEFAULT DENY =====
4761+
4762+
default:
4763+
return false;
4764+
}
4765+
}
4766+
4767+
return walk(ast);
4768+
}
4769+
46244770
var Handlers = function() {
46254771
return this.initialize.apply(this, arguments);
46264772
}
@@ -4857,8 +5003,11 @@ function _traverse(passable) {
48575003
}
48585004
}
48595005

4860-
function evaluate() {
4861-
try { return _evaluate.apply(this, arguments) }
5006+
function evaluate(ast, scope) {
5007+
if (!isSafeAst(ast)) {
5008+
throw new Error('Unsafe expression: script and filter expressions may only access the current node (@) with safe property names');
5009+
}
5010+
try { return _evaluate(ast, scope) }
48625011
catch (e) { }
48635012
}
48645013

0 commit comments

Comments
 (0)