fix: skip Accept-Language injection for non-routable/local hosts#77
fix: skip Accept-Language injection for non-routable/local hosts#77
Conversation
Co-authored-by: ormidales <46538211+ormidales@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR prevents the extension’s onBeforeSendHeaders handler from spoofing Accept-Language on local/non-routable destinations (e.g., localhost and private address space), preserving normal browser locale behavior in local dev environments.
Changes:
- Adds an
isLocalHost(hostname)helper to detect localhost /.localand various non-routable IP ranges. - Adds an early-return guard in
onBeforeSendHeadersto skip header mutation when the request host is local.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| parts[0] === 10 || | ||
| (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) || | ||
| (parts[0] === 192 && parts[1] === 168) || | ||
| (parts[0] === 169 && parts[1] === 254) |
There was a problem hiding this comment.
isLocalHost does not treat 0.0.0.0 (IPv4 “unspecified” address commonly used as a localhost alias in dev URLs) as local/non-routable, so Accept-Language spoofing would still apply for http://0.0.0.0:… and can continue to break local localization tests. Consider including 0.* (or at least 0.0.0.0) in the IPv4 early-return set if the intent is to skip all non-routable/local destinations.
| (parts[0] === 169 && parts[1] === 254) | |
| (parts[0] === 169 && parts[1] === 254) || | |
| (parts[0] === 0 && parts[1] === 0 && parts[2] === 0 && parts[3] === 0) |
| /** | ||
| * Returns true if the hostname is a local or non-routable address that should | ||
| * not have its Accept-Language header modified. | ||
| * Covers: localhost, .local TLD, loopback/private IPv4, loopback/link-local IPv6. | ||
| * @param {string} hostname Hostname to test. | ||
| * @returns {boolean} | ||
| */ | ||
| const isLocalHost = (hostname) => { | ||
| if (!hostname) return false; | ||
| const h = hostname.toLowerCase(); | ||
|
|
||
| if (h === "localhost" || h.endsWith(".local")) return true; | ||
|
|
||
| if (IPV4_REGEX.test(h)) { | ||
| const parts = h.split(".").map(Number); | ||
| return ( | ||
| parts[0] === 127 || | ||
| parts[0] === 10 || | ||
| (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) || | ||
| (parts[0] === 192 && parts[1] === 168) || | ||
| (parts[0] === 169 && parts[1] === 254) | ||
| ); | ||
| } | ||
|
|
||
| if (IPV6_REGEX.test(h)) { | ||
| const bare = h.replace(/^\[|\]$/g, "").split("%")[0].toLowerCase(); | ||
| return bare === "::1" || bare.startsWith("fe80:") || bare.startsWith("fc") || bare.startsWith("fd"); | ||
| } |
There was a problem hiding this comment.
The JSDoc/summary says IPv6 handling covers only loopback/link-local, but the implementation also treats ULA (fc00::/7 via fc/fd) as local. Please update the comment to match the actual behavior (or narrow the behavior to match the documented scope).
| const isLocalHost = (hostname) => { | ||
| if (!hostname) return false; | ||
| const h = hostname.toLowerCase(); | ||
|
|
||
| if (h === "localhost" || h.endsWith(".local")) return true; |
There was a problem hiding this comment.
The helper name isLocalHost is a bit misleading since it returns true for private RFC1918 ranges and IPv6 ULA as well, not just localhost/loopback. Consider renaming to something like isLocalOrPrivateHost / isNonRoutableHost to better reflect what it matches.
|
@copilot apply changes based on the comments in this thread |
Co-authored-by: ormidales <46538211+ormidales@users.noreply.github.com>
Applied all three review suggestions in commit
|
The
onBeforeSendHeadershandler was unconditionally spoofingAccept-Languageon all HTTP(S) requests, includinglocalhost,.localdomains, and private IP ranges — breaking localization tests in local dev environments with no privacy benefit.Changes
New
isNonRoutableHost(hostname)helper — returnstruefor:localhostand*.localhostnames0.0.0.0), loopback (127.x.x.x), private RFC 1918 (10.*,172.16–31.*,192.168.*), and link-local (169.254.*)::1), link-local (fe80::), and unique-local ULA (fc00::/7)Early-return guard in
onBeforeSendHeaders— placed after the existingexceptionDomainscheck:Requests to local or non-routable addresses pass through unmodified, preserving the browser's native
Accept-Language.Original prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.