import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as smartproxy from '../ts/index.js'; import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js'; import * as net from 'net'; tap.test('route-specific security should be enforced', async () => { // Create a simple echo server for testing const echoServer = net.createServer((socket) => { socket.on('data', (data) => { socket.write(data); }); }); await new Promise((resolve) => { echoServer.listen(8877, '127.0.0.1', () => { console.log('Echo server listening on port 8877'); resolve(); }); }); // Create proxy with route-specific security const routes: IRouteConfig[] = [{ name: 'secure-route', match: { ports: 8878 }, action: { type: 'forward', target: { host: '127.0.0.1', port: 8877 } }, security: { ipAllowList: ['127.0.0.1', '::1', '::ffff:127.0.0.1'] } }]; const proxy = new smartproxy.SmartProxy({ enableDetailedLogging: true, routes: routes }); await proxy.start(); // Test 1: Connection from allowed IP should work const client1 = new net.Socket(); const connected = await new Promise((resolve) => { client1.connect(8878, '127.0.0.1', () => { console.log('Client connected from allowed IP'); resolve(true); }); client1.on('error', (err) => { console.log('Connection error:', err.message); resolve(false); }); // Set timeout to prevent hanging setTimeout(() => resolve(false), 2000); }); if (connected) { // Test echo const testData = 'Hello from allowed IP'; client1.write(testData); const response = await new Promise((resolve) => { client1.once('data', (data) => { resolve(data.toString()); }); setTimeout(() => resolve(''), 2000); }); expect(response).toEqual(testData); client1.destroy(); } else { expect(connected).toBeTrue(); } // Clean up await proxy.stop(); await new Promise((resolve) => { echoServer.close(() => resolve()); }); }); tap.test('route-specific IP block list should be enforced', async () => { // Create a simple echo server for testing const echoServer = net.createServer((socket) => { socket.on('data', (data) => { socket.write(data); }); }); await new Promise((resolve) => { echoServer.listen(8879, '127.0.0.1', () => { console.log('Echo server listening on port 8879'); resolve(); }); }); // Create proxy with route-specific block list const routes: IRouteConfig[] = [{ name: 'blocked-route', match: { ports: 8880 }, action: { type: 'forward', target: { host: '127.0.0.1', port: 8879 } }, security: { ipAllowList: ['0.0.0.0/0', '::/0'], // Allow all IPs ipBlockList: ['127.0.0.1', '::1', '::ffff:127.0.0.1'] // But block localhost } }]; const proxy = new smartproxy.SmartProxy({ enableDetailedLogging: true, routes: routes }); await proxy.start(); // Test: Connection from blocked IP should fail or be immediately closed const client = new net.Socket(); let connectionSuccessful = false; const result = await new Promise<{ connected: boolean; dataReceived: boolean }>((resolve) => { let resolved = false; let dataReceived = false; const doResolve = (connected: boolean) => { if (!resolved) { resolved = true; resolve({ connected, dataReceived }); } }; client.connect(8880, '127.0.0.1', () => { console.log('Client connect event fired'); connectionSuccessful = true; // Try to send data to test if the connection is really established try { client.write('test data'); } catch (e) { console.log('Write failed:', e.message); } }); client.on('data', () => { dataReceived = true; }); client.on('error', (err) => { console.log('Connection error:', err.message); doResolve(false); }); client.on('close', () => { console.log('Connection closed, connectionSuccessful:', connectionSuccessful, 'dataReceived:', dataReceived); doResolve(connectionSuccessful); }); // Set timeout setTimeout(() => doResolve(connectionSuccessful), 1000); }); // The connection should either fail to connect OR connect but immediately close without data exchange if (result.connected) { // If connected, it should have been immediately closed without data exchange expect(result.dataReceived).toBeFalse(); console.log('Connection was established but immediately closed (acceptable behavior)'); } else { // Connection failed entirely (also acceptable) expect(result.connected).toBeFalse(); console.log('Connection was blocked entirely (preferred behavior)'); } if (client.readyState !== 'closed') { client.destroy(); } // Clean up await proxy.stop(); await new Promise((resolve) => { echoServer.close(() => resolve()); }); }); tap.test('routes without security should allow all connections', async () => { // Create a simple echo server for testing const echoServer = net.createServer((socket) => { socket.on('data', (data) => { socket.write(data); }); }); await new Promise((resolve) => { echoServer.listen(8881, '127.0.0.1', () => { console.log('Echo server listening on port 8881'); resolve(); }); }); // Create proxy without route-specific security const routes: IRouteConfig[] = [{ name: 'open-route', match: { ports: 8882 }, action: { type: 'forward', target: { host: '127.0.0.1', port: 8881 } // No security section - should allow all } }]; const proxy = new smartproxy.SmartProxy({ enableDetailedLogging: true, routes: routes }); await proxy.start(); // Test: Connection should work without security restrictions const client = new net.Socket(); const connected = await new Promise((resolve) => { client.connect(8882, '127.0.0.1', () => { console.log('Client connected to open route'); resolve(true); }); client.on('error', (err) => { console.log('Connection error:', err.message); resolve(false); }); // Set timeout setTimeout(() => resolve(false), 2000); }); expect(connected).toBeTrue(); if (connected) { // Test echo const testData = 'Hello from open route'; client.write(testData); const response = await new Promise((resolve) => { client.once('data', (data) => { resolve(data.toString()); }); setTimeout(() => resolve(''), 2000); }); expect(response).toEqual(testData); client.destroy(); } // Clean up await proxy.stop(); await new Promise((resolve) => { echoServer.close(() => resolve()); }); }); export default tap.start();