From d5c591317bb970c3808020032a3c7d37be58a15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Mon, 16 Apr 2018 18:55:42 +0200 Subject: [PATCH] Add coordinates validation to specific endpoints --- lib/cartodb/controllers/layergroup/static.js | 2 + lib/cartodb/controllers/layergroup/tile.js | 4 + lib/cartodb/controllers/named_maps.js | 2 + lib/cartodb/middleware/coordinates.js | 32 +++ test/support/test-client.js | 4 +- .../cartodb/middlewares/coordinates.test.js | 215 ++++++++++++++++++ 6 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 lib/cartodb/middleware/coordinates.js create mode 100644 test/unit/cartodb/middlewares/coordinates.test.js diff --git a/lib/cartodb/controllers/layergroup/static.js b/lib/cartodb/controllers/layergroup/static.js index 00510780..1aa9cdeb 100644 --- a/lib/cartodb/controllers/layergroup/static.js +++ b/lib/cartodb/controllers/layergroup/static.js @@ -1,6 +1,7 @@ const cors = require('../../middleware/cors'); const user = require('../../middleware/user'); const layergroupToken = require('../../middleware/layergroup-token'); +const coordinates = require('../../middleware/coordinates'); const cleanUpQueryParams = require('../../middleware/clean-up-query-params'); const credentials = require('../../middleware/credentials'); const dbConnSetup = require('../../middleware/db-conn-setup'); @@ -43,6 +44,7 @@ module.exports = class StaticController { cors(), user(), layergroupToken(), + coordinates({ z: true, x: false, y: false }), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), diff --git a/lib/cartodb/controllers/layergroup/tile.js b/lib/cartodb/controllers/layergroup/tile.js index 07727236..544c12b2 100644 --- a/lib/cartodb/controllers/layergroup/tile.js +++ b/lib/cartodb/controllers/layergroup/tile.js @@ -1,6 +1,7 @@ const cors = require('../../middleware/cors'); const user = require('../../middleware/user'); const layergroupToken = require('../../middleware/layergroup-token'); +const coordinates = require('../../middleware/coordinates'); const cleanUpQueryParams = require('../../middleware/clean-up-query-params'); const credentials = require('../../middleware/credentials'); const dbConnSetup = require('../../middleware/db-conn-setup'); @@ -51,6 +52,7 @@ module.exports = class TileController { cors(), user(), layergroupToken(), + coordinates(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), @@ -79,6 +81,7 @@ module.exports = class TileController { cors(), user(), layergroupToken(), + coordinates(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), @@ -108,6 +111,7 @@ module.exports = class TileController { cors(), user(), layergroupToken(), + coordinates(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), diff --git a/lib/cartodb/controllers/named_maps.js b/lib/cartodb/controllers/named_maps.js index 93e56bbe..c9656de0 100644 --- a/lib/cartodb/controllers/named_maps.js +++ b/lib/cartodb/controllers/named_maps.js @@ -1,6 +1,7 @@ const cors = require('../middleware/cors'); const user = require('../middleware/user'); const cleanUpQueryParams = require('../middleware/clean-up-query-params'); +const coordinates = require('../middleware/coordinates'); const credentials = require('../middleware/credentials'); const dbConnSetup = require('../middleware/db-conn-setup'); const authorize = require('../middleware/authorize'); @@ -56,6 +57,7 @@ NamedMapsController.prototype.register = function(app) { `${templateBasePath}/:template_id/:layer/:z/:x/:y.(:format)`, cors(), user(), + coordinates(), credentials(), authorize(this.authApi), dbConnSetup(this.pgConnection), diff --git a/lib/cartodb/middleware/coordinates.js b/lib/cartodb/middleware/coordinates.js new file mode 100644 index 00000000..d02b5624 --- /dev/null +++ b/lib/cartodb/middleware/coordinates.js @@ -0,0 +1,32 @@ +module.exports = function coordinates (validate = { z: true, x: true, y: true }) { + const positiveIntegerNumber = /^\d+$/; + const integerNumber = /^-?\d+$/; + + return function coordinatesMiddleware (req, res, next) { + const { z, x, y } = req.params; + + if (validate.z && !positiveIntegerNumber.test(z)) { + const err = new Error(`Invalid zoom value (${z}). It should be a positive number`); + err.http_status = 400; + + return next(err); + } + + // Negatives values for x param are valid. The x param is wrapped + if (validate.x && !integerNumber.test(x)) { + const err = new Error(`Invalid coodinate 'x' value (${x}). It should be a number`); + err.http_status = 400; + + return next(err); + } + + if (validate.y && !positiveIntegerNumber.test(y)) { + const err = new Error(`Invalid coodinate 'y' value (${y}). It should be a positive number`); + err.http_status = 400; + + return next(err); + } + + next(); + }; +}; diff --git a/test/support/test-client.js b/test/support/test-client.js index ea3305fd..5e08394a 100644 --- a/test/support/test-client.js +++ b/test/support/test-client.js @@ -905,7 +905,7 @@ TestClient.prototype.getLayergroup = function (params, callback) { TestClient.prototype.getStaticCenter = function (params, callback) { var self = this; - let { layergroupid, z, lat, lng, width, height, format } = params; + let { layergroupid, zoom, lat, lng, width, height, format } = params; var url = `/api/v1/map/`; @@ -954,7 +954,7 @@ TestClient.prototype.getStaticCenter = function (params, callback) { self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; self.keysToDelete['user:localhost:mapviews:global'] = 5; - url = `/api/v1/map/static/center/${layergroupId}/${z}/${lat}/${lng}/${width}/${height}.${format}`; + url = `/api/v1/map/static/center/${layergroupId}/${zoom}/${lat}/${lng}/${width}/${height}.${format}`; if (self.apiKey) { url += '?' + qs.stringify({api_key: self.apiKey}); diff --git a/test/unit/cartodb/middlewares/coordinates.test.js b/test/unit/cartodb/middlewares/coordinates.test.js new file mode 100644 index 00000000..cd1d022e --- /dev/null +++ b/test/unit/cartodb/middlewares/coordinates.test.js @@ -0,0 +1,215 @@ +const assert = require('assert'); +const coordinates = require('../../../../lib/cartodb/middleware/coordinates'); + +describe('coordinates middleware', function () { + it('should return error: invalid zoom paramenter (-1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '-1', + x: '0', + y: '0' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, 'Invalid zoom value (-1). It should be a positive number'); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should return error: invalid zoom paramenter (1.1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1.1', + x: '0', + y: '0' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, 'Invalid zoom value (1.1). It should be a positive number'); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should return error: invalid zoom paramenter (wadus)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: 'wadus', + x: '0', + y: '0' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, 'Invalid zoom value (wadus). It should be a positive number'); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should NOT return error: \'zoom\' paramenter (1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: '1', + y: '0' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.ifError(err); + done(); + }); + }); + + it('should return error: invalid coordinate \'x\' paramenter (1.1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: '1.1', + y: '0' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, `Invalid coodinate 'x' value (1.1). It should be a number`); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should return error: invalid coordinate \'x\' paramenter (wadus)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: 'wadus', + y: '0' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, `Invalid coodinate 'x' value (wadus). It should be a number`); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should NOT return error: \'x\' paramenter (-1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: '-3', + y: '0' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.ifError(err); + done(); + }); + }); + + it('should return error: invalid coordinate \'y\' paramenter (-1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: '0', + y: '-1' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, `Invalid coodinate 'y' value (-1). It should be a positive number`); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should return error: invalid coordinate \'y\' paramenter (1.1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: '0', + y: '1.1' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, `Invalid coodinate 'y' value (1.1). It should be a positive number`); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should return error: invalid coordinate \'y\' paramenter (wadus)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: '0', + y: 'wadus' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, `Invalid coodinate 'y' value (wadus). It should be a positive number`); + assert.equal(err.http_status, 400); + done(); + }); + }); + + it('should NOT return error: \'y\' paramenter (1)', function (done) { + const coords = coordinates(); + const req = { + params: { + z: '1', + x: '1', + y: '1' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.ifError(err); + done(); + }); + }); + + it('should validate zoom and should return error: invalid zoom paramenter (-1.1)', function (done) { + const coords = coordinates({ z: true, x: false, y: false }); + const req = { + params: { + z: '-1.1' + } + }; + const res = {}; + + coords(req, res, function (err) { + assert.equal(err.message, 'Invalid zoom value (-1.1). It should be a positive number'); + assert.equal(err.http_status, 400); + done(); + }); + }); +});