IP range matched as a text prefix
high
shExpMatch(host, "127.*") or "10.*" compares characters, not addresses. It matches the hostname 127.foo.evil.test and, for "172.*", every public 172.x address. Use isInNet with a mask.
Why it matters
host is a string. shExpMatch(host, "127.*") is true for 127.0.0.1 but also for the
hostname 127.foo.evil.test (lab, 2026-05-20) and any name starting with 127.. "172.*" covers
172.16.0.0/12 and the public ranges 172.0.0.0-172.15.255.255 and
172.32.0.0-172.255.255.255. "192.168.*" matches 192.168.evil.test. A DIRECT branch
written this way can be reached by anyone who controls a hostname starting with digits.
The correct tool is isInNet(host, "127.0.0.0", "255.0.0.0"), ideally guarded by a check
that host is a dotted-quad literal so that no DNS lookup is triggered (PAC-P002).
How to fix
Guard with an IP-literal test and use isInNet(host, "10.0.0.0", "255.0.0.0") etc.; keep "localhost" as a string comparison.
Examples
Bad
function FindProxyForURL(url, host) {
if (shExpMatch(host, "172.*") || shExpMatch(host, "127.*")) {
return "DIRECT";
}
return "PROXY proxy.corp.example:8080";
}
Open bad example in checkerGood
function FindProxyForURL(url, host) {
if (host == "localhost") {
return "DIRECT";
}
if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
(isInNet(host, "172.16.0.0", "255.240.0.0") ||
isInNet(host, "127.0.0.0", "255.0.0.0"))) {
return "DIRECT";
}
return "PROXY proxy.corp.example:8080";
}
Open good example in checkerEngine behaviour
| Engine | Behaviour | Source |
|---|---|---|
| pacparser | "127.*" matches the hostname 127.foo.evil.test; "127example.test" does not (the dot is required). | lab, verified 2026-05-20 |
Related rules
- String prefix used to match an IP range PAC-X007
- RFC 1918 range with the wrong mask PAC-C007
- Only 127.0.0.1 excluded instead of 127.0.0.0/8 PAC-C008
- isInNet called with a hostname PAC-P002