diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index 957277772f..5c83493c94 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -109,6 +109,17 @@ describe('Vulnerabilities', () => { ); }); + it('denies expanding existing object with polluted keys', async () => { + const obj = await new Parse.Object('RCE', { a: { foo: [] } }).save(); + await reconfigureServer({ + requestKeywordDenylist: ['foo'], + }); + obj.addUnique('a.foo', 'abc'); + await expectAsync(obj.save()).toBeRejectedWith( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Prohibited keyword in request data: "foo".`) + ); + }); + it('denies creating a cloud trigger with polluted data', async () => { Parse.Cloud.beforeSave('TestObject', ({ object }) => { object.set('obj', { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 25b97d0ec7..4c2e50e0b6 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -1765,7 +1765,11 @@ class DatabaseController { if (this.options && this.options.requestKeywordDenylist) { // Scan request data for denied keywords for (const keyword of this.options.requestKeywordDenylist) { - const match = Utils.objectContainsKeyValue({ firstKey: undefined }, keyword.key, undefined); + const match = Utils.objectContainsKeyValue( + { [firstKey]: true, [nextPath]: true }, + keyword.key, + true + ); if (match) { throw new Parse.Error( Parse.Error.INVALID_KEY_NAME,