mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-04-19 05:18:42 -04:00
160 lines
4.8 KiB
TypeScript
160 lines
4.8 KiB
TypeScript
/**
|
|
* Tests for isLocalAddress IP classification and getClientIp proxy header handling
|
|
*/
|
|
|
|
import { assertEquals } from '@std/assert';
|
|
import { isLocalAddress, getClientIp } from '$auth/network.ts';
|
|
|
|
// IPv4 loopback
|
|
Deno.test('isLocalAddress: loopback 127.0.0.1', () => {
|
|
assertEquals(isLocalAddress('127.0.0.1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: loopback 127.255.255.255', () => {
|
|
assertEquals(isLocalAddress('127.255.255.255'), true);
|
|
});
|
|
|
|
// IPv4 Class A private
|
|
Deno.test('isLocalAddress: class A 10.0.0.1', () => {
|
|
assertEquals(isLocalAddress('10.0.0.1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: class A 10.255.255.255', () => {
|
|
assertEquals(isLocalAddress('10.255.255.255'), true);
|
|
});
|
|
|
|
// IPv4 Class B private
|
|
Deno.test('isLocalAddress: class B 172.16.0.1', () => {
|
|
assertEquals(isLocalAddress('172.16.0.1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: class B 172.31.255.255', () => {
|
|
assertEquals(isLocalAddress('172.31.255.255'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: class B boundary low 172.15.0.1 is public', () => {
|
|
assertEquals(isLocalAddress('172.15.0.1'), false);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: class B boundary high 172.32.0.1 is public', () => {
|
|
assertEquals(isLocalAddress('172.32.0.1'), false);
|
|
});
|
|
|
|
// IPv4 Class C private
|
|
Deno.test('isLocalAddress: class C 192.168.0.1', () => {
|
|
assertEquals(isLocalAddress('192.168.0.1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: class C 192.168.1.100', () => {
|
|
assertEquals(isLocalAddress('192.168.1.100'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: 192.167.1.1 is public', () => {
|
|
assertEquals(isLocalAddress('192.167.1.1'), false);
|
|
});
|
|
|
|
// IPv4 link-local
|
|
Deno.test('isLocalAddress: link-local 169.254.0.1', () => {
|
|
assertEquals(isLocalAddress('169.254.0.1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: 169.253.0.1 is public', () => {
|
|
assertEquals(isLocalAddress('169.253.0.1'), false);
|
|
});
|
|
|
|
// IPv4 public
|
|
Deno.test('isLocalAddress: public 8.8.8.8', () => {
|
|
assertEquals(isLocalAddress('8.8.8.8'), false);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: public 1.1.1.1', () => {
|
|
assertEquals(isLocalAddress('1.1.1.1'), false);
|
|
});
|
|
|
|
// IPv6
|
|
Deno.test('isLocalAddress: IPv6 loopback ::1', () => {
|
|
assertEquals(isLocalAddress('::1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: IPv6 link-local fe80::1', () => {
|
|
assertEquals(isLocalAddress('fe80::1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: IPv6 unique local fc00::1', () => {
|
|
assertEquals(isLocalAddress('fc00::1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: IPv6 unique local fd00::1', () => {
|
|
assertEquals(isLocalAddress('fd00::1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: IPv6 site-local fec0::1', () => {
|
|
assertEquals(isLocalAddress('fec0::1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: IPv6 public 2001:db8::1', () => {
|
|
assertEquals(isLocalAddress('2001:db8::1'), false);
|
|
});
|
|
|
|
// IPv6-mapped IPv4
|
|
Deno.test('isLocalAddress: IPv6-mapped private ::ffff:192.168.1.1', () => {
|
|
assertEquals(isLocalAddress('::ffff:192.168.1.1'), true);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: IPv6-mapped public ::ffff:8.8.8.8', () => {
|
|
assertEquals(isLocalAddress('::ffff:8.8.8.8'), false);
|
|
});
|
|
|
|
// Invalid input
|
|
Deno.test('isLocalAddress: invalid string', () => {
|
|
assertEquals(isLocalAddress('not-an-ip'), false);
|
|
});
|
|
|
|
Deno.test('isLocalAddress: empty string', () => {
|
|
assertEquals(isLocalAddress(''), false);
|
|
});
|
|
|
|
// --- getClientIp: X-Forwarded-For spoofing ---
|
|
|
|
function mockEvent(tcpAddress: string, headers: Record<string, string> = {}) {
|
|
return {
|
|
getClientAddress: () => tcpAddress,
|
|
request: new Request('http://localhost', { headers })
|
|
};
|
|
}
|
|
|
|
Deno.test('getClientIp: trustProxy=false ignores X-Forwarded-For', () => {
|
|
const event = mockEvent('45.33.32.1', { 'X-Forwarded-For': '192.168.1.1' });
|
|
assertEquals(getClientIp(event, false), '45.33.32.1');
|
|
});
|
|
|
|
Deno.test('getClientIp: trustProxy=false — spoofed local IP does not appear local', () => {
|
|
const event = mockEvent('45.33.32.1', { 'X-Forwarded-For': '192.168.1.1' });
|
|
const ip = getClientIp(event, false);
|
|
assertEquals(isLocalAddress(ip), false);
|
|
});
|
|
|
|
Deno.test('getClientIp: trustProxy=true reads X-Forwarded-For', () => {
|
|
const event = mockEvent('10.0.0.5', { 'X-Forwarded-For': '203.0.113.50' });
|
|
assertEquals(getClientIp(event, true), '203.0.113.50');
|
|
});
|
|
|
|
Deno.test('getClientIp: trustProxy=true takes first IP from comma-separated list', () => {
|
|
const event = mockEvent('10.0.0.5', { 'X-Forwarded-For': '203.0.113.50, 10.0.0.5' });
|
|
assertEquals(getClientIp(event, true), '203.0.113.50');
|
|
});
|
|
|
|
Deno.test('getClientIp: trustProxy=false ignores all proxy headers', () => {
|
|
const event = mockEvent('45.33.32.1', {
|
|
'X-Forwarded-For': '192.168.1.1',
|
|
'X-Real-Ip': '10.0.0.1',
|
|
'CF-Connecting-IP': '172.16.0.1'
|
|
});
|
|
assertEquals(getClientIp(event, false), '45.33.32.1');
|
|
});
|
|
|
|
Deno.test('getClientIp: trustProxy=true falls back to getClientAddress when no headers', () => {
|
|
const event = mockEvent('203.0.113.99');
|
|
assertEquals(getClientIp(event, true), '203.0.113.99');
|
|
});
|