Skip to content

Improve DNS rebinding fix with IPv6 private range checks#77

Merged
ACK-J merged 4 commits into
mainfrom
cursor/dns-rebinding-improvements-92ba
Jul 11, 2026
Merged

Improve DNS rebinding fix with IPv6 private range checks#77
ACK-J merged 4 commits into
mainfrom
cursor/dns-rebinding-improvements-92ba

Conversation

@ACK-J

@ACK-J ACK-J commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Builds on #73 to close the DNS rebinding bypass (#69).

Changes

  • Replace local_filter regex with URL parsingisLocalRequestUrl() uses the URL API and global/privateAddress.js instead of regex-matching raw URL strings.
  • DNS rebinding protection — resolve domain hostnames via browser.dns.resolve() and block when any address is private.
  • IPv6 coverage — loopback (::1), link-local (fe80::/10), ULA (fc00::/7), IPv4-mapped private addresses.
  • Safer hostname handling — exact localhost matching (excludes localhost.example.com), trailing-dot FQDN normalization, browser-normalized IPv4 forms.
  • Fix url.hostname for DNS — use url.hostname (not url.host) so URLs with ports like http://example.com:8080 resolve correctly.
  • Skip DNS for literal IPs — public IP literals skip redundant DNS lookups; private literals are caught by isLocalRequestUrl().

Review fixes (latest)

Issue Fix
browser.dns.resolve(url.host) included port, causing fail-open Changed to url.hostname
Redundant DNS on http://8.8.8.8 Skip DNS when hostname is a literal IP
Duplicated block logic Extracted blockPortScan() helper

Behavior improvements over old regex

URL Old regex This PR
http://localhost.example.com blocked (false positive) allowed
http://[::1]:8080 missed blocked
http://localtest.me (DNS→127.0.0.1) missed blocked
http://rebinding.local:8080 via /etc/hosts→192.168.1.1 missed blocked

Testing

# Unit checks
node --input-type=module -e "import { isLocalRequestUrl, isPrivateAddress } from './global/privateAddress.js'; ..."

# Manual (Firefox): load temp add-on, from external page console:
fetch('http://localtest.me').catch(() => {});
# → extension popup badge increases, background log: "DNS resolved to private address"

Closes #69. Supersedes #73.

Open in Web Open in Cursor 

GOPJ1 and others added 3 commits May 9, 2026 08:14
Hostnames that resolve to RFC-1918/loopback addresses (e.g. evil.com → 127.0.0.1)
previously bypassed local_filter because the regex only matched literal IPs. After
the regex check, resolve the hostname and reject any address that falls in a private
range. Reuses the existing dns permission and browser.dns.resolve() call.

Closes #69

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extend PR #73's DNS-resolved address validation to cover IPv6 loopback,
link-local, and ULA ranges, plus IPv4-mapped IPv6 addresses. Extract
isPrivateAddress() into global/privateAddress.js and return early when
the request URL cannot be parsed before calling browser.dns.resolve().

Co-authored-by: Hacks and Hops <ACK-J@users.noreply.github.com>
Use isLocalRequestUrl() to detect local/private targets via the URL API
and isPrivateAddress() instead of matching raw URL strings with a large
regex. This correctly rejects localhost.example.com, handles IPv6 literals,
and normalizes hostname edge cases like trailing dots and octal IPv4 forms.

Co-authored-by: Hacks and Hops <ACK-J@users.noreply.github.com>
@ACK-J ACK-J marked this pull request as ready for review July 11, 2026 04:33
Pass url.hostname (not url.host) to browser.dns.resolve() so URLs with
ports are checked correctly. Skip DNS for public IP literals since
isLocalRequestUrl() already handles private literals. Extract
blockPortScan() to deduplicate the port-scan block path.

Co-authored-by: Hacks and Hops <ACK-J@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Domain names that resolve to local addresses aren't blocked

3 participants