From 640f9262d1136ad8040ccc750f54f97b66e84661 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Thu, 24 Mar 2022 02:17:59 +0100 Subject: [PATCH] fix and optimize keyword detection --- spec/vulnerabilities.spec.js | 14 ++++++++++++++ src/Controllers/DatabaseController.js | 5 +++-- src/Utils.js | 4 ++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index 1255d64398..02a4ff5433 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -280,4 +280,18 @@ describe('Vulnerabilities', () => { expect(text.error).toBe('Prohibited keyword in request data: {"value":"aValue[123]*"}.'); }); }); + + describe('Ignore non-matches', () => { + it('ignores write request that contains only fraction of denied keyword', async () => { + await reconfigureServer({ + requestKeywordDenylist: [{ key: 'abc' }], + }); + // Initially saving an object executes the keyword detection in RestWrite.js + const obj = new TestObject({ a: { b: { c: 0 } } }); + await expectAsync(obj.save()).toBeResolved(); + // Modifying a nested key executes the keyword detection in DatabaseController.js + obj.increment('a.b.c'); + await expectAsync(obj.save()).toBeResolved(); + }); + }); }); diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 7c5153f839..3e69b1f5eb 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -11,6 +11,7 @@ import intersect from 'intersect'; // @flow-disable-next import deepcopy from 'deepcopy'; import logger from '../logger'; +import Utils from '../Utils'; import * as SchemaController from './SchemaController'; import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; import MongoStorageAdapter from '../Adapters/Storage/Mongo/MongoStorageAdapter'; @@ -1763,8 +1764,8 @@ class DatabaseController { if (this.options && this.options.requestKeywordDenylist) { // Scan request data for denied keywords for (const keyword of this.options.requestKeywordDenylist) { - const isMatch = (a, b) => (typeof a === 'string' && new RegExp(b).test(a)) || a === b; - if (isMatch(firstKey, keyword.key)) { + const match = Utils.objectContainsKeyValue({ firstKey: undefined }, keyword.key, undefined); + if (match) { throw new Parse.Error( Parse.Error.INVALID_KEY_NAME, `Prohibited keyword in request data: ${JSON.stringify(keyword)}.` diff --git a/src/Utils.js b/src/Utils.js index 6d66cde567..d5a255a5ca 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -342,8 +342,8 @@ class Utils { */ static objectContainsKeyValue(obj, key, value) { const isMatch = (a, b) => (typeof a === 'string' && new RegExp(b).test(a)) || a === b; - const isKeyMatch = k => isMatch(key, k); - const isValueMatch = v => isMatch(value, v); + const isKeyMatch = k => isMatch(k, key); + const isValueMatch = v => isMatch(v, value); for (const [k, v] of Object.entries(obj)) { if (key !== undefined && value === undefined && isKeyMatch(k)) { return true;