158 lines
5.2 KiB
TypeScript
158 lines
5.2 KiB
TypeScript
import { expect, tap } from '@push.rocks/tapbundle';
|
|
import { SharedSecurityManager } from '../../../ts/core/utils/shared-security-manager.js';
|
|
import type { IRouteConfig, IRouteContext } from '../../../ts/proxies/smart-proxy/models/route-types.js';
|
|
|
|
// Test security manager
|
|
tap.test('Shared Security Manager', async () => {
|
|
let securityManager: SharedSecurityManager;
|
|
|
|
// Set up a new security manager for each test
|
|
securityManager = new SharedSecurityManager({
|
|
maxConnectionsPerIP: 5,
|
|
connectionRateLimitPerMinute: 10
|
|
});
|
|
|
|
tap.test('should validate IPs correctly', async () => {
|
|
// Should allow IPs under connection limit
|
|
expect(securityManager.validateIP('192.168.1.1').allowed).toBeTrue();
|
|
|
|
// Track multiple connections
|
|
for (let i = 0; i < 4; i++) {
|
|
securityManager.trackConnectionByIP('192.168.1.1', `conn_${i}`);
|
|
}
|
|
|
|
// Should still allow IPs under connection limit
|
|
expect(securityManager.validateIP('192.168.1.1').allowed).toBeTrue();
|
|
|
|
// Add one more to reach the limit
|
|
securityManager.trackConnectionByIP('192.168.1.1', 'conn_4');
|
|
|
|
// Should now block IPs over connection limit
|
|
expect(securityManager.validateIP('192.168.1.1').allowed).toBeFalse();
|
|
|
|
// Remove a connection
|
|
securityManager.removeConnectionByIP('192.168.1.1', 'conn_0');
|
|
|
|
// Should allow again after connection is removed
|
|
expect(securityManager.validateIP('192.168.1.1').allowed).toBeTrue();
|
|
});
|
|
|
|
tap.test('should authorize IPs based on allow/block lists', async () => {
|
|
// Test with allow list only
|
|
expect(securityManager.isIPAuthorized('192.168.1.1', ['192.168.1.*'])).toBeTrue();
|
|
expect(securityManager.isIPAuthorized('192.168.2.1', ['192.168.1.*'])).toBeFalse();
|
|
|
|
// Test with block list
|
|
expect(securityManager.isIPAuthorized('192.168.1.5', ['*'], ['192.168.1.5'])).toBeFalse();
|
|
expect(securityManager.isIPAuthorized('192.168.1.1', ['*'], ['192.168.1.5'])).toBeTrue();
|
|
|
|
// Test with both allow and block lists
|
|
expect(securityManager.isIPAuthorized('192.168.1.1', ['192.168.1.*'], ['192.168.1.5'])).toBeTrue();
|
|
expect(securityManager.isIPAuthorized('192.168.1.5', ['192.168.1.*'], ['192.168.1.5'])).toBeFalse();
|
|
});
|
|
|
|
tap.test('should validate route access', async () => {
|
|
const route: IRouteConfig = {
|
|
match: {
|
|
ports: [8080]
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
target: { host: 'target.com', port: 443 }
|
|
},
|
|
security: {
|
|
ipAllowList: ['10.0.0.*', '192.168.1.*'],
|
|
ipBlockList: ['192.168.1.100'],
|
|
maxConnections: 3
|
|
}
|
|
};
|
|
|
|
const allowedContext: IRouteContext = {
|
|
clientIp: '192.168.1.1',
|
|
port: 8080,
|
|
serverIp: '127.0.0.1',
|
|
isTls: false,
|
|
timestamp: Date.now(),
|
|
connectionId: 'test_conn_1'
|
|
};
|
|
|
|
const blockedByIPContext: IRouteContext = {
|
|
...allowedContext,
|
|
clientIp: '192.168.1.100'
|
|
};
|
|
|
|
const blockedByRangeContext: IRouteContext = {
|
|
...allowedContext,
|
|
clientIp: '172.16.0.1'
|
|
};
|
|
|
|
const blockedByMaxConnectionsContext: IRouteContext = {
|
|
...allowedContext,
|
|
connectionId: 'test_conn_4'
|
|
};
|
|
|
|
expect(securityManager.isAllowed(route, allowedContext)).toBeTrue();
|
|
expect(securityManager.isAllowed(route, blockedByIPContext)).toBeFalse();
|
|
expect(securityManager.isAllowed(route, blockedByRangeContext)).toBeFalse();
|
|
|
|
// Test max connections for route - assuming implementation has been updated
|
|
if ((securityManager as any).trackConnectionByRoute) {
|
|
(securityManager as any).trackConnectionByRoute(route, 'conn_1');
|
|
(securityManager as any).trackConnectionByRoute(route, 'conn_2');
|
|
(securityManager as any).trackConnectionByRoute(route, 'conn_3');
|
|
|
|
// Should now block due to max connections
|
|
expect(securityManager.isAllowed(route, blockedByMaxConnectionsContext)).toBeFalse();
|
|
}
|
|
});
|
|
|
|
tap.test('should clean up expired entries', async () => {
|
|
const route: IRouteConfig = {
|
|
match: {
|
|
ports: [8080]
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
target: { host: 'target.com', port: 443 }
|
|
},
|
|
security: {
|
|
rateLimit: {
|
|
enabled: true,
|
|
maxRequests: 5,
|
|
window: 60 // 60 seconds
|
|
}
|
|
}
|
|
};
|
|
|
|
const context: IRouteContext = {
|
|
clientIp: '192.168.1.1',
|
|
port: 8080,
|
|
serverIp: '127.0.0.1',
|
|
isTls: false,
|
|
timestamp: Date.now(),
|
|
connectionId: 'test_conn_1'
|
|
};
|
|
|
|
// Test rate limiting if method exists
|
|
if ((securityManager as any).checkRateLimit) {
|
|
// Add 5 attempts (max allowed)
|
|
for (let i = 0; i < 5; i++) {
|
|
expect((securityManager as any).checkRateLimit(route, context)).toBeTrue();
|
|
}
|
|
|
|
// Should now be blocked
|
|
expect((securityManager as any).checkRateLimit(route, context)).toBeFalse();
|
|
|
|
// Force cleanup (normally runs periodically)
|
|
if ((securityManager as any).cleanup) {
|
|
(securityManager as any).cleanup();
|
|
}
|
|
|
|
// Should still be blocked since entries are not expired yet
|
|
expect((securityManager as any).checkRateLimit(route, context)).toBeFalse();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Export test runner
|
|
export default tap.start(); |