fix: hlavičky s identitou přijímat jen z důvěryhodných adres
CI / Generate TypeScript types (push) Successful in 11s
CI / Server unit tests (push) Successful in 31s
CI / Build server (push) Successful in 27s
CI / Build client (push) Successful in 47s
CI / Playwright E2E tests (push) Successful in 1m39s
CI / Build and push Docker image (push) Successful in 45s
CI / Notify (push) Successful in 2s

This commit is contained in:
2026-09-24 16:12:46 +02:00
parent 89d2cc2bc3
commit 7cda7cfdb6
5 changed files with 168 additions and 36 deletions
+64
View File
@@ -0,0 +1,64 @@
import express from 'express';
import request from 'supertest';
import { getTrustedRemoteUser, isTrustedPeer, parseTrustedIps } from '../trustedHeaders';
describe('parseTrustedIps / isTrustedPeer', () => {
const trusted = parseTrustedIps('10.42.0.0/16, 127.0.0.1, ::1, fd00::/8');
test('rozsah CIDR i jednotlivé adresy (IPv4 i IPv6)', () => {
expect(isTrustedPeer('10.42.0.84', trusted)).toBe(true);
expect(isTrustedPeer('10.43.0.1', trusted)).toBe(false);
expect(isTrustedPeer('127.0.0.1', trusted)).toBe(true);
expect(isTrustedPeer('::1', trusted)).toBe(true);
expect(isTrustedPeer('fd12:3456::1', trusted)).toBe(true);
expect(isTrustedPeer('192.168.1.10', trusted)).toBe(false);
});
test('IPv4 mapovaná do IPv6 (jak ji hlásí Node) se porovnává jako IPv4', () => {
expect(isTrustedPeer('::ffff:10.42.0.84', trusted)).toBe(true);
expect(isTrustedPeer('::FFFF:8.8.8.8', trusted)).toBe(false);
});
test('chybějící nebo nesmyslná adresa není důvěryhodná', () => {
expect(isTrustedPeer(undefined, trusted)).toBe(false);
expect(isTrustedPeer('nesmysl', trusted)).toBe(false);
});
test('neplatná konfigurace vyhodí chybu (server nenaběhne)', () => {
expect(() => parseTrustedIps('10.0.0.0/33')).toThrow('neplatný rozsah');
expect(() => parseTrustedIps('proxy.local')).toThrow('neplatná adresa');
expect(() => parseTrustedIps('10.0.0.0/x')).toThrow('neplatný rozsah');
});
test('prázdný seznam nedůvěřuje nikomu', () => {
expect(isTrustedPeer('127.0.0.1', parseTrustedIps(''))).toBe(false);
});
});
describe('getTrustedRemoteUser', () => {
/** Mini aplikace, která vrátí identitu z hlavičky (jako /api/whoami). */
function buildApp(trustedList: string) {
const trusted = parseTrustedIps(trustedList);
const app = express();
app.get('/whoami', (req, res) => res.send(getTrustedRemoteUser(req, 'remote-user', trusted) ?? ''));
return app;
}
test('hlavička od důvěryhodné adresy se přijme (i s diakritikou v latin1)', async () => {
const res = await request(buildApp('127.0.0.1,::1,::ffff:127.0.0.1'))
.get('/whoami')
.set('remote-user', Buffer.from('Novák', 'utf8').toString('latin1'));
expect(res.text).toBe('Novák');
});
test('hlavička od nedůvěryhodné adresy se ignoruje — ani X-Forwarded-For nepomůže', async () => {
const warn = jest.spyOn(console, 'warn').mockImplementation(() => { });
const res = await request(buildApp('10.42.0.0/16'))
.get('/whoami')
.set('remote-user', 'utocnik')
.set('X-Forwarded-For', '10.42.0.84');
expect(res.text).toBe('');
expect(warn).toHaveBeenCalledWith(expect.stringContaining('nedůvěryhodné adresy'));
warn.mockRestore();
});
});