@@ -53,10 +53,26 @@ interface PatternsByProtocol {
5353 both : DomainPattern [ ] ;
5454}
5555
56+ /**
57+ * Defense-in-depth: assert a domain/regex/URL-pattern string is safe for Squid config interpolation.
58+ * Rejects any whitespace (\s covers space, tab, CR, LF, Unicode whitespace) and null bytes.
59+ * Squid config is line-and-space delimited, so any whitespace could inject directives or tokens.
60+ */
61+ function assertSafeForSquidConfig ( value : string ) : string {
62+ if ( / [ \s \0 ] / . test ( value ) ) {
63+ throw new Error (
64+ `SECURITY: Domain or pattern contains whitespace or null bytes and cannot be ` +
65+ `interpolated into squid.conf: ${ JSON . stringify ( value ) } `
66+ ) ;
67+ }
68+ return value ;
69+ }
70+
5671/**
5772 * Helper to add leading dot to domain for Squid subdomain matching
5873 */
5974function formatDomainForSquid ( domain : string ) : string {
75+ assertSafeForSquidConfig ( domain ) ;
6076 return domain . startsWith ( '.' ) ? domain : `.${ domain } ` ;
6177}
6278
@@ -151,7 +167,7 @@ function generateSslBumpSection(
151167 let urlAccessRules = '' ;
152168 if ( urlPatterns && urlPatterns . length > 0 ) {
153169 const urlAcls = urlPatterns
154- . map ( ( pattern , i ) => `acl allowed_url_${ i } url_regex ${ pattern } ` )
170+ . map ( ( pattern , i ) => `acl allowed_url_${ i } url_regex ${ assertSafeForSquidConfig ( pattern ) } ` )
155171 . join ( '\n' ) ;
156172 urlAclSection = `\n# URL pattern ACLs for HTTPS content inspection\n${ urlAcls } \n` ;
157173
@@ -262,7 +278,7 @@ export function generateSquidConfig(config: SquidConfig): string {
262278 aclLines . push ( '' ) ;
263279 aclLines . push ( '# ACL definitions for allowed domain patterns (HTTP and HTTPS)' ) ;
264280 for ( const p of patternsByProto . both ) {
265- aclLines . push ( `acl allowed_domains_regex dstdom_regex -i ${ p . regex } ` ) ;
281+ aclLines . push ( `acl allowed_domains_regex dstdom_regex -i ${ assertSafeForSquidConfig ( p . regex ) } ` ) ;
266282 }
267283 }
268284
@@ -280,7 +296,7 @@ export function generateSquidConfig(config: SquidConfig): string {
280296 aclLines . push ( '' ) ;
281297 aclLines . push ( '# ACL definitions for HTTP-only domain patterns' ) ;
282298 for ( const p of patternsByProto . http ) {
283- aclLines . push ( `acl allowed_http_only_regex dstdom_regex -i ${ p . regex } ` ) ;
299+ aclLines . push ( `acl allowed_http_only_regex dstdom_regex -i ${ assertSafeForSquidConfig ( p . regex ) } ` ) ;
284300 }
285301 }
286302
@@ -298,7 +314,7 @@ export function generateSquidConfig(config: SquidConfig): string {
298314 aclLines . push ( '' ) ;
299315 aclLines . push ( '# ACL definitions for HTTPS-only domain patterns' ) ;
300316 for ( const p of patternsByProto . https ) {
301- aclLines . push ( `acl allowed_https_only_regex dstdom_regex -i ${ p . regex } ` ) ;
317+ aclLines . push ( `acl allowed_https_only_regex dstdom_regex -i ${ assertSafeForSquidConfig ( p . regex ) } ` ) ;
302318 }
303319 }
304320
@@ -362,7 +378,7 @@ export function generateSquidConfig(config: SquidConfig): string {
362378 blockedAclLines . push ( '' ) ;
363379 blockedAclLines . push ( '# ACL definitions for blocked domain patterns (wildcard)' ) ;
364380 for ( const p of blockedPatterns ) {
365- blockedAclLines . push ( `acl blocked_domains_regex dstdom_regex -i ${ p . regex } ` ) ;
381+ blockedAclLines . push ( `acl blocked_domains_regex dstdom_regex -i ${ assertSafeForSquidConfig ( p . regex ) } ` ) ;
366382 }
367383 blockedAccessRules . push ( 'http_access deny blocked_domains_regex' ) ;
368384 }
0 commit comments