Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(remix): Correctly parse X-Forwarded-For Http header #7329

Merged
merged 3 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/remix/src/utils/getIpAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function getClientIPAddress(headers: Headers): string | null {
return parseForwardedHeader(value);
}

return value?.split(', ');
return value?.split(',').map((v: string) => v.trim());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super-l: This may be slightly more efficient as:

return value ? value.replace(/\s*/gm, '').split(',') : null;

Copy link
Member Author

@Lms24 Lms24 Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally good idea (and I definitely didn't think of this initially) but my only concern here is that we're potentially rewriting individual IP addresses in the header value, very potentially making an invalid IP address appear valid:

'2b01:cb19:8350:ed00:d0 dd:fa5b:de31:8be5, 141.101.69.35' would first be rewritten to
'2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5,141.101.69.35' before it's split, resulting in the function returning 2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5 when it would otherwise have returned 141.101.69.35.

Of course, that's a super unlikely edge case but I'd rather avoid it. Wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't both code snippets (so the regex and the trim variants) return the same thing in this scenario? 😅 But maybe I am missing something here :) really it probably doesn't matter at all, so let's just go with what you wrote 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since trim only looks at trailing/leading white spaces it'd not touch the whitespace in the middle of the originally invalid IP address. (I double checked this with a test, just to be sure because I was also confused 😅 ).

But yeah, I'll go with the original version.

});

// Flatten the array and filter out any falsy entries
Expand Down
37 changes: 37 additions & 0 deletions packages/remix/test/utils/getIpAddress.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { getClientIPAddress } from '../../src/utils/getIpAddress';

class Headers {
private _headers: Record<string, string> = {};

get(key: string): string | null {
return this._headers[key] ?? null;
}

set(key: string, value: string): void {
this._headers[key] = value;
}
}

describe('getClientIPAddress', () => {
it.each([
[
'2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5,2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5, 141.101.69.35',
'2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5',
Comment on lines +17 to +19
Copy link
Member Author

@Lms24 Lms24 Mar 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test failed before the fix, returning 141.101.69.35 instead of 2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5, due to a missing space after the first comma.

],
[
'2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5, 2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5, 141.101.69.35',
'2b01:cb19:8350:ed00:d0dd:fa5b:de31:8be5',
],
[
'2a01:cb19:8350:ed00:d0dd:INVALID_IP_ADDR:8be5,141.101.69.35,2a01:cb19:8350:ed00:d0dd:fa5b:de31:8be5',
'141.101.69.35',
],
])('should parse the IP from the X-Forwarded-For header %s', (headerValue, expectedIP) => {
const headers = new Headers();
headers.set('X-Forwarded-For', headerValue);

const ip = getClientIPAddress(headers as any);

expect(ip).toEqual(expectedIP);
});
});