Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions src/docker-manager-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { subnetsOverlap, validateIdNotInSystemRange, getSafeHostUid, getSafeHostGid, getRealUserHome, extractGhHostFromServerUrl, readGitHubPathEntries, mergeGitHubPathEntries, readGitHubEnvEntries, parseGitHubEnvFile, readEnvFile, MIN_REGULAR_UID, ACT_PRESET_BASE_IMAGE, stripScheme } from './host-env';
import { testHelpers, validateIdNotInSystemRange, getSafeHostUid, getSafeHostGid, getRealUserHome, extractGhHostFromServerUrl, readGitHubPathEntries, mergeGitHubPathEntries, readGitHubEnvEntries, parseGitHubEnvFile, readEnvFile, MIN_REGULAR_UID, ACT_PRESET_BASE_IMAGE, stripScheme } from './host-env';
import { parseDifcProxyHost } from './docker-manager';
import * as fs from 'fs';
import * as path from 'path';
Expand All @@ -14,31 +14,31 @@ describe('docker-manager utilities', () => {
describe('subnetsOverlap', () => {

it('should detect overlapping subnets with same CIDR', () => {
expect(subnetsOverlap('172.30.0.0/24', '172.30.0.0/24')).toBe(true);
expect(testHelpers.subnetsOverlap('172.30.0.0/24', '172.30.0.0/24')).toBe(true);
});

it('should detect non-overlapping subnets', () => {
expect(subnetsOverlap('172.30.0.0/24', '172.31.0.0/24')).toBe(false);
expect(subnetsOverlap('192.168.1.0/24', '192.168.2.0/24')).toBe(false);
expect(testHelpers.subnetsOverlap('172.30.0.0/24', '172.31.0.0/24')).toBe(false);
expect(testHelpers.subnetsOverlap('192.168.1.0/24', '192.168.2.0/24')).toBe(false);
});

it('should detect when smaller subnet is inside larger subnet', () => {
expect(subnetsOverlap('172.16.0.0/16', '172.16.5.0/24')).toBe(true);
expect(subnetsOverlap('172.16.5.0/24', '172.16.0.0/16')).toBe(true);
expect(testHelpers.subnetsOverlap('172.16.0.0/16', '172.16.5.0/24')).toBe(true);
expect(testHelpers.subnetsOverlap('172.16.5.0/24', '172.16.0.0/16')).toBe(true);
});

it('should detect partial overlap', () => {
expect(subnetsOverlap('172.30.0.0/23', '172.30.1.0/24')).toBe(true);
expect(testHelpers.subnetsOverlap('172.30.0.0/23', '172.30.1.0/24')).toBe(true);
});

it('should handle Docker default bridge network', () => {
expect(subnetsOverlap('172.17.0.0/16', '172.17.5.0/24')).toBe(true);
expect(subnetsOverlap('172.17.0.0/16', '172.18.0.0/16')).toBe(false);
expect(testHelpers.subnetsOverlap('172.17.0.0/16', '172.17.5.0/24')).toBe(true);
expect(testHelpers.subnetsOverlap('172.17.0.0/16', '172.18.0.0/16')).toBe(false);
});

it('should handle /32 (single host) networks', () => {
expect(subnetsOverlap('192.168.1.1/32', '192.168.1.1/32')).toBe(true);
expect(subnetsOverlap('192.168.1.1/32', '192.168.1.2/32')).toBe(false);
expect(testHelpers.subnetsOverlap('192.168.1.1/32', '192.168.1.1/32')).toBe(true);
expect(testHelpers.subnetsOverlap('192.168.1.1/32', '192.168.1.2/32')).toBe(false);
});
});

Expand Down
2 changes: 2 additions & 0 deletions src/domain-patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ export function isWildcardPattern(domain: string): boolean {
* Uses character class instead of .* to prevent catastrophic backtracking (ReDoS).
* Per RFC 1035, valid domain characters are: letters, digits, hyphens, and dots.
*/
/** @internal Exported only for test assertions — not part of the public API. */
// ts-prune-ignore-next
export const DOMAIN_CHAR_PATTERN = '[a-zA-Z0-9.-]*';

/**
Expand Down
6 changes: 5 additions & 1 deletion src/host-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export function readEnvFile(filePath: string): Record<string, string> {
* Checks if two subnets overlap
* Returns true if the new subnet conflicts with an existing subnet
*/
export function subnetsOverlap(subnet1: string, subnet2: string): boolean {
function subnetsOverlap(subnet1: string, subnet2: string): boolean {
// Parse CIDR notation: "172.17.0.0/16" -> ["172.17.0.0", "16"]
const [ip1, cidr1] = subnet1.split('/');
const [ip2, cidr2] = subnet2.split('/');
Expand All @@ -442,6 +442,10 @@ export function subnetsOverlap(subnet1: string, subnet2: string): boolean {
return (start1 <= end2 && end1 >= start2);
}

/** @internal Exposed only for unit tests — not part of the public API. */
// ts-prune-ignore-next
export const testHelpers = { subnetsOverlap };

/**
* SSL configuration for Docker Compose (when SSL Bump is enabled)
*/
Expand Down
4 changes: 2 additions & 2 deletions src/host-iptables-cleanup.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { execaResult, mockedExeca, setupHostIptablesTestSuite } from './test-helpers/host-iptables-test-setup';
import { cleanupHostIptables, setupHostIptables } from './host-iptables';
import { _testing } from './host-iptables-shared';
import { testHelpers } from './host-iptables-shared';

describe('host-iptables (cleanup)', () => {
setupHostIptablesTestSuite(_testing.resetIpv6State);
setupHostIptablesTestSuite(testHelpers.resetIpv6State);

describe('cleanupHostIptables', () => {
it('should flush and delete both FW_WRAPPER and FW_WRAPPER_V6 chains', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/host-iptables-doh.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { execaResult, mockedExeca, setupHostIptablesTestSuite } from './test-helpers/host-iptables-test-setup';
import { setupHostIptables } from './host-iptables';
import { _testing } from './host-iptables-shared';
import { testHelpers } from './host-iptables-shared';

describe('host-iptables (doh)', () => {
setupHostIptablesTestSuite(_testing.resetIpv6State);
setupHostIptablesTestSuite(testHelpers.resetIpv6State);

describe('setupHostIptables with DoH proxy', () => {
it('should add HTTPS ACCEPT rule for DoH proxy when dohProxyIp is provided', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/host-iptables-host-access.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { mockedExeca, setupDefaultIptablesMocks, setupHostIptablesTestSuite } from './test-helpers/host-iptables-test-setup';
import { HostAccessConfig, setupHostIptables } from './host-iptables';
import { _testing } from './host-iptables-shared';
import { testHelpers } from './host-iptables-shared';

describe('host-iptables (host access)', () => {
setupHostIptablesTestSuite(_testing.resetIpv6State);
setupHostIptablesTestSuite(testHelpers.resetIpv6State);

describe('setupHostIptables with host access', () => {
it('should add gateway ACCEPT rules when hostAccess is enabled', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/host-iptables-network.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { execaResult, mockedExeca, setupHostIptablesTestSuite } from './test-helpers/host-iptables-test-setup';
import { cleanupFirewallNetwork } from './host-iptables-network';
import { ensureFirewallNetwork } from './host-iptables';
import { _testing } from './host-iptables-shared';
import { testHelpers } from './host-iptables-shared';

describe('host-iptables (network)', () => {
setupHostIptablesTestSuite(_testing.resetIpv6State);
setupHostIptablesTestSuite(testHelpers.resetIpv6State);

describe('ensureFirewallNetwork', () => {
it('should return network config when network already exists', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/host-iptables-setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { API_PROXY_PORTS } from './types';
import { execaError, execaResult, mockedExeca, setupDefaultIptablesMocks, setupHostIptablesTestSuite } from './test-helpers/host-iptables-test-setup';
import { isValidPortSpec } from './host-iptables-rules';
import { setupHostIptables } from './host-iptables';
import { _testing } from './host-iptables-shared';
import { testHelpers } from './host-iptables-shared';

// setupHostIptables intentionally allows the inclusive min:max API proxy port window.
const apiProxyPortRange = `${Math.min(...Object.values(API_PROXY_PORTS))}:${Math.max(...Object.values(API_PROXY_PORTS))}`;

describe('host-iptables (setup)', () => {
setupHostIptablesTestSuite(_testing.resetIpv6State);
setupHostIptablesTestSuite(testHelpers.resetIpv6State);

describe('setupHostIptables', () => {
it('should throw error if iptables permission denied', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/host-iptables-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ function resetIpv6State(): void {
ipv6DisabledViaSysctl = false;
}

/** @internal Test-only helpers */
/** @internal Exposed only for unit tests — not part of the public API. */
// ts-prune-ignore-next
export const _testing = { resetIpv6State };
export const testHelpers = { resetIpv6State };

/**
* Gets the bridge interface name for the firewall network
Expand Down
Loading