UNPKG

twenty-mcp-server

Version:

Easy-to-install Model Context Protocol server for Twenty CRM. Try instantly with 'npx twenty-mcp-server setup' or install globally for permanent use.

202 lines 8.93 kB
import { describe, it, expect, beforeEach, jest } from '@jest/globals'; import { IPMiddleware } from './ip-middleware.js'; // Mock environment variables const mockEnv = { IP_PROTECTION_ENABLED: 'false', IP_ALLOWLIST: '', TRUSTED_PROXIES: '', IP_BLOCK_UNKNOWN: 'true', }; describe('IPMiddleware', () => { let middleware; let mockReq; let mockRes; beforeEach(() => { // Reset environment Object.keys(mockEnv).forEach(key => { delete process.env[key]; }); middleware = new IPMiddleware(); mockReq = { headers: {}, socket: { remoteAddress: '127.0.0.1' }, }; mockRes = { writeHead: jest.fn(), end: jest.fn(), }; }); describe('Configuration', () => { it('should have IP protection disabled by default', () => { const config = middleware.getConfig(); expect(config.enabled).toBe(false); }); it('should parse IP allowlist from environment', () => { process.env.IP_PROTECTION_ENABLED = 'true'; process.env.IP_ALLOWLIST = '192.168.1.0/24,10.0.0.1'; const newMiddleware = new IPMiddleware(); const config = newMiddleware.getConfig(); expect(config.enabled).toBe(true); expect(config.allowlist).toEqual(['192.168.1.0/24', '10.0.0.1']); }); it('should parse trusted proxies from environment', () => { process.env.TRUSTED_PROXIES = '127.0.0.1,192.168.1.1'; const newMiddleware = new IPMiddleware(); const config = newMiddleware.getConfig(); expect(config.trustedProxies).toEqual(['127.0.0.1', '192.168.1.1']); }); }); describe('IP Protection Disabled', () => { it('should allow all requests when IP protection is disabled', async () => { const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); }); describe('IP Protection Enabled', () => { beforeEach(() => { process.env.IP_PROTECTION_ENABLED = 'true'; process.env.IP_ALLOWLIST = '192.168.1.0/24,10.0.0.1'; middleware = new IPMiddleware(); }); it('should allow localhost connections', async () => { mockReq.socket.remoteAddress = '127.0.0.1'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); it('should allow IPv6 localhost connections', async () => { mockReq.socket.remoteAddress = '::1'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); it('should allow IPs in allowlist', async () => { mockReq.socket.remoteAddress = '192.168.1.100'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); it('should allow exact IP matches', async () => { mockReq.socket.remoteAddress = '10.0.0.1'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); it('should block IPs not in allowlist', async () => { mockReq.socket.remoteAddress = '203.0.113.1'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(false); expect(mockRes.writeHead).toHaveBeenCalledWith(403, expect.any(Object)); expect(mockRes.end).toHaveBeenCalledWith(expect.stringContaining('forbidden')); }); it('should handle CIDR ranges correctly', async () => { // Test IP within CIDR range mockReq.socket.remoteAddress = '192.168.1.50'; let result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); // Test IP outside CIDR range mockReq.socket.remoteAddress = '192.168.2.50'; result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(false); }); }); describe('Trusted Proxies', () => { beforeEach(() => { process.env.IP_PROTECTION_ENABLED = 'true'; process.env.IP_ALLOWLIST = '192.168.1.0/24'; process.env.TRUSTED_PROXIES = '127.0.0.1,10.0.0.1'; middleware = new IPMiddleware(); }); it('should read X-Forwarded-For header from trusted proxy', async () => { mockReq.socket.remoteAddress = '127.0.0.1'; // Trusted proxy mockReq.headers['x-forwarded-for'] = '192.168.1.100, 10.0.0.1'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); it('should read X-Real-IP header from trusted proxy', async () => { mockReq.socket.remoteAddress = '127.0.0.1'; // Trusted proxy mockReq.headers['x-real-ip'] = '192.168.1.100'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); it('should ignore proxy headers from untrusted sources', async () => { mockReq.socket.remoteAddress = '203.0.113.1'; // Untrusted IP mockReq.headers['x-forwarded-for'] = '192.168.1.100'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(false); // Should use socket IP, not header }); }); describe('Unknown IP Handling', () => { beforeEach(() => { process.env.IP_PROTECTION_ENABLED = 'true'; process.env.IP_ALLOWLIST = '192.168.1.0/24'; }); it('should block unknown IPs when IP_BLOCK_UNKNOWN is true', async () => { process.env.IP_BLOCK_UNKNOWN = 'true'; middleware = new IPMiddleware(); mockReq.socket.remoteAddress = undefined; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(false); }); it('should allow unknown IPs when IP_BLOCK_UNKNOWN is false', async () => { process.env.IP_BLOCK_UNKNOWN = 'false'; middleware = new IPMiddleware(); mockReq.socket.remoteAddress = undefined; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); }); describe('IPv6 Support', () => { beforeEach(() => { process.env.IP_PROTECTION_ENABLED = 'true'; process.env.IP_ALLOWLIST = '2001:db8::/32,192.168.1.0/24'; middleware = new IPMiddleware(); }); it('should handle IPv6 CIDR ranges', async () => { // IP within IPv6 CIDR range mockReq.socket.remoteAddress = '2001:db8::1'; let result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); // IP outside IPv6 CIDR range mockReq.socket.remoteAddress = '2001:db9::1'; result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(false); }); it('should handle mixed IPv4 and IPv6 allowlists', async () => { // IPv4 allowed mockReq.socket.remoteAddress = '192.168.1.100'; let result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); // IPv6 allowed mockReq.socket.remoteAddress = '2001:db8::100'; result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(true); }); }); describe('Invalid IP Handling', () => { beforeEach(() => { process.env.IP_PROTECTION_ENABLED = 'true'; process.env.IP_ALLOWLIST = '192.168.1.0/24'; middleware = new IPMiddleware(); }); it('should reject invalid IP addresses', async () => { mockReq.socket.remoteAddress = 'invalid-ip'; const result = await middleware.checkAccess(mockReq, mockRes); expect(result).toBe(false); expect(mockRes.writeHead).toHaveBeenCalledWith(403, expect.any(Object)); }); }); describe('Configuration Updates', () => { it('should allow runtime configuration updates', () => { const newConfig = { enabled: true, allowlist: ['10.0.0.0/8'], blockUnknown: false, }; middleware.updateConfig(newConfig); const config = middleware.getConfig(); expect(config.enabled).toBe(true); expect(config.allowlist).toEqual(['10.0.0.0/8']); expect(config.blockUnknown).toBe(false); }); }); }); //# sourceMappingURL=ip-middleware.test.js.map