diff --git a/src/squid-config.test.ts b/src/squid-config.test.ts index fb63f91..8e4dae5 100644 --- a/src/squid-config.test.ts +++ b/src/squid-config.test.ts @@ -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'); + expect(result).toContain('http_access deny !allowed_domains'); }); it('should handle only pattern domains', () => { @@ -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 + }); + + 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'); + }); + }); }); diff --git a/src/squid-config.ts b/src/squid-config.ts index 7e6e22e..aa9d607 100644 --- a/src/squid-config.ts +++ b/src/squid-config.ts @@ -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]+$ +# 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:]*\\]?$ + # 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