Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
53 changes: 51 additions & 2 deletions src/squid-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,10 @@ describe('generateSquidConfig', () => {
};
const result = generateSquidConfig(config);
expect(result).toContain('acl allowed_domains dstdomain');
expect(result).not.toContain('dstdom_regex');
expect(result).toContain('http_access deny !allowed_domains');
// Should not have domain pattern regex (allowed_domains_regex) for plain domains
// Note: IP blocking ACLs (ip_dst_ipv4, ip_dst_ipv6) use dstdom_regex but are separate
expect(result).not.toContain('allowed_domains_regex');
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The comment clarification about IP blocking ACLs is helpful, but the test name 'should handle only plain domains (backward compatibility)' could be clearer. Consider updating the test description to explicitly mention that it tests backward compatibility while allowing the new IP blocking ACLs, e.g., 'should handle only plain domains without pattern ACLs (backward compatibility, IP blocking ACLs are separate)'.

Copilot uses AI. Check for mistakes.
expect(result).toContain('http_access deny !allowed_domains');
});

it('should handle only pattern domains', () => {
Expand Down Expand Up @@ -692,4 +693,52 @@ describe('generateSquidConfig', () => {
expect(result).toContain('# ACL definitions for allowed domain patterns');
});
});

describe('Direct IP Address Blocking (Security)', () => {
it('should include ACL to block direct IPv4 address connections', () => {
const config: SquidConfig = {
domains: ['example.com'],
port: defaultPort,
};
const result = generateSquidConfig(config);
// Should contain IPv4 address blocking ACL
expect(result).toContain('acl ip_dst_ipv4 dstdom_regex');
expect(result).toMatch(/\^\\?\[0-9\]\+/); // Should match IP pattern
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

This regex test pattern is imprecise and may not accurately validate the IPv4 ACL pattern. The test regex /\^\\?\[0-9\]\+/ checks for an optional backslash (\\?) which shouldn't be optional - the actual pattern should have \\. (escaped dot) between octets. Consider testing for the complete pattern or at least a more representative substring like /\^\\[0-9\\]\+\\\\\\.\\[0-9\\]/ to verify the pattern includes escaped dots between digit groups.

Suggested change
expect(result).toMatch(/\^\\?\[0-9\]\+/); // Should match IP pattern
expect(result).toMatch(/\^\[0-9]\+\\\.\[0-9]\+\\\.\[0-9]\+\\\.\[0-9]\+\$/); // Should match full IPv4 regex pattern

Copilot uses AI. Check for mistakes.
});

it('should include ACL to block direct IPv6 address connections', () => {
const config: SquidConfig = {
domains: ['example.com'],
port: defaultPort,
};
const result = generateSquidConfig(config);
// Should contain IPv6 address blocking ACL
expect(result).toContain('acl ip_dst_ipv6 dstdom_regex');
});

it('should deny access to IP addresses before domain filtering', () => {
const config: SquidConfig = {
domains: ['example.com'],
port: defaultPort,
};
const result = generateSquidConfig(config);
// Deny rules should be present and before domain filtering
expect(result).toContain('http_access deny ip_dst_ipv4');
expect(result).toContain('http_access deny ip_dst_ipv6');

// Verify order: IP blocking comes before domain filtering
const ipv4DenyIndex = result.indexOf('http_access deny ip_dst_ipv4');
const domainFilterIndex = result.indexOf('http_access deny !allowed_domains');
expect(ipv4DenyIndex).toBeLessThan(domainFilterIndex);
});

it('should include security comment about bypass prevention', () => {
const config: SquidConfig = {
domains: ['example.com'],
port: defaultPort,
};
const result = generateSquidConfig(config);
expect(result).toContain('bypass prevention');
});
});
});
14 changes: 13 additions & 1 deletion src/squid-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,20 @@ acl Safe_ports port 80
acl Safe_ports port 443
acl CONNECT method CONNECT

# Security: Block direct IP address connections (bypass prevention)
# Clients must use domain names, not raw IP addresses
# This prevents bypassing domain-based filtering via direct IP HTTPS connections
acl ip_dst_ipv4 dstdom_regex ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The IPv4 regex pattern is too permissive and will match invalid IP addresses. The pattern [0-9]+ matches one or more digits without bounds, allowing invalid IPs like 999.999.999.999 or 1.2.3.4567. Consider using a more restrictive pattern that validates each octet is between 0-255, such as ^([0-9]{1,3}\\.){3}[0-9]{1,3}$ or the more precise ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$. While the overly permissive pattern will still block most IP-based bypass attempts (since real IPs will match), it may also accidentally match malformed strings that resemble IPs.

Suggested change
acl ip_dst_ipv4 dstdom_regex ^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$
acl ip_dst_ipv4 dstdom_regex ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

Copilot uses AI. Check for mistakes.
# IPv6: Must contain at least one colon (distinguishes from domain names)
# Matches: ::1, fe80::1, 2001:db8::1, [::1] (bracket notation for URLs)
acl ip_dst_ipv6 dstdom_regex ^\\[?[0-9a-fA-F]*:[0-9a-fA-F:]*\\]?$
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The IPv6 regex pattern is overly permissive and will match invalid or malformed IPv6 addresses. The pattern [0-9a-fA-F]* allows zero or more hex digits, which means it would match strings like : (just a colon), ::::::: (multiple colons without hex digits), or [:] (brackets with just a colon). While the requirement of at least one colon helps distinguish from domain names, consider a more restrictive pattern that validates proper IPv6 structure, such as requiring at least one hex digit between colons: ^\\[?([0-9a-fA-F]{1,4}:)+[0-9a-fA-F:]*\\]?$ or using a more complete IPv6 validation regex.

Suggested change
acl ip_dst_ipv6 dstdom_regex ^\\[?[0-9a-fA-F]*:[0-9a-fA-F:]*\\]?$
acl ip_dst_ipv6 dstdom_regex ^\\[?([0-9a-fA-F]{1,4}:)+[0-9a-fA-F:]*\\]?$

Copilot uses AI. Check for mistakes.

# Access rules
# Deny unsafe ports first
# Deny direct IP connections first (before domain filtering)
http_access deny ip_dst_ipv4
http_access deny ip_dst_ipv6

# Deny unsafe ports
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports

Expand Down
Loading