Handle invalid IPs and localhost lookup failures in location detection

This commit is contained in:
Mike Cao committed 2026-06-11 19:01:04 -07:00
1 parent fa4318fb87
commit f69f76b3b1
2 files changed
+65 -2

No files matched your search

+56 -1
View File
@@ -1,8 +1,23 @@
import { expect, test } from 'vitest';
import { beforeEach, expect, test, vi } from 'vitest';
import { getLocation, hasBlockedIp } from './detect';
import { getIpAddress } from './ip';
const IP = '127.0.0.1';
const isLocalhost = vi.mocked(await import('is-localhost-ip'));
vi.mock('is-localhost-ip', () => ({
default: vi.fn(),
}));
beforeEach(() => {
vi.resetAllMocks();
delete process.env.CLIENT_IP_HEADER;
delete process.env.IGNORE_IP;
delete process.env.SKIP_LOCATION_HEADERS;
});
test('getIpAddress: Custom header', () => {
process.env.CLIENT_IP_HEADER = 'x-custom-ip-header';
@@ -20,3 +35,43 @@ test('getIpAddress: Standard header', () => {
test('getIpAddress: No header', () => {
expect(getIpAddress(new Headers())).toEqual(undefined);
});
test('getLocation: returns null for malformed ip', async () => {
await expect(
getLocation(
'not-an-ip',
new Headers({
'cf-ipcountry': 'US',
'cf-region-code': 'CA',
'cf-ipcity': 'Los Angeles',
}),
false,
),
).resolves.toEqual(null);
});
test('getLocation: treats localhost check errors as non-local', async () => {
isLocalhost.default.mockRejectedValue(new Error('DNS Lookup failed.'));
await expect(
getLocation(
'8.8.8.8',
new Headers({
'cf-ipcountry': 'US',
'cf-region-code': 'CA',
'cf-ipcity': 'Los Angeles',
}),
false,
),
).resolves.toEqual({
country: 'US',
region: 'US-CA',
city: 'Los Angeles',
});
});
test('hasBlockedIp: returns false for malformed client ip with cidr block', () => {
process.env.IGNORE_IP = '10.0.0.0/8';
expect(hasBlockedIp('not-an-ip')).toBe(false);
});
+9 -1
View File
@@ -76,11 +76,19 @@ function decodeHeader(s: string | undefined | null): string | undefined | null {
return Buffer.from(s, 'latin1').toString('utf-8');
}
async function isLocalIp(ip: string) {
try {
return await isLocalhost(ip);
} catch {
return false;
}
}
export async function getLocation(ip: string = '', headers: Headers, skipHeaders: boolean) {
const cleanIp = stripPort(ip);
// Ignore local or invalid ips
if (!cleanIp || !ipaddr.isValid(cleanIp) || (await isLocalhost(cleanIp))) {
if (!cleanIp || !ipaddr.isValid(cleanIp) || (await isLocalIp(cleanIp))) {
return null;
}