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
JavaScript
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