Skip to content

fix(pg-connection-string): strip brackets from IPv6 host#3698

Open
spokodev wants to merge 1 commit into
brianc:masterfrom
spokodev:fix/pg-connection-string-ipv6-host
Open

fix(pg-connection-string): strip brackets from IPv6 host#3698
spokodev wants to merge 1 commit into
brianc:masterfrom
spokodev:fix/pg-connection-string-ipv6-host

Conversation

@spokodev

Copy link
Copy Markdown

Problem

pg-connection-string leaves the URI square brackets on the host for IPv6 literals, so the parsed host is unusable:

const parse = require('pg-connection-string')
parse('postgresql://[::1]:5432/mydb').host       // '[::1]'         (should be '::1')
parse('postgresql://[2001:db8::1]/db').host      // '[2001:db8::1]' (should be '2001:db8::1')

The brackets are RFC 3986 / WHATWG URI delimiters for an IPv6 host, not part of the address. libpq stores the bracket-free literal, and pg passes config.host straight to net.connect / dns.lookup:

pg connection-parameters.js → this.host = '[::1]' → net.connect(port, '[::1]')
→ net.isIP('[::1]') === 0 → dns.lookup('[::1]') → ENOTFOUND

So every IPv6 connection string fails to connect today. There is no test or option covering IPv6, so this is an unguarded gap, not intentional.

Fix

Strip a leading [ / trailing ] from the parsed hostname:

-  const hostname = dummyHost ? '' : result.hostname
+  // The WHATWG URL parser keeps the square brackets around an IPv6 literal
+  // (e.g. [::1]); strip them so config.host is a bare address that libpq, and
+  // node's net.connect / dns.lookup, can use directly.
+  const hostname = (dummyHost ? '' : result.hostname).replace(/^\[(.+)\]$/, '$1')

Unconditional replace (no new branch, keeps 100% branch coverage); a no-op for non-bracketed hosts.

Verification

  • New test: postgres://user:pw@[2001:db8::1]:5432/mydb → host 2001:db8::1, port 5432; [::1]::1. Fails on master, passes with the fix.
  • Confirmed unaffected: IPv4 / DNS hostnames, percent-encoded Unix-socket hosts (%2F...), the ?host= query-param override, and a bracketed database name (%5Bweird%5D stays [weird]).
  • Cross-checked against libpq conninfo_uri_parse_options, which advances past [ and NUL-terminates at ] (stores the bare literal).
  • Full suite: 80 passing, nyc check-coverage 100% maintained.

The WHATWG URL parser keeps the square brackets around an IPv6 literal,
so parse('postgresql://[::1]:5432/db').host returned '[::1]'. libpq
stores the address without the brackets, and consumers pass config.host
straight to net.connect / dns.lookup, which treat '[::1]' as a hostname
and fail with ENOTFOUND. Strip the brackets so IPv6 connection strings
resolve to the bare address.
@spokodev spokodev requested a review from hjr3 as a code owner June 23, 2026 16:05
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.

1 participant