/** * 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'; const hostGate = require('../../../src/middlewares/hostGate'); describe('Host Gate Middleware', () => { let req, res, next; let originalEnv; beforeAll(() => { // Sichere Original-Env originalEnv = { ...process.env }; }); beforeEach(() => { // Mock Request req = { get: jest.fn(), path: '/api/admin/test' }; // Mock Response res = { status: jest.fn().mockReturnThis(), json: jest.fn() }; // Mock Next next = jest.fn(); // 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.get.mockImplementation((header) => { if (header === 'x-forwarded-host') return 'public.example.com'; return null; }); 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.get.mockImplementation((header) => { if (header === 'x-forwarded-host') return 'internal.example.com'; return null; }); 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.get.mockImplementation((header) => { if (header === 'x-forwarded-host') return null; if (header === 'host') return 'public.example.com'; return null; }); hostGate(req, res, next); expect(req.isPublicHost).toBe(true); }); test('should handle localhost as internal host', () => { req.get.mockImplementation((header) => { if (header === 'x-forwarded-host') return null; if (header === 'host') return 'localhost:3000'; return null; }); hostGate(req, res, next); expect(req.isInternalHost).toBe(true); expect(req.isPublicHost).toBe(false); }); test('should strip port from hostname', () => { req.get.mockReturnValue('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.get.mockReturnValue('public.example.com'); req.path = '/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.get.mockReturnValue('public.example.com'); req.path = '/api/groups'; hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); test('should block slideshow routes on public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/slideshow'; hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); test('should block migration routes on public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/migration/start'; hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); test('should block auth login on public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/auth/login'; hostGate(req, res, next); expect(res.status).toHaveBeenCalledWith(403); }); }); describe('Allowed Routes', () => { test('should allow upload route on public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/upload'; hostGate(req, res, next); expect(next).toHaveBeenCalled(); expect(res.status).not.toHaveBeenCalled(); }); test('should allow manage routes on public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/manage/abc-123'; hostGate(req, res, next); expect(next).toHaveBeenCalled(); }); test('should allow preview routes on public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/previews/image.jpg'; hostGate(req, res, next); expect(next).toHaveBeenCalled(); }); test('should allow consent routes on public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/consent'; hostGate(req, res, next); expect(next).toHaveBeenCalled(); }); test('should allow all routes on internal host', () => { req.get.mockReturnValue('internal.example.com'); req.path = '/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'; // Not explicitly enabled const hostGateTest = require('../../../src/middlewares/hostGate'); req.get.mockReturnValue('public.example.com'); req.path = '/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', () => { // Already set up correctly process.env.NODE_ENV = 'development'; expect(req.isInternalHost).toBeUndefined(); // Not processed yet, just checking setup }); }); describe('Request Source Tracking', () => { test('should set requestSource to "public" for public host', () => { req.get.mockReturnValue('public.example.com'); req.path = '/api/upload'; hostGate(req, res, next); expect(req.requestSource).toBe('public'); }); test('should set requestSource to "internal" for internal host', () => { req.get.mockReturnValue('internal.example.com'); req.path = '/api/admin/test'; hostGate(req, res, next); expect(req.requestSource).toBe('internal'); }); test('should set requestSource to "internal" when restrictions disabled', () => { process.env.ENABLE_HOST_RESTRICTION = 'false'; req.get.mockReturnValue('anything.example.com'); req.path = '/api/test'; hostGate(req, res, next); expect(req.requestSource).toBe('internal'); }); }); });