Skip to content

Commit

Permalink
Add eslint config
Browse files Browse the repository at this point in the history
  • Loading branch information
pbojinov committed Jun 1, 2022
1 parent 5972ad9 commit de7c791
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 36 deletions.
7 changes: 7 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": [
"plugin:@shopify/node",
"plugin:@shopify/esnext",
"plugin:@shopify/prettier"
]
}
11 changes: 0 additions & 11 deletions .eslintrc.js

This file was deleted.

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ The user ip is determined by the following order:
11. `req.socket.remoteAddress`
12. `req.connection.socket.remoteAddress`
13. `req.info.remoteAddress`
14. `request.raw` (Fastify)
14. `Cf-Pseudo-IPv4` (Cloudflare fallback)
15. `request.raw` (Fastify)

If an IP address cannot be found, it will return `null`.

Expand Down
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
"ipv4",
"ipv6",
"fastify",
"x-appengine-user-ip"
"x-appengine-user-ip",
"cloudflare",
"Cf-Pseudo-IPv4"
],
"homepage": "/~https://github.com/pbojinov/request-ip",
"bugs": {
Expand Down Expand Up @@ -54,17 +56,18 @@
"coverage": "nyc report --reporter=text-lcov | coveralls",
"test": "nyc --reporter=html --reporter=text --check-coverage --lines=100 --statements=100 tape ./test/index.js"
},
"prettier": "@shopify/prettier-config",
"dependencies": {
"is_js": "^0.9.0"
},
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@shopify/eslint-plugin": "^41.3.0",
"@shopify/prettier-config": "^1.1.2",
"coveralls": "^3.0.2",
"eslint": "^5.8.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.2.0",
"eslint": "^8.16.0",
"nyc": "^13.1.0",
"request": "^2.54.0",
"tap-spec": "^5.0.0",
Expand Down
28 changes: 21 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ function getClientIpFromXForwardedFor(value) {
* @returns {string} ip - The IP address if known, defaulting to empty string if unknown.
*/
function getClientIp(req) {

// Server is probably behind a proxy.
if (req.headers) {

// Standard headers used by Amazon EC2, Heroku, and others.
if (is.ip(req.headers['x-client-ip'])) {
return req.headers['x-client-ip'];
}

// Load-balancers (AWS ELB) or proxies.
const xForwardedFor = getClientIpFromXForwardedFor(req.headers['x-forwarded-for']);
const xForwardedFor = getClientIpFromXForwardedFor(
req.headers['x-forwarded-for'],
);
if (is.ip(xForwardedFor)) {
return xForwardedFor;
}
Expand Down Expand Up @@ -115,7 +115,6 @@ function getClientIp(req) {
if (is.ip(req.headers['x-appengine-user-ip'])) {
return req.headers['x-appengine-user-ip'];
}

}

// Remote address checks.
Expand All @@ -124,7 +123,10 @@ function getClientIp(req) {
if (is.ip(req.connection.remoteAddress)) {
return req.connection.remoteAddress;
}
if (is.existy(req.connection.socket) && is.ip(req.connection.socket.remoteAddress)) {
if (
is.existy(req.connection.socket) &&
is.ip(req.connection.socket.remoteAddress)
) {
return req.connection.socket.remoteAddress;
}
}
Expand All @@ -138,10 +140,22 @@ function getClientIp(req) {
}

// AWS Api Gateway + Lambda
if (is.existy(req.requestContext) && is.existy(req.requestContext.identity) && is.ip(req.requestContext.identity.sourceIp)) {
if (
is.existy(req.requestContext) &&
is.existy(req.requestContext.identity) &&
is.ip(req.requestContext.identity.sourceIp)
) {
return req.requestContext.identity.sourceIp;
}

// Cloudflare fallback
// https://blog.cloudflare.com/eliminating-the-last-reasons-to-not-enable-ipv6/#introducingpseudoipv4
if (req.headers) {
if (is.ip(req.headers['Cf-Pseudo-IPv4'])) {
return req.headers['Cf-Pseudo-IPv4'];
}
}

// Fastify https://www.fastify.io/docs/latest/Reference/Request/
if (is.existy(req.raw)) {
return getClientIp(req.raw);
Expand Down Expand Up @@ -171,7 +185,7 @@ function mw(options) {
const ip = getClientIp(req);
Object.defineProperty(req, attributeName, {
get: () => ip,
configurable: true
configurable: true,
});
next();
};
Expand Down
77 changes: 64 additions & 13 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ test('req.headers is undefined', (t) => {

test('getClientIpFromXForwardedFor', (t) => {
t.plan(3);
t.equal(requestIp.getClientIpFromXForwardedFor('107.77.213.113, 172.31.41.116'), '172.31.41.116');
t.equal(
requestIp.getClientIpFromXForwardedFor('107.77.213.113, 172.31.41.116'),
'172.31.41.116',
);
t.equal(requestIp.getClientIpFromXForwardedFor('unknown, unknown'), null);
t.throws(() => requestIp.getClientIpFromXForwardedFor({}), TypeError);
});
Expand Down Expand Up @@ -121,7 +124,9 @@ test('x-forwarded-for', (t) => {
request(options, (error, response, found) => {
if (!error && response.statusCode === 200) {
// make sure response ip is the same as the one we passed in
const lastIp = options.headers['x-forwarded-for'].split(',')[2].trim();
const lastIp = options.headers['x-forwarded-for']
.split(',')[2]
.trim();
t.equal(lastIp, found);
server.close();
}
Expand All @@ -146,7 +151,9 @@ test('x-forwarded-for with unknown first ip', (t) => {
request(options, (error, response, found) => {
if (!error && response.statusCode === 200) {
// make sure response ip is the same as the one we passed in
const secondIp = options.headers['x-forwarded-for'].split(',')[1].trim();
const secondIp = options.headers['x-forwarded-for']
.split(',')[1]
.trim();
t.equal(secondIp, found);
server.close();
}
Expand All @@ -171,7 +178,10 @@ test('x-forwarded-for with ipv4:port', (t) => {
request(options, (error, response, found) => {
if (!error && response.statusCode === 200) {
// make sure response ip is the same as the one we passed in
const firstIp = options.headers['x-forwarded-for'].split(',')[0].trim().split(':')[0];
const firstIp = options.headers['x-forwarded-for']
.split(',')[0]
.trim()
.split(':')[0];
t.equal(firstIp, found);
server.close();
}
Expand Down Expand Up @@ -442,8 +452,16 @@ test('getClientIp - default', (t) => {

test('request-ip.mw', (t) => {
t.plan(3);
t.equal(typeof requestIp.mw, 'function', 'requestIp.mw - should be a factory function');
t.equal(requestIp.mw.length, 1, 'requestIp.mw expects 1 argument - options');
t.equal(
typeof requestIp.mw,
'function',
'requestIp.mw - should be a factory function',
);
t.equal(
requestIp.mw.length,
1,
'requestIp.mw expects 1 argument - options',
);
t.throws(() => requestIp.mw('fail'), TypeError);
});

Expand All @@ -452,20 +470,28 @@ test('request-ip.mw - used with no arguments', (t) => {
const mw = requestIp.mw();
t.ok(typeof mw === 'function' && mw.length === 3, 'returns a middleware');

const mockReq = { headers: { 'x-forwarded-for': '111.222.111.222' } };
const mockReq = {headers: {'x-forwarded-for': '111.222.111.222'}};
mw(mockReq, null, () => {
t.equal(mockReq.clientIp, '111.222.111.222', "when used - the middleware augments the request object with attribute 'clientIp'");
t.equal(
mockReq.clientIp,
'111.222.111.222',
"when used - the middleware augments the request object with attribute 'clientIp'",
);
});
});

test('request-ip.mw - user code customizes augmented attribute name', (t) => {
t.plan(2);
const mw = requestIp.mw({ attributeName: 'realIp' });
const mw = requestIp.mw({attributeName: 'realIp'});
t.ok(typeof mw === 'function' && mw.length === 3, 'returns a middleware');

const mockReq = { headers: { 'x-forwarded-for': '111.222.111.222' } };
const mockReq = {headers: {'x-forwarded-for': '111.222.111.222'}};
mw(mockReq, null, () => {
t.equal(mockReq.realIp, '111.222.111.222', 'when used - the middleware augments the request object with user-specified attribute name ');
t.equal(
mockReq.realIp,
'111.222.111.222',
'when used - the middleware augments the request object with user-specified attribute name ',
);
});
});

Expand All @@ -474,14 +500,18 @@ test('request-ip.mw - attribute has getter by Object.defineProperty', (t) => {
const mw = requestIp.mw();
t.ok(typeof mw === 'function' && mw.length === 3, 'returns a middleware');

const mockReq = { headers: { 'x-forwarded-for': '111.222.111.222' } };
const mockReq = {headers: {'x-forwarded-for': '111.222.111.222'}};
Object.defineProperty(mockReq, 'clientIp', {
enumerable: true,
configurable: true,
get: () => '1.2.3.4',
});
mw(mockReq, null, () => {
t.equal(mockReq.clientIp, '111.222.111.222', "when used - the middleware augments the request object with attribute 'clientIp'");
t.equal(
mockReq.clientIp,
'111.222.111.222',
"when used - the middleware augments the request object with attribute 'clientIp'",
);
});
});

Expand Down Expand Up @@ -564,3 +594,24 @@ test('Fastify (request.raw) not found', (t) => {
});
t.equal(found, null);
});

test('Cf-Pseudo-IPv4 – Cloudflare fallback', (t) => {
t.plan(1);
const found = requestIp.getClientIp({
headers: {
'Cf-Pseudo-IPv4': '29.74.48.74',
},
});
t.equal(found, '29.74.48.74');
});

test('Cf-Pseudo-IPv4 is not used when other valid headers exist', (t) => {
t.plan(1);
const found = requestIp.getClientIp({
headers: {
'Cf-Pseudo-IPv4': '29.74.48.74',
'x-forwarded-for': '129.78.138.66',
},
});
t.equal(found, '129.78.138.66');
});

0 comments on commit de7c791

Please sign in to comment.