From 565b2a2eb6bd8f4ea7749673880009630e82538a Mon Sep 17 00:00:00 2001 From: dmlvr Date: Mon, 22 Jun 2026 17:45:25 +0300 Subject: [PATCH 1/8] prevent prototype pollution via unsafe path --- .../devextreme/js/__internal/core/m_errors.ts | 2 + .../js/__internal/core/utils/m_data.ts | 26 +++++- .../tests/DevExpress.core/utils.data.tests.js | 83 +++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/core/m_errors.ts b/packages/devextreme/js/__internal/core/m_errors.ts index ddfbb4c4345b..46efeabe1290 100644 --- a/packages/devextreme/js/__internal/core/m_errors.ts +++ b/packages/devextreme/js/__internal/core/m_errors.ts @@ -64,6 +64,8 @@ export default errorUtils({ E0122: 'AIIntegration: The sendRequest method is missing.', + E0123: 'Prototype pollution attempt detected: the path contains an unsafe fragment \'{0}\'', + W0000: '\'{0}\' is deprecated in {1}. {2}', W0001: '{0} - \'{1}\' option is deprecated in {2}. {3}', diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index 9c1dce560ef6..9a59ec610fef 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -17,6 +17,10 @@ const bracketsToDots = function (expr) { .replace(/\]/g, ''); }; +const UNSAFE_PATH_FRAGMENTS = new Set(['__proto__', 'constructor', 'prototype']); + +const isUnsafePathFragment = (name: string): boolean => UNSAFE_PATH_FRAGMENTS.has(name); + export const getPathParts = function (name) { return bracketsToDots(name).split('.'); }; @@ -81,6 +85,11 @@ export const compileGetter = function (expr) { const pathPart = path[i]; + if (isUnsafePathFragment(pathPart)) { + errors.log('E0123', pathPart); + return; + } + if (hasDefaultValue && isObject(current) && !(pathPart in current)) { return options.defaultValue; } @@ -130,13 +139,23 @@ function combineGetters(getters) { for (let i = 0; i < last; i++) { const pathItem = path[i]; + if (isUnsafePathFragment(pathItem)) { + errors.log('E0123', pathItem); + return; + } if (!(pathItem in current)) { current[pathItem] = { }; } current = current[pathItem]; } - current[path[last]] = value; + const lastPathItem = path[last]; + if (isUnsafePathFragment(lastPathItem)) { + errors.log('E0123', lastPathItem); + return; + } + + current[lastPathItem] = value; }); return result; }; @@ -170,6 +189,11 @@ export const compileSetter = function (expr) { let currentValue = unwrap(obj, options); expr.forEach(function (propertyName, levelIndex) { + if (isUnsafePathFragment(propertyName)) { + errors.log('E0123', propertyName); + return; + } + let propertyValue = readPropValue(currentValue, propertyName, options); const isPropertyFunc = !options.functionsAsIs && isFunction(propertyValue) && !isWrapped(propertyValue); diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js index 3e88a51f741c..f41370fbdc69 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js @@ -2,6 +2,7 @@ import { compileGetter as GETTER, compileSetter as SETTER } from 'core/utils/data'; +import errors from 'core/errors'; import variableWrapper from 'core/utils/variable_wrapper'; const mockVariableWrapper = { @@ -480,6 +481,88 @@ QUnit.module('setter', () => { }); +QUnit.module('prototype pollution protection', { + beforeEach: function() { + sinon.spy(errors, 'log'); + }, + afterEach: function() { + errors.log.restore(); + delete Object.prototype['pp_dx']; + delete Object.prototype['pp_dx2']; + } +}, () => { + + test('compileSetter logs error and skips assignment for __proto__ path fragment', function(assert) { + SETTER('__proto__.pp_dx')({}, 'yes', { functionsAsIs: true }); + + assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); + assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); + }); + + test('compileSetter logs error and skips assignment for constructor path fragment', function(assert) { + SETTER('constructor.prototype.pp_dx')({}, 'yes', { functionsAsIs: true }); + + assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); + }); + + test('compileSetter logs error and skips assignment for prototype path fragment', function(assert) { + const fn = function() {}; + SETTER('prototype.pp_dx')(fn, 'yes', { functionsAsIs: true }); + + assert.strictEqual(errors.log.calledWith('E0123', 'prototype'), true, 'should log E0123 for prototype'); + assert.strictEqual(fn.prototype.pp_dx, undefined, 'function prototype must not be modified'); + }); + + test('compileSetter works normally for safe paths', function(assert) { + const obj = { a: { b: 1 } }; + SETTER('a.b')(obj, 42); + + assert.strictEqual(obj.a.b, 42, 'safe paths must still work'); + assert.strictEqual(errors.log.called, false, 'should not log for safe paths'); + }); + + test('compileGetter logs error and returns undefined for __proto__ path fragment', function(assert) { + const result = GETTER('__proto__.pp_dx')({}); + + assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); + assert.strictEqual(result, undefined, 'getter must return undefined for __proto__'); + }); + + test('compileGetter logs error and returns undefined for constructor path fragment', function(assert) { + const result = GETTER('constructor.prototype')(function() {}); + + assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(result, undefined, 'getter must return undefined for constructor'); + }); + + test('compileGetter logs error and returns undefined for prototype path fragment', function(assert) { + const result = GETTER('prototype.pp_dx')(function() {}); + + assert.strictEqual(errors.log.calledWith('E0123', 'prototype'), true, 'should log E0123 for prototype'); + assert.strictEqual(result, undefined, 'getter must return undefined for prototype'); + }); + + test('combineGetters logs error and skips __proto__ fragment, returns safe fields', function(assert) { + const obj = { safe: 'value' }; + const result = GETTER(['__proto__.pp_dx', 'safe'])(obj); + + assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); + assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); + assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); + }); + + test('combineGetters logs error and skips constructor fragment, returns safe fields', function(assert) { + const obj = { safe: 'value' }; + const result = GETTER(['constructor.prototype.pp_dx', 'safe'])(obj); + + assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); + assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); + }); +}); + + QUnit.module('setter with wrapped variables', { beforeEach: function() { variableWrapper.inject(mockVariableWrapper); From 9239cd390e6520107fe3c53251e64fb4271865b8 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Mon, 22 Jun 2026 18:30:42 +0300 Subject: [PATCH 2/8] fix by copilot's review --- .../devextreme/js/__internal/core/utils/m_data.ts | 12 +++++++----- .../tests/DevExpress.core/utils.data.tests.js | 9 +++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index 9a59ec610fef..eb58652b7949 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -186,14 +186,16 @@ export const compileSetter = function (expr) { return function (obj, value, options) { options = prepareOptions(options); + + const unsafeFragment = expr.find(isUnsafePathFragment); + if (unsafeFragment !== undefined) { + errors.log('E0123', unsafeFragment); + return; + } + let currentValue = unwrap(obj, options); expr.forEach(function (propertyName, levelIndex) { - if (isUnsafePathFragment(propertyName)) { - errors.log('E0123', propertyName); - return; - } - let propertyValue = readPropValue(currentValue, propertyName, options); const isPropertyFunc = !options.functionsAsIs && isFunction(propertyValue) && !isWrapped(propertyValue); diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js index f41370fbdc69..27ab043f602c 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js @@ -493,16 +493,20 @@ QUnit.module('prototype pollution protection', { }, () => { test('compileSetter logs error and skips assignment for __proto__ path fragment', function(assert) { - SETTER('__proto__.pp_dx')({}, 'yes', { functionsAsIs: true }); + const obj = {}; + SETTER('__proto__.pp_dx')(obj, 'yes', { functionsAsIs: true }); assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); + assert.strictEqual(obj.pp_dx, undefined, 'target object must not be modified'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); test('compileSetter logs error and skips assignment for constructor path fragment', function(assert) { - SETTER('constructor.prototype.pp_dx')({}, 'yes', { functionsAsIs: true }); + const obj = {}; + SETTER('constructor.prototype.pp_dx')(obj, 'yes', { functionsAsIs: true }); assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(obj.pp_dx, undefined, 'target object must not be modified'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); @@ -511,6 +515,7 @@ QUnit.module('prototype pollution protection', { SETTER('prototype.pp_dx')(fn, 'yes', { functionsAsIs: true }); assert.strictEqual(errors.log.calledWith('E0123', 'prototype'), true, 'should log E0123 for prototype'); + assert.strictEqual(fn.pp_dx, undefined, 'function object must not be modified'); assert.strictEqual(fn.prototype.pp_dx, undefined, 'function prototype must not be modified'); }); From fcee1db099cc27deb273ad96cabd97b7d3ea9c45 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Tue, 23 Jun 2026 13:41:53 +0300 Subject: [PATCH 3/8] remove extre unsafe checking --- .../devextreme/js/__internal/core/utils/m_data.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index eb58652b7949..13bd3adadbbf 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -139,23 +139,13 @@ function combineGetters(getters) { for (let i = 0; i < last; i++) { const pathItem = path[i]; - if (isUnsafePathFragment(pathItem)) { - errors.log('E0123', pathItem); - return; - } if (!(pathItem in current)) { current[pathItem] = { }; } current = current[pathItem]; } - const lastPathItem = path[last]; - if (isUnsafePathFragment(lastPathItem)) { - errors.log('E0123', lastPathItem); - return; - } - - current[lastPathItem] = value; + current[path[last]] = value; }); return result; }; From 36866cb100791efdab57336d593561b7a5c7b2c2 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Tue, 23 Jun 2026 16:15:38 +0300 Subject: [PATCH 4/8] fix by Copilot's review --- .../js/__internal/core/utils/m_data.ts | 13 ++++----- .../tests/DevExpress.core/utils.data.tests.js | 27 ++++++++++++------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index 13bd3adadbbf..29e477ce8970 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -68,8 +68,14 @@ export const compileGetter = function (expr) { if (typeof expr === 'string') { const path = getPathParts(expr); + const unsafeFragment = path.find(isUnsafePathFragment); return function (obj, options) { + if (unsafeFragment !== undefined) { + errors.log('E0123', unsafeFragment); + return; + } + options = prepareOptions(options); const functionAsIs = options.functionsAsIs; const hasDefaultValue = 'defaultValue' in options; @@ -85,11 +91,6 @@ export const compileGetter = function (expr) { const pathPart = path[i]; - if (isUnsafePathFragment(pathPart)) { - errors.log('E0123', pathPart); - return; - } - if (hasDefaultValue && isObject(current) && !(pathPart in current)) { return options.defaultValue; } @@ -173,11 +174,11 @@ const ensurePropValueDefined = function (obj, propName, value, options) { export const compileSetter = function (expr) { expr = getPathParts(expr || 'this'); const lastLevelIndex = expr.length - 1; + const unsafeFragment = expr.find(isUnsafePathFragment); return function (obj, value, options) { options = prepareOptions(options); - const unsafeFragment = expr.find(isUnsafePathFragment); if (unsafeFragment !== undefined) { errors.log('E0123', unsafeFragment); return; diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js index 27ab043f602c..dc1331180d02 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js @@ -492,7 +492,7 @@ QUnit.module('prototype pollution protection', { } }, () => { - test('compileSetter logs error and skips assignment for __proto__ path fragment', function(assert) { + test('compileSetter logs error and skips assignment for __proto__ path fragment (T1330839)', function(assert) { const obj = {}; SETTER('__proto__.pp_dx')(obj, 'yes', { functionsAsIs: true }); @@ -501,7 +501,7 @@ QUnit.module('prototype pollution protection', { assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); - test('compileSetter logs error and skips assignment for constructor path fragment', function(assert) { + test('compileSetter logs error and skips assignment for constructor path fragment (T1330839)', function(assert) { const obj = {}; SETTER('constructor.prototype.pp_dx')(obj, 'yes', { functionsAsIs: true }); @@ -510,7 +510,7 @@ QUnit.module('prototype pollution protection', { assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); - test('compileSetter logs error and skips assignment for prototype path fragment', function(assert) { + test('compileSetter logs error and skips assignment for prototype path fragment (T1330839)', function(assert) { const fn = function() {}; SETTER('prototype.pp_dx')(fn, 'yes', { functionsAsIs: true }); @@ -519,7 +519,7 @@ QUnit.module('prototype pollution protection', { assert.strictEqual(fn.prototype.pp_dx, undefined, 'function prototype must not be modified'); }); - test('compileSetter works normally for safe paths', function(assert) { + test('compileSetter works normally for safe paths (T1330839)', function(assert) { const obj = { a: { b: 1 } }; SETTER('a.b')(obj, 42); @@ -527,28 +527,28 @@ QUnit.module('prototype pollution protection', { assert.strictEqual(errors.log.called, false, 'should not log for safe paths'); }); - test('compileGetter logs error and returns undefined for __proto__ path fragment', function(assert) { + test('compileGetter logs error and returns undefined for __proto__ path fragment (T1330839)', function(assert) { const result = GETTER('__proto__.pp_dx')({}); assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); assert.strictEqual(result, undefined, 'getter must return undefined for __proto__'); }); - test('compileGetter logs error and returns undefined for constructor path fragment', function(assert) { + test('compileGetter logs error and returns undefined for constructor path fragment (T1330839)', function(assert) { const result = GETTER('constructor.prototype')(function() {}); assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); assert.strictEqual(result, undefined, 'getter must return undefined for constructor'); }); - test('compileGetter logs error and returns undefined for prototype path fragment', function(assert) { + test('compileGetter logs error and returns undefined for prototype path fragment (T1330839)', function(assert) { const result = GETTER('prototype.pp_dx')(function() {}); assert.strictEqual(errors.log.calledWith('E0123', 'prototype'), true, 'should log E0123 for prototype'); assert.strictEqual(result, undefined, 'getter must return undefined for prototype'); }); - test('combineGetters logs error and skips __proto__ fragment, returns safe fields', function(assert) { + test('combineGetters logs error and skips __proto__ fragment, returns safe fields (T1330839)', function(assert) { const obj = { safe: 'value' }; const result = GETTER(['__proto__.pp_dx', 'safe'])(obj); @@ -557,7 +557,7 @@ QUnit.module('prototype pollution protection', { assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); }); - test('combineGetters logs error and skips constructor fragment, returns safe fields', function(assert) { + test('combineGetters logs error and skips constructor fragment, returns safe fields (T1330839)', function(assert) { const obj = { safe: 'value' }; const result = GETTER(['constructor.prototype.pp_dx', 'safe'])(obj); @@ -565,6 +565,15 @@ QUnit.module('prototype pollution protection', { assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); }); + + test('combineGetters logs error and skips unsafe paths even if intermediate values break early (with defaultValue) (T1330839)', function(assert) { + const obj = { a: null, safe: 'value' }; + const result = GETTER(['a.constructor.prototype.pp_dx2', 'safe'])(obj, { defaultValue: 'default' }); + + assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(({}).pp_dx2, undefined, 'Object.prototype must not be polluted'); + assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); + }); }); From 1ab7b7393d06a176a3b1480dd9e3591de83b4429 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 24 Jun 2026 11:57:52 +0300 Subject: [PATCH 5/8] refactoring --- .../devextreme/js/__internal/core/utils/m_data.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index 29e477ce8970..6ffbd68c8ec7 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -176,14 +176,15 @@ export const compileSetter = function (expr) { const lastLevelIndex = expr.length - 1; const unsafeFragment = expr.find(isUnsafePathFragment); + if (unsafeFragment !== undefined) { + return function () { + errors.log('E0123', unsafeFragment); + }; + } + return function (obj, value, options) { options = prepareOptions(options); - if (unsafeFragment !== undefined) { - errors.log('E0123', unsafeFragment); - return; - } - let currentValue = unwrap(obj, options); expr.forEach(function (propertyName, levelIndex) { From 82503a39dcacdc074e944545c86329fd8246a662 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 24 Jun 2026 16:34:30 +0300 Subject: [PATCH 6/8] fix by review --- .../js/__internal/core/utils/m_data.ts | 11 ++++++++--- .../tests/DevExpress.core/utils.data.tests.js | 19 ++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index 6ffbd68c8ec7..9a725b02da1b 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -70,12 +70,15 @@ export const compileGetter = function (expr) { const path = getPathParts(expr); const unsafeFragment = path.find(isUnsafePathFragment); - return function (obj, options) { - if (unsafeFragment !== undefined) { + if (unsafeFragment !== undefined) { + return function () { errors.log('E0123', unsafeFragment); + // eslint-disable-next-line no-useless-return return; - } + }; + } + return function (obj, options) { options = prepareOptions(options); const functionAsIs = options.functionsAsIs; const hasDefaultValue = 'defaultValue' in options; @@ -179,6 +182,8 @@ export const compileSetter = function (expr) { if (unsafeFragment !== undefined) { return function () { errors.log('E0123', unsafeFragment); + // eslint-disable-next-line no-useless-return + return; }; } diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js index dc1331180d02..378c38a28366 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js @@ -527,7 +527,24 @@ QUnit.module('prototype pollution protection', { assert.strictEqual(errors.log.called, false, 'should not log for safe paths'); }); - test('compileGetter logs error and returns undefined for __proto__ path fragment (T1330839)', function(assert) { + test('compileSetter blocks unsafe fragment written in bracket notation (T1330839)', function(assert) { + const obj = {}; + SETTER('a[constructor][prototype].pp_dx')(obj, 'yes', { functionsAsIs: true }); + + assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor in bracket notation'); + assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); + }); + + test('compileSetter blocks unsafe fragment in non-first position (T1330839)', function(assert) { + const obj = { a: { b: {} } }; + SETTER('a.b.__proto__')(obj, { pp_dx: 'yes' }, { functionsAsIs: true }); + + assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__ in non-first position'); + assert.strictEqual(obj.a.b.pp_dx, undefined, 'nested object must not inherit a polluted property'); + assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); + }); + + test('compileGetter logs error and returns undefined for __proto__ path fragent (T1330839)', function(assert) { const result = GETTER('__proto__.pp_dx')({}); assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); From bd4eaa1ea75a35ff4c16d69a875cec7d9eac6295 Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 24 Jun 2026 17:30:11 +0300 Subject: [PATCH 7/8] error - updating text and call --- packages/devextreme/js/__internal/core/m_errors.ts | 2 +- packages/devextreme/js/__internal/core/utils/m_data.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/devextreme/js/__internal/core/m_errors.ts b/packages/devextreme/js/__internal/core/m_errors.ts index 46efeabe1290..b685269e9fcb 100644 --- a/packages/devextreme/js/__internal/core/m_errors.ts +++ b/packages/devextreme/js/__internal/core/m_errors.ts @@ -64,7 +64,7 @@ export default errorUtils({ E0122: 'AIIntegration: The sendRequest method is missing.', - E0123: 'Prototype pollution attempt detected: the path contains an unsafe fragment \'{0}\'', + E0123: '{0} received an unsafe expression: \'{1}\'', W0000: '\'{0}\' is deprecated in {1}. {2}', diff --git a/packages/devextreme/js/__internal/core/utils/m_data.ts b/packages/devextreme/js/__internal/core/utils/m_data.ts index 9a725b02da1b..e023bc8df089 100644 --- a/packages/devextreme/js/__internal/core/utils/m_data.ts +++ b/packages/devextreme/js/__internal/core/utils/m_data.ts @@ -72,7 +72,7 @@ export const compileGetter = function (expr) { if (unsafeFragment !== undefined) { return function () { - errors.log('E0123', unsafeFragment); + errors.log('E0123', 'compileGetter', unsafeFragment); // eslint-disable-next-line no-useless-return return; }; @@ -181,7 +181,7 @@ export const compileSetter = function (expr) { if (unsafeFragment !== undefined) { return function () { - errors.log('E0123', unsafeFragment); + errors.log('E0123', 'compileSetter', unsafeFragment); // eslint-disable-next-line no-useless-return return; }; From 065affc8a3c72abafffb4215385b5d0f3ad4611d Mon Sep 17 00:00:00 2001 From: dmlvr Date: Wed, 24 Jun 2026 18:46:13 +0300 Subject: [PATCH 8/8] update tests for new error calling --- .../tests/DevExpress.core/utils.data.tests.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js index 378c38a28366..288e5545bf96 100644 --- a/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.core/utils.data.tests.js @@ -496,7 +496,7 @@ QUnit.module('prototype pollution protection', { const obj = {}; SETTER('__proto__.pp_dx')(obj, 'yes', { functionsAsIs: true }); - assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileSetter', '__proto__'), true, 'should log E0123 for __proto__'); assert.strictEqual(obj.pp_dx, undefined, 'target object must not be modified'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); @@ -505,7 +505,7 @@ QUnit.module('prototype pollution protection', { const obj = {}; SETTER('constructor.prototype.pp_dx')(obj, 'yes', { functionsAsIs: true }); - assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileSetter', 'constructor'), true, 'should log E0123 for constructor'); assert.strictEqual(obj.pp_dx, undefined, 'target object must not be modified'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); @@ -514,7 +514,7 @@ QUnit.module('prototype pollution protection', { const fn = function() {}; SETTER('prototype.pp_dx')(fn, 'yes', { functionsAsIs: true }); - assert.strictEqual(errors.log.calledWith('E0123', 'prototype'), true, 'should log E0123 for prototype'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileSetter', 'prototype'), true, 'should log E0123 for prototype'); assert.strictEqual(fn.pp_dx, undefined, 'function object must not be modified'); assert.strictEqual(fn.prototype.pp_dx, undefined, 'function prototype must not be modified'); }); @@ -531,7 +531,7 @@ QUnit.module('prototype pollution protection', { const obj = {}; SETTER('a[constructor][prototype].pp_dx')(obj, 'yes', { functionsAsIs: true }); - assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor in bracket notation'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileSetter', 'constructor'), true, 'should log E0123 for constructor in bracket notation'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); @@ -539,7 +539,7 @@ QUnit.module('prototype pollution protection', { const obj = { a: { b: {} } }; SETTER('a.b.__proto__')(obj, { pp_dx: 'yes' }, { functionsAsIs: true }); - assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__ in non-first position'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileSetter', '__proto__'), true, 'should log E0123 for __proto__ in non-first position'); assert.strictEqual(obj.a.b.pp_dx, undefined, 'nested object must not inherit a polluted property'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); }); @@ -547,21 +547,21 @@ QUnit.module('prototype pollution protection', { test('compileGetter logs error and returns undefined for __proto__ path fragent (T1330839)', function(assert) { const result = GETTER('__proto__.pp_dx')({}); - assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileGetter', '__proto__'), true, 'should log E0123 for __proto__'); assert.strictEqual(result, undefined, 'getter must return undefined for __proto__'); }); test('compileGetter logs error and returns undefined for constructor path fragment (T1330839)', function(assert) { const result = GETTER('constructor.prototype')(function() {}); - assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileGetter', 'constructor'), true, 'should log E0123 for constructor'); assert.strictEqual(result, undefined, 'getter must return undefined for constructor'); }); test('compileGetter logs error and returns undefined for prototype path fragment (T1330839)', function(assert) { const result = GETTER('prototype.pp_dx')(function() {}); - assert.strictEqual(errors.log.calledWith('E0123', 'prototype'), true, 'should log E0123 for prototype'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileGetter', 'prototype'), true, 'should log E0123 for prototype'); assert.strictEqual(result, undefined, 'getter must return undefined for prototype'); }); @@ -569,7 +569,7 @@ QUnit.module('prototype pollution protection', { const obj = { safe: 'value' }; const result = GETTER(['__proto__.pp_dx', 'safe'])(obj); - assert.strictEqual(errors.log.calledWith('E0123', '__proto__'), true, 'should log E0123 for __proto__'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileGetter', '__proto__'), true, 'should log E0123 for __proto__'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); }); @@ -578,7 +578,7 @@ QUnit.module('prototype pollution protection', { const obj = { safe: 'value' }; const result = GETTER(['constructor.prototype.pp_dx', 'safe'])(obj); - assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileGetter', 'constructor'), true, 'should log E0123 for constructor'); assert.strictEqual(({}).pp_dx, undefined, 'Object.prototype must not be polluted'); assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); }); @@ -587,7 +587,7 @@ QUnit.module('prototype pollution protection', { const obj = { a: null, safe: 'value' }; const result = GETTER(['a.constructor.prototype.pp_dx2', 'safe'])(obj, { defaultValue: 'default' }); - assert.strictEqual(errors.log.calledWith('E0123', 'constructor'), true, 'should log E0123 for constructor'); + assert.strictEqual(errors.log.calledWith('E0123', 'compileGetter', 'constructor'), true, 'should log E0123 for constructor'); assert.strictEqual(({}).pp_dx2, undefined, 'Object.prototype must not be polluted'); assert.deepEqual(result, { safe: 'value' }, 'safe field must still be returned'); });