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
3 changes: 2 additions & 1 deletion src/docker-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1906,12 +1906,13 @@ describe('docker-manager', () => {
// May fail after writing configs
}

// Verify squid.conf includes api-proxy in allowed domains
// Verify squid.conf includes api-proxy hostname and IP in allowed domains
const squidConfPath = path.join(testDir, 'squid.conf');
if (fs.existsSync(squidConfPath)) {
const content = fs.readFileSync(squidConfPath, 'utf-8');
expect(content).toContain('github.com');
expect(content).toContain('api-proxy');
expect(content).toContain('172.30.0.30'); // api-proxy IP address
Comment on lines +1909 to +1915
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test verifies that the IP address '172.30.0.30' appears in the Squid config, but it doesn't verify that the IP is correctly formatted for Squid ACLs. Squid's dstdomain ACL type does not match IP addresses - only domain names. IP addresses require the 'dst' ACL type. The test should verify that the IP appears in a 'dst' ACL, not a 'dstdomain' ACL, or alternatively verify that the IP is NOT added if the design is to rely solely on NO_PROXY for direct connections.

Suggested change
// Verify squid.conf includes api-proxy hostname and IP in allowed domains
const squidConfPath = path.join(testDir, 'squid.conf');
if (fs.existsSync(squidConfPath)) {
const content = fs.readFileSync(squidConfPath, 'utf-8');
expect(content).toContain('github.com');
expect(content).toContain('api-proxy');
expect(content).toContain('172.30.0.30'); // api-proxy IP address
// Verify squid.conf includes api-proxy hostname and IP in allowed ACLs
const squidConfPath = path.join(testDir, 'squid.conf');
if (fs.existsSync(squidConfPath)) {
const content = fs.readFileSync(squidConfPath, 'utf-8');
expect(content).toContain('github.com');
expect(content).toContain('api-proxy');
expect(content).toMatch(/^acl\s+\S+\s+dst\s+172\.30\.0\.30/m); // api-proxy IP address in dst ACL

Copilot uses AI. Check for mistakes.
}
});

Expand Down
5 changes: 3 additions & 2 deletions src/docker-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1147,9 +1147,10 @@ export async function writeConfigs(config: WrapperConfig): Promise<void> {

// Write Squid config
// Note: Use container path for SSL database since it's mounted at /var/spool/squid_ssl_db
// When API proxy is enabled and has API keys, add api-proxy to allowed domains so agent can communicate with it
// When API proxy is enabled and has API keys, add api-proxy hostname and IP to allowed domains so agent can communicate with it
// The IP address is necessary because some tools may bypass NO_PROXY settings or use the IP directly
const domainsForSquid = config.enableApiProxy && networkConfig.proxyIp && (config.openaiApiKey || config.anthropicApiKey)
? [...config.allowedDomains, 'api-proxy']
? [...config.allowedDomains, 'api-proxy', networkConfig.proxyIp]
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IP addresses cannot be properly handled by Squid's dstdomain ACL type. The current implementation adds the IP address to the domains array, which will be processed by formatDomainForSquid() and used with the 'dstdomain' ACL. However, Squid's dstdomain ACL is designed for domain names and will not match IP addresses. For IP addresses, Squid requires the 'dst' ACL type instead.

Additionally, formatDomainForSquid() will incorrectly add a leading dot to the IP address (e.g., ".172.30.0.30") since it contains dots, which is semantically incorrect for an IP.

To fix this, IP addresses should either be:

  1. Handled separately and added to Squid config using 'acl allowed_ips dst' instead of 'acl allowed_domains dstdomain', or
  2. Not added to Squid allowlist at all if NO_PROXY is sufficient (which is the intended design based on comments at lines 966-975)

See below for a potential fix:

  // When API proxy is enabled and has API keys, add api-proxy hostname to allowed domains so the agent can communicate with it
  const domainsForSquid = config.enableApiProxy && networkConfig.proxyIp && (config.openaiApiKey || config.anthropicApiKey)
    ? [...config.allowedDomains, 'api-proxy']

Copilot uses AI. Check for mistakes.
: config.allowedDomains;

const squidConfig = generateSquidConfig({
Expand Down
28 changes: 0 additions & 28 deletions src/host-iptables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,34 +226,6 @@ describe('host-iptables', () => {
]);
});

it('should add api-proxy ACCEPT rule when apiProxyIp is provided', async () => {
mockedExeca.mockResolvedValue({
stdout: '',
stderr: '',
exitCode: 0,
} as any);

// Mock chain existence check to return "not exists" (exitCode: 1)
mockedExeca
// Mock getNetworkBridgeName
.mockResolvedValueOnce({ stdout: 'fw-bridge', stderr: '', exitCode: 0 } as any)
// Mock iptables -L DOCKER-USER (permission check)
.mockResolvedValueOnce({ stdout: '', stderr: '', exitCode: 0 } as any)
// Mock chain existence check (does not exist)
.mockResolvedValueOnce({ stdout: '', stderr: '', exitCode: 1 } as any)
// All subsequent calls succeed with default mock
.mockResolvedValue({ stdout: '', stderr: '', exitCode: 0 } as any);

await setupHostIptables('172.30.0.10', 3128, ['8.8.8.8', '8.8.4.4'], '172.30.0.30');

// Verify api-proxy ACCEPT rule was created
expect(mockedExeca).toHaveBeenCalledWith('iptables', [
'-t', 'filter', '-A', 'FW_WRAPPER',
'-s', '172.30.0.30',
'-j', 'ACCEPT',
]);
});

it('should cleanup existing chain before creating new one', async () => {
mockedExeca
// Mock getNetworkBridgeName
Expand Down
Loading