diff --git a/lib/index.js b/lib/index.js index 602c836..1829a32 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,6 +3,7 @@ const Boom = require('boom'); const Joi = require('joi'); const Pkg = require('../package.json'); +const RangeCheck = require('range_check'); const internals = {}; @@ -71,9 +72,13 @@ internals.getIP = function getIP(request, settings) { internals.pathCheck = async function (pathCache, request, settings) { + const ip = internals.getIP(request, settings); const path = request.path; - if (settings.pathLimit === false) { + if ( + (RangeCheck.inRange(ip, settings.ipWhitelist)) || + (settings.pathLimit === false) + ) { request.plugins[internals.pluginName].pathLimit = false; return { remaining: 1 }; } @@ -108,8 +113,9 @@ internals.userCheck = async function (userCache, request, settings) { const ip = internals.getIP(request, settings); let user = internals.getUser(request, settings); + if ( - (settings.ipWhitelist.indexOf(ip) > -1) || + (RangeCheck.inRange(ip, settings.ipWhitelist)) || (user && settings.userWhitelist.indexOf(user) > -1) || (settings.userLimit === false) ) { @@ -154,7 +160,7 @@ internals.userPathCheck = async function (userPathCache, request, settings) { const path = request.path; if ( - (settings.ipWhitelist.indexOf(ip) > -1) || + (RangeCheck.inRange(ip, settings.ipWhitelist)) || (user && settings.userWhitelist.indexOf(user) > -1) || (settings.userPathLimit === false) ) { diff --git a/package-lock.json b/package-lock.json index d84f75e..afc785a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1111,6 +1111,16 @@ "through": "^2.3.6" } }, + "ip6": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/ip6/-/ip6-0.0.4.tgz", + "integrity": "sha1-RMWp23njnUBSAbTXjROzhw5I2zE=" + }, + "ipaddr.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.2.0.tgz", + "integrity": "sha1-irpJyRknmVhb3WQ+DMtQ6K53e6Q=" + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -1567,6 +1577,15 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" }, + "range_check": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/range_check/-/range_check-1.4.0.tgz", + "integrity": "sha1-zYfHrGLEC6nfabhwPGBPYMN0hjU=", + "requires": { + "ip6": "0.0.4", + "ipaddr.js": "1.2" + } + }, "regexpp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", diff --git a/package.json b/package.json index 26715a7..ac33ce2 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ }, "dependencies": { "boom": "^7.2.0", - "joi": "^14.3.0" + "joi": "^14.3.0", + "range_check": "^1.4.0" }, "files": [ "index.js", diff --git a/test/index.js b/test/index.js index 958b33a..5737ee1 100644 --- a/test/index.js +++ b/test/index.js @@ -316,11 +316,10 @@ describe('hapi-rate-limit', () => { expect(res.headers['x-ratelimit-userremaining']).to.equal(299); }); - it('route configured ipWhitelist', async () => { + it('route configured ipWhitelist for specified ip whitelist', async () => { const res = await server.inject({ method: 'GET', url: '/ipWhitelist', headers: { 'x-forwarded-for': '127.0.0.2, 127.0.0.1' } }); - expect(res.headers).to.include(['x-ratelimit-pathlimit', 'x-ratelimit-pathremaining', 'x-ratelimit-pathreset']); - expect(res.headers).to.not.include(['x-ratelimit-userlimit', 'x-ratelimit-userremaining', 'x-ratelimit-userreset']); + expect(res.headers).to.not.include(['x-ratelimit-pathlimit', 'x-ratelimit-pathremaining', 'x-ratelimit-pathreset', 'x-ratelimit-userlimit', 'x-ratelimit-userremaining', 'x-ratelimit-userreset']); }); it('route configured userWhitelist', async () => { @@ -606,6 +605,57 @@ describe('hapi-rate-limit', () => { }); + describe('configured ip whitelist allows ranges', () => { + + let server; + + beforeEach(async () => { + + server = Hapi.server({ + autoListen: false + }); + + server.auth.scheme('trusty', () => { + + return { + authenticate: function (request, h) { + + return h.authenticated({ credentials: { id: request.query.id, name: request.query.name } }); + } + }; + }); + server.auth.strategy('trusty', 'trusty'); + + await server.register([{ + plugin: HapiRateLimit, + options: { + ipWhitelist: ['127.0.0.0/8'], + trustProxy: true + } + }]); + server.route(require('./test-routes')); + await server.initialize(); + }); + + it('request from the ip whitelisted range should not count towards path or user remaining', async () => { + + let res; + res = await server.inject({ method: 'GET', url: '/defaults', headers: { 'x-forwarded-for': '127.0.1.256' } }); + expect(res.headers['x-ratelimit-userremaining']).to.equal(299); + expect(res.headers['x-ratelimit-pathremaining']).to.equal(49); + + res = await server.inject({ method: 'GET', url: '/defaults', headers: { 'x-forwarded-for': '127.0.0.1' } }); + expect(res.headers['x-ratelimit-userremaining']).to.undefined(); + expect(res.headers['x-ratelimit-pathremaining']).to.undefined(); + + res = await server.inject({ method: 'GET', url: '/defaults', headers: { 'x-forwarded-for': '127.0.1.256' } }); + expect(res.headers['x-ratelimit-userremaining']).to.equal(298); + expect(res.headers['x-ratelimit-pathremaining']).to.equal(48); + + }); + + }); + describe('configured user limit with strings joi converts to primatives', () => { let server; diff --git a/test/test-routes.js b/test/test-routes.js index 1018701..8806c98 100644 --- a/test/test-routes.js +++ b/test/test-routes.js @@ -276,7 +276,7 @@ module.exports = [{ }, plugins: { 'hapi-rate-limit': { - ipWhitelist: ['127.0.0.1'] + ipWhitelist: ['127.0.0.0/16'] } } }