randomayzer/tests/production-transport.test.ts

54 lines
3 KiB
TypeScript

import { createRequire } from 'node:module';
import { createServer, request } from 'node:http';
import { describe, expect, it } from 'vitest';
const require = createRequire(import.meta.url);
const { sanitizeProxyHeaders } = require('../tools/production-transport.cjs');
const origin = new URL('https://random.ochenstarik.ru');
function fake(peer: string) {
return { socket: { remoteAddress: peer }, headers: {
host: 'evil.test', 'x-forwarded-for': '203.0.113.8', 'x-forwarded-proto': 'https',
'x-forwarded-host': 'evil.test', 'x-real-ip': '203.0.113.9', forwarded: 'for=evil',
'cf-connecting-ip': '203.0.113.10', 'x-forwarded-unknown': 'evil',
} as Record<string, string> };
}
describe('production socket trust boundary', () => {
it.each(['192.168.1.2', '10.77.0.2', '127.0.0.1', '::ffff:192.168.1.2'])('ignores all forwarded headers from foreign peer %s', (peer) => {
const req = fake(peer);
sanitizeProxyHeaders(req, { trustedProxyIp: '10.77.0.1', origin });
expect(req.headers['x-forwarded-for']).toBe(peer.replace('::ffff:', ''));
expect(req.headers['x-forwarded-proto']).toBe('http');
expect(req.headers.host).toBe(origin.host);
expect(req.headers.forwarded).toBeUndefined();
expect(req.headers['x-real-ip']).toBeUndefined();
expect(req.headers['cf-connecting-ip']).toBeUndefined();
expect(req.headers['x-forwarded-unknown']).toBeUndefined();
});
it('accepts one overwritten client IP only from the VPS socket peer', () => {
const req = fake('::ffff:10.77.0.1');
sanitizeProxyHeaders(req, { trustedProxyIp: '10.77.0.1', origin });
expect(req.headers['x-forwarded-for']).toBe('203.0.113.8');
expect(req.headers['x-forwarded-proto']).toBe('https');
req.headers['x-forwarded-for'] = '203.0.113.8, 192.0.2.3';
sanitizeProxyHeaders(req, { trustedProxyIp: '10.77.0.1', origin });
expect(req.headers['x-forwarded-for']).toBe('10.77.0.1');
});
it('sanitizes a real loopback HTTP connection before the downstream handler', async () => {
const server = createServer((req, res) => {
sanitizeProxyHeaders(req, { trustedProxyIp: '10.77.0.1', origin });
res.end(JSON.stringify(req.headers));
});
await new Promise<void>(resolve => server.listen(0, '127.0.0.1', resolve));
try {
const address = server.address() as { port: number };
const body = await new Promise<string>((resolve, reject) => {
const req = request({ host: '127.0.0.1', port: address.port, headers: fake('ignored').headers }, res => {
let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => resolve(data));
}); req.on('error', reject); req.end();
});
const headers = JSON.parse(body);
expect(headers['x-forwarded-for']).toBe('127.0.0.1');
expect(headers['x-forwarded-proto']).toBe('http');
expect(headers['x-forwarded-host']).toBe(origin.host);
} finally { await new Promise<void>((resolve, reject) => server.close(err => err ? reject(err) : resolve())); }
});
});