feat(exports): export datagram handler types and align tests with updated nftables and route security APIs
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import { IpUtils } from '../../../ts/core/utils/ip-utils.js';
|
||||
import { IpMatcher } from '../../../ts/core/routing/matchers/ip.js';
|
||||
|
||||
const isGlobIPMatch = (ip: string, patterns: string[]): boolean =>
|
||||
patterns.some((pattern) => IpMatcher.match(pattern, ip));
|
||||
|
||||
const isIPAuthorized = (ip: string, allowedIPs: string[], blockedIPs: string[]): boolean =>
|
||||
IpMatcher.isAuthorized(ip, allowedIPs, blockedIPs);
|
||||
|
||||
// Test the overlap case
|
||||
const result = IpUtils.isIPAuthorized('127.0.0.1', ['127.0.0.1'], ['127.0.0.1']);
|
||||
const result = isIPAuthorized('127.0.0.1', ['127.0.0.1'], ['127.0.0.1']);
|
||||
console.log('Result of IP that is both allowed and blocked:', result);
|
||||
|
||||
// Trace through the code logic
|
||||
@@ -13,10 +19,10 @@ console.log('Step 1 check:', (!ip || (allowedIPs.length === 0 && blockedIPs.leng
|
||||
|
||||
// Check if IP is blocked - blocked IPs take precedence
|
||||
console.log('blockedIPs length > 0:', blockedIPs.length > 0);
|
||||
console.log('isGlobIPMatch result:', IpUtils.isGlobIPMatch(ip, blockedIPs));
|
||||
console.log('Step 2 check (is blocked):', (blockedIPs.length > 0 && IpUtils.isGlobIPMatch(ip, blockedIPs)));
|
||||
console.log('isGlobIPMatch result:', isGlobIPMatch(ip, blockedIPs));
|
||||
console.log('Step 2 check (is blocked):', (blockedIPs.length > 0 && isGlobIPMatch(ip, blockedIPs)));
|
||||
|
||||
// Check if IP is allowed
|
||||
console.log('allowedIPs length === 0:', allowedIPs.length === 0);
|
||||
console.log('isGlobIPMatch for allowed:', IpUtils.isGlobIPMatch(ip, allowedIPs));
|
||||
console.log('Step 3 (is allowed):', allowedIPs.length === 0 || IpUtils.isGlobIPMatch(ip, allowedIPs));
|
||||
console.log('isGlobIPMatch for allowed:', isGlobIPMatch(ip, allowedIPs));
|
||||
console.log('Step 3 (is allowed):', allowedIPs.length === 0 || isGlobIPMatch(ip, allowedIPs));
|
||||
|
||||
@@ -27,7 +27,8 @@ export function loadTestCertificates(): TestCertificates {
|
||||
key: privateKey
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid certificates: ${error.message}`);
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`Invalid certificates: ${message}`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -79,18 +79,23 @@ testFn('NFTables integration tests', async () => {
|
||||
const status = await smartProxy.getNfTablesStatus();
|
||||
console.log('NFTables status:', JSON.stringify(status, null, 2));
|
||||
|
||||
expect(Object.keys(status).length).toEqual(routes.length);
|
||||
if (!status) {
|
||||
throw new Error('Expected NFTables status after SmartProxy start');
|
||||
}
|
||||
|
||||
for (const routeStatus of Object.values(status)) {
|
||||
expect(routeStatus.active).toBeTrue();
|
||||
expect(routeStatus.ruleCount.total).toBeGreaterThan(0);
|
||||
expect(status.activeGroups).toEqual(routes.length);
|
||||
expect(Object.keys(status.groups).length).toEqual(routes.length);
|
||||
|
||||
for (const routeStatus of Object.values(status.groups)) {
|
||||
expect(routeStatus.ruleCount).toBeGreaterThan(0);
|
||||
expect(routeStatus.createdAt).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
await smartProxy.stop();
|
||||
console.log('SmartProxy stopped');
|
||||
|
||||
const finalStatus = await smartProxy.getNfTablesStatus();
|
||||
expect(Object.keys(finalStatus).length).toEqual(0);
|
||||
expect(finalStatus).toEqual(null);
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
@@ -150,8 +150,9 @@ tap.skip.test('setup NFTables integration test environment', async () => {
|
||||
type: 'forward',
|
||||
forwardingEngine: 'nftables',
|
||||
targets: [{ host: 'localhost', port: TEST_TCP_PORT }],
|
||||
nftables: { protocol: 'tcp', ipAllowList: ['127.0.0.1', '::1'] }
|
||||
nftables: { protocol: 'tcp' }
|
||||
},
|
||||
security: { ipAllowList: ['127.0.0.1', '::1'] },
|
||||
name: 'secure-tcp'
|
||||
},
|
||||
|
||||
@@ -174,7 +175,7 @@ tap.skip.test('setup NFTables integration test environment', async () => {
|
||||
await smartProxy.start();
|
||||
console.log('SmartProxy started successfully');
|
||||
|
||||
const listeningPorts = smartProxy.getListeningPorts();
|
||||
const listeningPorts = await smartProxy.getListeningPorts();
|
||||
console.log(`SmartProxy is listening on ports: ${listeningPorts.join(', ')}`);
|
||||
} catch (err) {
|
||||
console.error('Failed to start SmartProxy:', err);
|
||||
@@ -301,14 +302,19 @@ tap.skip.test('should respect IP allow lists in NFTables', async () => {
|
||||
tap.skip.test('should get NFTables status', async () => {
|
||||
const status = await smartProxy.getNfTablesStatus();
|
||||
|
||||
const statusKeys = Object.keys(status);
|
||||
if (!status) {
|
||||
throw new Error('Expected NFTables status after SmartProxy start');
|
||||
}
|
||||
|
||||
const statusKeys = Object.keys(status.groups);
|
||||
expect(statusKeys.length).toBeGreaterThan(0);
|
||||
|
||||
const firstStatus = status[statusKeys[0]];
|
||||
expect(firstStatus).toHaveProperty('active');
|
||||
expect(firstStatus).toHaveProperty('ruleCount');
|
||||
expect(firstStatus.ruleCount).toHaveProperty('total');
|
||||
expect(firstStatus.ruleCount).toHaveProperty('added');
|
||||
const firstStatus = Object.values(status.groups)[0];
|
||||
if (!firstStatus) {
|
||||
throw new Error('Expected at least one NFTables rule group');
|
||||
}
|
||||
expect(firstStatus.ruleCount).toBeGreaterThan(0);
|
||||
expect(firstStatus.createdAt).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
tap.skip.test('cleanup NFTables integration test environment', async () => {
|
||||
|
||||
@@ -327,12 +327,12 @@ tap.test('Edge Case - Wildcard Domains and Path Matching', async () => {
|
||||
const bestMatch = findBestMatchingRoute(routes, { domain: 'api.example.com', path: '/api/users', port: 443 });
|
||||
expect(bestMatch).not.toBeUndefined();
|
||||
if (bestMatch) {
|
||||
expect(bestMatch.action.targets[0].port).toEqual(3001);
|
||||
expect(bestMatch.action.targets?.[0]?.port).toEqual(3001);
|
||||
}
|
||||
|
||||
const otherMatches = findMatchingRoutes(routes, { domain: 'other.example.com', path: '/api/products', port: 443 });
|
||||
expect(otherMatches.length).toEqual(1);
|
||||
expect(otherMatches[0].action.targets[0].port).toEqual(3000);
|
||||
expect(otherMatches[0]?.action.targets?.[0]?.port).toEqual(3000);
|
||||
});
|
||||
|
||||
tap.test('Edge Case - Disabled Routes', async () => {
|
||||
@@ -353,7 +353,7 @@ tap.test('Edge Case - Disabled Routes', async () => {
|
||||
const matches = findMatchingRoutes(routes, { domain: 'example.com', port: 80 });
|
||||
|
||||
expect(matches.length).toEqual(1);
|
||||
expect(matches[0].action.targets[0].port).toEqual(3000);
|
||||
expect(matches[0]?.action.targets?.[0]?.port).toEqual(3000);
|
||||
});
|
||||
|
||||
tap.test('Edge Case - Complex Path and Headers Matching', async () => {
|
||||
@@ -487,7 +487,7 @@ tap.test('Wildcard Domain Handling', async () => {
|
||||
const bestSpecificMatch = findBestMatchingRoute(routes, specificSubdomainRequest);
|
||||
expect(bestSpecificMatch).not.toBeUndefined();
|
||||
if (bestSpecificMatch) {
|
||||
const matchedPort = bestSpecificMatch.action.targets[0].port;
|
||||
const matchedPort = bestSpecificMatch.action.targets?.[0]?.port;
|
||||
console.log(`Matched route with port: ${matchedPort}`);
|
||||
|
||||
expect(bestSpecificMatch.priority).toEqual(200);
|
||||
@@ -497,7 +497,7 @@ tap.test('Wildcard Domain Handling', async () => {
|
||||
const bestWildcardMatch = findBestMatchingRoute(routes, otherSubdomainRequest);
|
||||
expect(bestWildcardMatch).not.toBeUndefined();
|
||||
if (bestWildcardMatch) {
|
||||
const matchedPort = bestWildcardMatch.action.targets[0].port;
|
||||
const matchedPort = bestWildcardMatch.action.targets?.[0]?.port;
|
||||
console.log(`Matched route with port: ${matchedPort}`);
|
||||
|
||||
expect(bestWildcardMatch.priority).toEqual(100);
|
||||
@@ -573,7 +573,7 @@ tap.test('Route Integration - Combining Multiple Route Types', async () => {
|
||||
expect(webServerMatch).not.toBeUndefined();
|
||||
if (webServerMatch) {
|
||||
expect(webServerMatch.action.type).toEqual('forward');
|
||||
expect(webServerMatch.action.targets[0].host).toEqual('web-server');
|
||||
expect(webServerMatch.action.targets?.[0]?.host).toEqual('web-server');
|
||||
}
|
||||
|
||||
const webRedirectMatch = findBestMatchingRoute(routes, { domain: 'example.com', port: 80 });
|
||||
@@ -590,7 +590,7 @@ tap.test('Route Integration - Combining Multiple Route Types', async () => {
|
||||
expect(apiMatch).not.toBeUndefined();
|
||||
if (apiMatch) {
|
||||
expect(apiMatch.action.type).toEqual('forward');
|
||||
expect(apiMatch.action.targets[0].host).toEqual('api-server');
|
||||
expect(apiMatch.action.targets?.[0]?.host).toEqual('api-server');
|
||||
}
|
||||
|
||||
const wsMatch = findBestMatchingRoute(routes, {
|
||||
@@ -601,7 +601,7 @@ tap.test('Route Integration - Combining Multiple Route Types', async () => {
|
||||
expect(wsMatch).not.toBeUndefined();
|
||||
if (wsMatch) {
|
||||
expect(wsMatch.action.type).toEqual('forward');
|
||||
expect(wsMatch.action.targets[0].host).toEqual('websocket-server');
|
||||
expect(wsMatch.action.targets?.[0]?.host).toEqual('websocket-server');
|
||||
expect(wsMatch.action.websocket?.enabled).toBeTrue();
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,8 @@ tap.test('route-specific IP block list should be enforced', async () => {
|
||||
try {
|
||||
client.write('test data');
|
||||
} catch (e) {
|
||||
console.log('Write failed:', e.message);
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
console.log('Write failed:', message);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -272,4 +273,4 @@ tap.test('routes without security should allow all connections', async () => {
|
||||
});
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
export default tap.start();
|
||||
|
||||
Reference in New Issue
Block a user