/** * Unit Tests für hostGate Middleware * Testet Host-basierte Zugriffskontrolle */ // Setup ENV VOR dem Require process.env.ENABLE_HOST_RESTRICTION = 'true'; process.env.PUBLIC_HOST = 'public.example.com'; process.env.INTERNAL_HOST = 'internal.example.com'; process.env.NODE_ENV = 'development'; let hostGate; // Helper to create mock request with headers const createMockRequest = (hostname, path = '/') => { return { path, get: (headerName) => { if (headerName.toLowerCase() === 'x-forwarded-host') { return hostname; } if (headerName.toLowerCase() === 'host') { return hostname; } return null; } }; }; describe('Host Gate Middleware', () => { let req, res, next; let originalEnv; beforeAll(() => { // Sichere Original-Env originalEnv = { ...process.env }; // Lade Modul NACH ENV setup hostGate = require('../../../src/middlewares/hostGate'); }); beforeEach(() => { // Mock response object res = { status: jest.fn().mockReturnThis(), json: jest.fn() }; // Mock next function next = jest.fn(); // Reset req for each test req = null; // Setup Environment process.env.ENABLE_HOST_RESTRICTION = 'true'; process.env.PUBLIC_HOST = 'public.example.com'; process.env.INTERNAL_HOST = 'internal.example.com'; process.env.NODE_ENV = 'development'; // NOT 'test' to enable restrictions }); afterEach(() => { jest.clearAllMocks(); }); afterAll(() => { // Restore Original-Env process.env = originalEnv; }); describe('Host Detection', () => { test('should detect public host from X-Forwarded-Host header', () => { req = createMockRequest('public.example.com'); hostGate(req, res, next); expect(req.isPublicHost).toBe(true); expect(req.isInternalHost).toBe(false); expect(req.requestSource).toBe('public'); }); test('should detect internal host from X-Forwarded-Host header', () => { req = createMockRequest('internal.example.com'); hostGate(req, res, next); expect(req.isPublicHost).toBe(false); expect(req.isInternalHost).toBe(true); expect(req.requestSource).toBe('internal'); }); test('should fallback to Host header if X-Forwarded-Host not present', () => { req = createMockRequest('public.example.com'); hostGate(req, res, next); expect(req.isPublicHost).toBe(true); }); test('should handle localhost as internal host', () => { req = createMockRequest('localhost:3000'); hostGate(req, res, next); expect(req.isInternalHost).toBe(true); expect(req.isPublicHost).toBe(false); }); test('should strip port from hostname', () => { req = createMockRequest('public.example.com:8080'); hostGate(req, res, next); expect(req.isPublicHost).toBe(true); }); }); describe('Route Protection', () => { test('should block admin routes on public host', () => { req = createMockRequest('public.example.com', '/api/admin/deletion-log'); hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); expect(res.json).toHaveBeenCalledWith({ error: 'Not available on public host', message: 'This endpoint is only available on the internal network' }); expect(next).not.toHaveBeenCalled(); }); test('should block groups routes on public host', () => { req = createMockRequest('public.example.com', '/api/groups'); hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); test('should block slideshow routes on public host', () => { req = createMockRequest('public.example.com', '/api/slideshow'); hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); test('should block migration routes on public host', () => { req = createMockRequest('public.example.com', '/api/migration/start'); hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); test('should block auth login on public host', () => { req = createMockRequest('public.example.com', '/api/auth/login'); hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); }); describe('Allowed Routes', () => { test('should allow upload route on public host', () => { req = createMockRequest('public.example.com', '/api/upload'); hostGate(req, res, next); expect(next).toHaveBeenCalled(); expect(res.status).not.toHaveBeenCalled(); }); test('should allow manage routes on public host', () => { req = createMockRequest('public.example.com', '/api/manage/abc-123'); hostGate(req, res, next); expect(next).toHaveBeenCalled(); }); test('should allow preview routes on public host', () => { req = createMockRequest('public.example.com', '/api/previews/image.jpg'); hostGate(req, res, next); expect(next).toHaveBeenCalled(); }); test('should allow consent routes on public host', () => { req = createMockRequest('public.example.com', '/api/consent'); hostGate(req, res, next); expect(next).toHaveBeenCalled(); }); test('should allow all routes on internal host', () => { req = createMockRequest('internal.example.com', '/api/admin/deletion-log'); hostGate(req, res, next); expect(next).toHaveBeenCalled(); expect(res.status).not.toHaveBeenCalled(); }); }); describe('Feature Flags', () => { test('should bypass restriction when NODE_ENV is test and not explicitly enabled', () => { // Reload module with test environment delete require.cache[require.resolve('../../../src/middlewares/hostGate')]; process.env.NODE_ENV = 'test'; process.env.ENABLE_HOST_RESTRICTION = 'false'; // Explicitly disabled const hostGateTest = require('../../../src/middlewares/hostGate'); req = createMockRequest('public.example.com', '/api/admin/test'); hostGateTest(req, res, next); expect(next).toHaveBeenCalled(); expect(res.status).not.toHaveBeenCalled(); expect(req.isInternalHost).toBe(true); // Restore delete require.cache[require.resolve('../../../src/middlewares/hostGate')]; process.env.NODE_ENV = 'development'; process.env.ENABLE_HOST_RESTRICTION = 'true'; }); test('should work in test environment when explicitly enabled', () => { // Reload module with test environment BUT explicitly enabled delete require.cache[require.resolve('../../../src/middlewares/hostGate')]; process.env.NODE_ENV = 'test'; process.env.ENABLE_HOST_RESTRICTION = 'true'; // Explicitly enabled const hostGateTest = require('../../../src/middlewares/hostGate'); req = createMockRequest('public.example.com', '/api/admin/test'); hostGateTest(req, res, next); // Should block because explicitly enabled expect(res.status).toHaveBeenCalledWith(403); expect(next).not.toHaveBeenCalled(); // Restore delete require.cache[require.resolve('../../../src/middlewares/hostGate')]; process.env.NODE_ENV = 'development'; process.env.ENABLE_HOST_RESTRICTION = 'true'; }); }); describe('Request Source Tracking', () => { test('should set requestSource to "public" for public host', () => { req = createMockRequest('public.example.com', '/api/upload'); hostGate(req, res, next); expect(req.requestSource).toBe('public'); }); test('should set requestSource to "internal" for internal host', () => { req = createMockRequest('internal.example.com', '/api/admin/test'); hostGate(req, res, next); expect(req.requestSource).toBe('internal'); }); test('should set requestSource to "internal" when restrictions disabled', () => { // Reload module with disabled restriction delete require.cache[require.resolve('../../../src/middlewares/hostGate')]; process.env.ENABLE_HOST_RESTRICTION = 'false'; const hostGateDisabled = require('../../../src/middlewares/hostGate'); req = createMockRequest('anything.example.com', '/api/test'); hostGateDisabled(req, res, next); expect(req.requestSource).toBe('internal'); // Restore delete require.cache[require.resolve('../../../src/middlewares/hostGate')]; process.env.ENABLE_HOST_RESTRICTION = 'true'; }); }); });