Allow configuration of maxDepthOfQuery

https://github.com/strongloop/loopback-datasource-juggler/issues/1651
This commit is contained in:
Raymond Feng 2018-10-30 12:14:52 -07:00
parent b4d0cf54a8
commit 41d5f20fff
4 changed files with 52 additions and 9 deletions

View File

@ -1684,8 +1684,8 @@ DataAccessObject._sanitizeQuery = function(query, options) {
prohibitHiddenPropertiesInQuery = true; prohibitHiddenPropertiesInQuery = true;
} }
if (!prohibitHiddenPropertiesInQuery) // See https://github.com/strongloop/loopback-datasource-juggler/issues/1651
console.log(prohibitHiddenPropertiesInQuery); var maxDepthOfQuery = (+this._getSetting('maxDepthOfQuery')) || 12;
var prohibitedKeys = []; var prohibitedKeys = [];
// Check violation of keys // Check violation of keys
@ -1697,6 +1697,7 @@ DataAccessObject._sanitizeQuery = function(query, options) {
} }
return sanitizeQueryOrData(query, return sanitizeQueryOrData(query,
Object.assign({ Object.assign({
maxDepth: maxDepthOfQuery,
prohibitedKeys: prohibitedKeys, prohibitedKeys: prohibitedKeys,
normalizeUndefinedInQuery: normalizeUndefinedInQuery, normalizeUndefinedInQuery: normalizeUndefinedInQuery,
}, options)); }, options));

View File

@ -336,18 +336,25 @@ function sanitizeQuery(query, options) {
const prohibitedKeys = options.prohibitedKeys; const prohibitedKeys = options.prohibitedKeys;
const offendingKeys = []; const offendingKeys = [];
const normalizeUndefinedInQuery = options.normalizeUndefinedInQuery; 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 // WARNING: [rfeng] Use map() will cause mongodb to produce invalid BSON
// as traverse doesn't transform the ObjectId correctly // as traverse doesn't transform the ObjectId correctly
const result = traverse(query).forEach(function(x) { const result = traverse(query).forEach(function(x) {
/** /**
* Security risk if the client passes in a very deep where object * Security risk if the client passes in a very deep where object
*/ */
if (this.level > maxDepth || this.circular) { if (this.circular) {
const msg = g.f('The query object is too deep or circular'); const msg = g.f('The query object is circular');
const err = new Error(msg); const err = new Error(msg);
err.statusCode = 400; 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; throw err;
} }
/** /**

View File

@ -257,6 +257,41 @@ describe('ModelDefinition class', function() {
done(); 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() { it('should serialize protected properties into JSON', function() {
var ProtectedModel = memory.createModel('protected', {}, { var ProtectedModel = memory.createModel('protected', {}, {
protected: ['protectedProperty'], protected: ['protectedProperty'],

View File

@ -88,18 +88,18 @@ describe('util.sanitizeQuery', function() {
var q7 = {where: {x: 1}}; var q7 = {where: {x: 1}};
q7.where.y = q7; q7.where.y = q7;
(function() { sanitizeQuery(q7); }).should.throw( (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: var q8 = {where: {and: [{and: [{and: [{and: [{and: [{and:
[{and: [{and: [{and: [{x: 1}]}]}]}]}]}]}]}]}]}}; [{and: [{and: [{and: [{x: 1}]}]}]}]}]}]}]}]}]}};
(function() { sanitizeQuery(q8); }).should.throw( (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}]}]}]}]}}; var q9 = {where: {and: [{and: [{and: [{and: [{x: 1}]}]}]}]}};
(function() { sanitizeQuery(q8, {maxDepth: 4}); }).should.throw( (function() { sanitizeQuery(q8, {maxDepth: 4}); }).should.throw(
/The query object is too deep or circular/ /The query object exceeds maximum depth 4/
); );
}); });