From eb85b3e3a6ed3e45ceb168c4634217f1e3a08d89 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 30 Oct 2018 12:14:52 -0700 Subject: [PATCH] Allow configuration of maxDepthOfQuery https://github.com/strongloop/loopback-datasource-juggler/issues/1651 --- lib/dao.js | 5 +++-- lib/utils.js | 15 +++++++++++---- test/model-definition.test.js | 35 +++++++++++++++++++++++++++++++++++ test/util.test.js | 6 +++--- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/lib/dao.js b/lib/dao.js index 0ed06bc3..959a734e 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -1684,8 +1684,8 @@ DataAccessObject._sanitizeQuery = function(query, options) { prohibitHiddenPropertiesInQuery = true; } - if (!prohibitHiddenPropertiesInQuery) - console.log(prohibitHiddenPropertiesInQuery); + // See https://github.com/strongloop/loopback-datasource-juggler/issues/1651 + var maxDepthOfQuery = (+this._getSetting('maxDepthOfQuery')) || 12; var prohibitedKeys = []; // Check violation of keys @@ -1697,6 +1697,7 @@ DataAccessObject._sanitizeQuery = function(query, options) { } return sanitizeQueryOrData(query, Object.assign({ + maxDepth: maxDepthOfQuery, prohibitedKeys: prohibitedKeys, normalizeUndefinedInQuery: normalizeUndefinedInQuery, }, options)); diff --git a/lib/utils.js b/lib/utils.js index b1e85482..1b4f8eb8 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -336,18 +336,25 @@ function sanitizeQuery(query, options) { const prohibitedKeys = options.prohibitedKeys; const offendingKeys = []; const normalizeUndefinedInQuery = options.normalizeUndefinedInQuery; - const maxDepth = options.maxDepth || 10; + const maxDepth = options.maxDepth || 12; // WARNING: [rfeng] Use map() will cause mongodb to produce invalid BSON // as traverse doesn't transform the ObjectId correctly const result = traverse(query).forEach(function(x) { /** * Security risk if the client passes in a very deep where object */ - if (this.level > maxDepth || this.circular) { - const msg = g.f('The query object is too deep or circular'); + if (this.circular) { + const msg = g.f('The query object is circular'); const err = new Error(msg); err.statusCode = 400; - err.code = 'WHERE_OBJECT_TOO_DEEP'; + err.code = 'QUERY_OBJECT_IS_CIRCULAR'; + throw err; + } + if (this.level > maxDepth) { + const msg = g.f('The query object exceeds maximum depth %d', maxDepth); + const err = new Error(msg); + err.statusCode = 400; + err.code = 'QUERY_OBJECT_TOO_DEEP'; throw err; } /** diff --git a/test/model-definition.test.js b/test/model-definition.test.js index f49ae8ae..053f6067 100644 --- a/test/model-definition.test.js +++ b/test/model-definition.test.js @@ -257,6 +257,41 @@ describe('ModelDefinition class', function() { done(); }); + describe('maxDepthOfQuery', function() { + it('should report errors for deep query than maxDepthOfQuery', function(done) { + var MyModel = memory.createModel('my-model', {}, { + maxDepthOfQuery: 5, + }); + + var filter = givenComplexFilter(); + + MyModel.find(filter, function(err) { + should.exist(err); + err.message.should.match('The query object exceeds maximum depth 5'); + done(); + }); + }); + + it('should honor maxDepthOfQuery setting', function(done) { + var MyModel = memory.createModel('my-model', {}, { + maxDepthOfQuery: 20, + }); + + var filter = givenComplexFilter(); + + MyModel.find(filter, function(err) { + should.not.exist(err); + done(); + }); + }); + + function givenComplexFilter() { + var filter = {where: {and: [{and: [{and: [{and: [{and: [{and: + [{and: [{and: [{and: [{x: 1}]}]}]}]}]}]}]}]}]}}; + return filter; + } + }); + it('should serialize protected properties into JSON', function() { var ProtectedModel = memory.createModel('protected', {}, { protected: ['protectedProperty'], diff --git a/test/util.test.js b/test/util.test.js index 3874faeb..131d27ab 100644 --- a/test/util.test.js +++ b/test/util.test.js @@ -88,18 +88,18 @@ describe('util.sanitizeQuery', function() { var q7 = {where: {x: 1}}; q7.where.y = q7; (function() { sanitizeQuery(q7); }).should.throw( - /The query object is too deep or circular/ + /The query object is circular/ ); var q8 = {where: {and: [{and: [{and: [{and: [{and: [{and: [{and: [{and: [{and: [{x: 1}]}]}]}]}]}]}]}]}]}}; (function() { sanitizeQuery(q8); }).should.throw( - /The query object is too deep or circular/ + /The query object exceeds maximum depth 12/ ); var q9 = {where: {and: [{and: [{and: [{and: [{x: 1}]}]}]}]}}; (function() { sanitizeQuery(q8, {maxDepth: 4}); }).should.throw( - /The query object is too deep or circular/ + /The query object exceeds maximum depth 4/ ); });