diff --git a/services/real-time/app/js/WebsocketAddressManager.js b/services/real-time/app/js/WebsocketAddressManager.js index 6371640fc2..d01f081580 100644 --- a/services/real-time/app/js/WebsocketAddressManager.js +++ b/services/real-time/app/js/WebsocketAddressManager.js @@ -11,17 +11,26 @@ module.exports = class WebsocketAddressManager { } getRemoteIp(clientHandshake) { - if (this.trust) { + if (!clientHandshake) { + return 'client-handshake-missing' + } else if (this.trust) { // create a dummy req object using the client handshake and // connection.remoteAddress for the proxy-addr module to parse - const addressPort = clientHandshake.address - const req = Object.create(clientHandshake, { - connection: { - value: { remoteAddress: addressPort && addressPort.address }, - }, - }) - // return the address parsed from x-forwarded-for - return proxyaddr(req, this.trust) + try { + const addressPort = clientHandshake.address + const req = { + headers: { + 'x-forwarded-for': + clientHandshake.headers && + clientHandshake.headers['x-forwarded-for'], + }, + connection: { remoteAddress: addressPort && addressPort.address }, + } + // return the address parsed from x-forwarded-for + return proxyaddr(req, this.trust) + } catch (err) { + return 'client-handshake-invalid' + } } else { // return the address from the client handshake itself return clientHandshake.address && clientHandshake.address.address diff --git a/services/real-time/test/unit/js/WebsocketAddressManagerTests.js b/services/real-time/test/unit/js/WebsocketAddressManagerTests.js new file mode 100644 index 0000000000..62cffcb85f --- /dev/null +++ b/services/real-time/test/unit/js/WebsocketAddressManagerTests.js @@ -0,0 +1,100 @@ +const SandboxedModule = require('sandboxed-module') +const { expect } = require('chai') +const modulePath = require('path').join( + __dirname, + '../../../app/js/WebsocketAddressManager' +) + +describe('WebsocketAddressManager', function () { + beforeEach(function () { + this.WebsocketAddressManager = SandboxedModule.require(modulePath, { + requires: {}, + }) + }) + + describe('with a proxy configuration', function () { + beforeEach(function () { + this.websocketAddressManager = new this.WebsocketAddressManager( + true, + '127.0.0.1' + ) + }) + + it('should return the client ip address when behind a proxy', function () { + expect( + this.websocketAddressManager.getRemoteIp({ + headers: { + 'x-forwarded-proto': 'https', + 'x-forwarded-for': '123.45.67.89', + }, + address: { address: '127.0.0.1' }, + }) + ).to.equal('123.45.67.89') + }) + + it('should return the client ip address for a direct connection', function () { + expect( + this.websocketAddressManager.getRemoteIp({ + headers: {}, + address: { address: '123.45.67.89' }, + }) + ).to.equal('123.45.67.89') + }) + + it('should return the client ip address when there are no headers in the handshake', function () { + expect( + this.websocketAddressManager.getRemoteIp({ + address: { address: '123.45.67.89' }, + }) + ).to.equal('123.45.67.89') + }) + + it('should return a "client-handshake-missing" response when the handshake is missing', function () { + expect(this.websocketAddressManager.getRemoteIp()).to.equal( + 'client-handshake-missing' + ) + }) + }) + + describe('without a proxy configuration', function () { + beforeEach(function () { + this.websocketAddressManager = new this.WebsocketAddressManager(false) + }) + + it('should return the client ip address for a direct connection', function () { + expect( + this.websocketAddressManager.getRemoteIp({ + headers: {}, + address: { address: '123.45.67.89' }, + }) + ).to.equal('123.45.67.89') + }) + + it('should return undefined if the client ip address is not present', function () { + expect( + this.websocketAddressManager.getRemoteIp({ + headers: {}, + address: { otherAddressProperty: '123.45.67.89' }, + }) + ).to.be.undefined + }) + + it('should return the proxy ip address if there is actually a proxy', function () { + expect( + this.websocketAddressManager.getRemoteIp({ + headers: { + 'x-forwarded-proto': 'https', + 'x-forwarded-for': '123.45.67.89', + }, + address: { address: '127.0.0.1' }, + }) + ).to.equal('127.0.0.1') + }) + + it('should return a "client-handshake-missing" response when the handshake is missing', function () { + expect(this.websocketAddressManager.getRemoteIp()).to.equal( + 'client-handshake-missing' + ) + }) + }) +})