Skip to content

Commit 8ea07f2

Browse files
committed
chore: update build and CHANGES
1 parent 7886251 commit 8ea07f2

10 files changed

Lines changed: 105 additions & 49 deletions

‎CHANGES.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGES for jsonpath-plus
22

3+
## 11.0.1
4+
5+
- fix(security) nested filter bypass exploit (@andrewmacheret)
6+
37
## 11.0.0
48

59
BREAKING CHANGES

‎badges/tests-badge.svg‎

Lines changed: 1 addition & 1 deletion
Loading

‎dist/index-browser-esm.js‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,26 @@ const isBlockedFunction = value => {
12491249
return typeof value === 'function' && BLOCKED_FUNCTIONS.has(value);
12501250
};
12511251

1252+
/**
1253+
* Guarded `obj[prop]`, applying the same restrictions as a MemberExpression.
1254+
* @param {UnknownResult} obj
1255+
* @param {string} prop
1256+
* @returns {UnknownResult}
1257+
*/
1258+
const getSafeProperty = (obj, prop) => {
1259+
if (obj === undefined || obj === null) {
1260+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1261+
}
1262+
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1263+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1264+
}
1265+
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1266+
if (isBlockedFunction(result)) {
1267+
throw new TypeError('Function constructor is disabled');
1268+
}
1269+
return result;
1270+
};
1271+
12521272
/**
12531273
* @typedef {Record<
12541274
* string,
@@ -1406,16 +1426,7 @@ const SafeEval = {
14061426
: ast.property.name // `object.property` property is Identifier
14071427
);
14081428
const obj = SafeEval.evalAst(ast.object, subs);
1409-
if (obj === undefined || obj === null) {
1410-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1411-
}
1412-
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1413-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1414-
}
1415-
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1416-
if (isBlockedFunction(result)) {
1417-
throw new TypeError('Function constructor is disabled');
1418-
}
1429+
const result = getSafeProperty(obj, prop);
14191430
if (typeof result === 'function') {
14201431
return result.bind(obj); // arrow functions aren't affected by bind.
14211432
}
@@ -2130,7 +2141,9 @@ class JSONPathClass {
21302141
const npath = [nested[2]];
21312142
const valObj2 = /** @type {Record<string, unknown>} */
21322143
val;
2133-
const nvalue = /** @type {ValueType} */nested[1] ? /** @type {Record<string, unknown>} */valObj2[m][nested[1]] : valObj2[m];
2144+
// guard against nested[1] resolving to `constructor`
2145+
const nvalue = /** @type {ValueType} */nested[1] ? getSafeProperty(/** @type {Record<string, unknown>} */
2146+
valObj2[m], nested[1]) : valObj2[m];
21342147
const filterResults = this._trace(npath, nvalue, path, parent, parentPropName, callback, true);
21352148
// eslint-disable-next-line @stylistic/max-len -- Long
21362149
/* c8 ignore next 3 -- Unreachable: _trace always returns array for nested filters */

‎dist/index-browser-esm.min.js‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/index-browser-esm.min.js.map‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/index-browser-umd.cjs‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,26 @@
12551255
return typeof value === 'function' && BLOCKED_FUNCTIONS.has(value);
12561256
};
12571257

1258+
/**
1259+
* Guarded `obj[prop]`, applying the same restrictions as a MemberExpression.
1260+
* @param {UnknownResult} obj
1261+
* @param {string} prop
1262+
* @returns {UnknownResult}
1263+
*/
1264+
const getSafeProperty = (obj, prop) => {
1265+
if (obj === undefined || obj === null) {
1266+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1267+
}
1268+
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1269+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1270+
}
1271+
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1272+
if (isBlockedFunction(result)) {
1273+
throw new TypeError('Function constructor is disabled');
1274+
}
1275+
return result;
1276+
};
1277+
12581278
/**
12591279
* @typedef {Record<
12601280
* string,
@@ -1412,16 +1432,7 @@
14121432
: ast.property.name // `object.property` property is Identifier
14131433
);
14141434
const obj = SafeEval.evalAst(ast.object, subs);
1415-
if (obj === undefined || obj === null) {
1416-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1417-
}
1418-
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1419-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1420-
}
1421-
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1422-
if (isBlockedFunction(result)) {
1423-
throw new TypeError('Function constructor is disabled');
1424-
}
1435+
const result = getSafeProperty(obj, prop);
14251436
if (typeof result === 'function') {
14261437
return result.bind(obj); // arrow functions aren't affected by bind.
14271438
}
@@ -2136,7 +2147,9 @@
21362147
const npath = [nested[2]];
21372148
const valObj2 = /** @type {Record<string, unknown>} */
21382149
val;
2139-
const nvalue = /** @type {ValueType} */nested[1] ? /** @type {Record<string, unknown>} */valObj2[m][nested[1]] : valObj2[m];
2150+
// guard against nested[1] resolving to `constructor`
2151+
const nvalue = /** @type {ValueType} */nested[1] ? getSafeProperty(/** @type {Record<string, unknown>} */
2152+
valObj2[m], nested[1]) : valObj2[m];
21402153
const filterResults = this._trace(npath, nvalue, path, parent, parentPropName, callback, true);
21412154
// eslint-disable-next-line @stylistic/max-len -- Long
21422155
/* c8 ignore next 3 -- Unreachable: _trace always returns array for nested filters */

‎dist/index-browser-umd.min.cjs‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎dist/index-browser-umd.min.cjs.map‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/index-node-cjs.cjs‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,26 @@ const isBlockedFunction = value => {
12531253
return typeof value === 'function' && BLOCKED_FUNCTIONS.has(value);
12541254
};
12551255

1256+
/**
1257+
* Guarded `obj[prop]`, applying the same restrictions as a MemberExpression.
1258+
* @param {UnknownResult} obj
1259+
* @param {string} prop
1260+
* @returns {UnknownResult}
1261+
*/
1262+
const getSafeProperty = (obj, prop) => {
1263+
if (obj === undefined || obj === null) {
1264+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1265+
}
1266+
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1267+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1268+
}
1269+
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1270+
if (isBlockedFunction(result)) {
1271+
throw new TypeError('Function constructor is disabled');
1272+
}
1273+
return result;
1274+
};
1275+
12561276
/**
12571277
* @typedef {Record<
12581278
* string,
@@ -1410,16 +1430,7 @@ const SafeEval = {
14101430
: ast.property.name // `object.property` property is Identifier
14111431
);
14121432
const obj = SafeEval.evalAst(ast.object, subs);
1413-
if (obj === undefined || obj === null) {
1414-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1415-
}
1416-
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1417-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1418-
}
1419-
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1420-
if (isBlockedFunction(result)) {
1421-
throw new TypeError('Function constructor is disabled');
1422-
}
1433+
const result = getSafeProperty(obj, prop);
14231434
if (typeof result === 'function') {
14241435
return result.bind(obj); // arrow functions aren't affected by bind.
14251436
}
@@ -2134,7 +2145,9 @@ class JSONPathClass {
21342145
const npath = [nested[2]];
21352146
const valObj2 = /** @type {Record<string, unknown>} */
21362147
val;
2137-
const nvalue = /** @type {ValueType} */nested[1] ? /** @type {Record<string, unknown>} */valObj2[m][nested[1]] : valObj2[m];
2148+
// guard against nested[1] resolving to `constructor`
2149+
const nvalue = /** @type {ValueType} */nested[1] ? getSafeProperty(/** @type {Record<string, unknown>} */
2150+
valObj2[m], nested[1]) : valObj2[m];
21382151
const filterResults = this._trace(npath, nvalue, path, parent, parentPropName, callback, true);
21392152
// eslint-disable-next-line @stylistic/max-len -- Long
21402153
/* c8 ignore next 3 -- Unreachable: _trace always returns array for nested filters */

‎dist/index-node-esm.js‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,26 @@ const isBlockedFunction = value => {
12511251
return typeof value === 'function' && BLOCKED_FUNCTIONS.has(value);
12521252
};
12531253

1254+
/**
1255+
* Guarded `obj[prop]`, applying the same restrictions as a MemberExpression.
1256+
* @param {UnknownResult} obj
1257+
* @param {string} prop
1258+
* @returns {UnknownResult}
1259+
*/
1260+
const getSafeProperty = (obj, prop) => {
1261+
if (obj === undefined || obj === null) {
1262+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1263+
}
1264+
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1265+
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1266+
}
1267+
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1268+
if (isBlockedFunction(result)) {
1269+
throw new TypeError('Function constructor is disabled');
1270+
}
1271+
return result;
1272+
};
1273+
12541274
/**
12551275
* @typedef {Record<
12561276
* string,
@@ -1408,16 +1428,7 @@ const SafeEval = {
14081428
: ast.property.name // `object.property` property is Identifier
14091429
);
14101430
const obj = SafeEval.evalAst(ast.object, subs);
1411-
if (obj === undefined || obj === null) {
1412-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1413-
}
1414-
if (!Object.hasOwn(obj, prop) && BLOCKED_PROTO_PROPERTIES.has(prop)) {
1415-
throw new TypeError(`Cannot read properties of ${obj} (reading '${prop}')`);
1416-
}
1417-
const result = /** @type {Record<string, UnknownResult>} */obj[prop];
1418-
if (isBlockedFunction(result)) {
1419-
throw new TypeError('Function constructor is disabled');
1420-
}
1431+
const result = getSafeProperty(obj, prop);
14211432
if (typeof result === 'function') {
14221433
return result.bind(obj); // arrow functions aren't affected by bind.
14231434
}
@@ -2132,7 +2143,9 @@ class JSONPathClass {
21322143
const npath = [nested[2]];
21332144
const valObj2 = /** @type {Record<string, unknown>} */
21342145
val;
2135-
const nvalue = /** @type {ValueType} */nested[1] ? /** @type {Record<string, unknown>} */valObj2[m][nested[1]] : valObj2[m];
2146+
// guard against nested[1] resolving to `constructor`
2147+
const nvalue = /** @type {ValueType} */nested[1] ? getSafeProperty(/** @type {Record<string, unknown>} */
2148+
valObj2[m], nested[1]) : valObj2[m];
21362149
const filterResults = this._trace(npath, nvalue, path, parent, parentPropName, callback, true);
21372150
// eslint-disable-next-line @stylistic/max-len -- Long
21382151
/* c8 ignore next 3 -- Unreachable: _trace always returns array for nested filters */

0 commit comments

Comments
 (0)