Skip to content

fix: preserve sslmode when serializing connection config in ToPostgresURL - #5958

Closed
PR1709 wants to merge 1 commit into
supabase:developfrom
PR1709:fix/preserve-sslmode-in-to-postgres-url
Closed

fix: preserve sslmode when serializing connection config in ToPostgresURL#5958
PR1709 wants to merge 1 commit into
supabase:developfrom
PR1709:fix/preserve-sslmode-in-to-postgres-url

Conversation

@PR1709

@PR1709 PR1709 commented Jul 27, 2026

Copy link
Copy Markdown

Fixes #5873

Summary

When pgconn.ParseConfig parses a Postgres database URL containing sslmode (e.g. sslmode=require or sslmode=disable), pgconn resolves sslmode into TLSConfig and Fallbacks and strips sslmode from RuntimeParams.

When utils.ToPostgresURL() subsequently serialized pgconn.Config back into a URL string, it only serialized connect_timeout and RuntimeParams, thereby dropping sslmode. As a result, downstream callers re-parsing the serialized URL reverted to default sslmode behavior (prefer + environment variable fallbacks), losing explicit --db-url sslmode settings.

This PR updates toPostgresURL to derive and append the appropriate sslmode query parameter from the pgconn.Config's TLSConfig and Fallbacks (if not already specified in RuntimeParams), ensuring sslmode requirements survive round-trips.

Testing

  • Added unit test TestPostgresURL_SSLModePreserved in internal/utils/connect_test.go verifying round-trip preservation across all sslmode settings (disable, allow, prefer, require, verify-full).
  • Verified with go test ./....

@PR1709
PR1709 requested a review from a team as a code owner July 27, 2026 15:34
@github-actions

Copy link
Copy Markdown
Contributor

👋 Thanks! The linked issue hasn't been marked open-for-contribution yet, so this pull request is being closed automatically.

A maintainer adds that label once an issue is triaged and ready to be worked on. Please wait for the label before opening a pull request.
See CONTRIBUTING.md for the full workflow. Once a maintainer adds the open-for-contribution label to a linked open issue, reopen or open a new pull request and it will be accepted.

@github-actions github-actions Bot closed this Jul 27, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e2325e228

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +80 to +83
if !config.TLSConfig.InsecureSkipVerify {
return "verify-full"
}
return "require"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve verify-ca instead of downgrading it to require

When an input --db-url uses sslmode=verify-ca, pgconn represents it with TLSConfig.InsecureSkipVerify plus a custom certificate-verification callback. This branch conflates that state with require and serializes sslmode=require; because the CA configuration is not serialized either, reparsing the URL can silently remove the certificate verification the user requested. Distinguish the verification callback state and cover verify-ca in the round-trip test.

Useful? React with 👍 / 👎.

Comment on lines +46 to +47
if _, ok := config.RuntimeParams["sslmode"]; !ok {
queryParams += fmt.Sprintf("&sslmode=%s", getSSLMode(config))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid emitting an sslmode that defeats the TLS probe

Whenever this serialized URL is passed to GetRootCA, it now already contains an sslmode, but isRequireSSL still appends another &sslmode=require in internal/gen/types/types.go. pgconn reads the first value for duplicate URL parameters, so a derived disable or prefer remains effective; against a plaintext-only database the probe can connect without TLS and incorrectly report that TLS is required, causing downstream pg-meta/pg-delta setup to receive CA/TLS configuration for a server that does not support it. The probe should replace the query parameter rather than append a duplicate.

Useful? React with 👍 / 👎.

Comment on lines +71 to +75
if config.TLSConfig == nil {
if len(config.Fallbacks) > 0 && config.Fallbacks[0].TLSConfig != nil {
return "allow"
}
return "disable"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid disabling TLS for manually constructed remote configs

Many remote configs are constructed directly rather than by pgconn.ParseConfig—for example NewDbConfigWithPassword in internal/utils/flags/db_url.go and the direct branch URL in internal/branches/get/get.go—so a nil TLSConfig means no TLS policy has been materialized, not that the user explicitly selected disable. Returning sslmode=disable here makes linked CLI operations and generated POSTGRES_URL_NON_POOLING values use plaintext; before this change, omitting the parameter let pgconn default to prefer, after which ConnectByUrl removed the insecure fallback and effectively required TLS. Preserve that secure default unless disabling TLS was explicit.

Useful? React with 👍 / 👎.

if len(config.Fallbacks) > 0 && config.Fallbacks[0].TLSConfig != nil {
return "allow"
}
return "disable"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Severity: MEDIUM

getSSLMode returns "disable" for any pgconn.Config with TLSConfig == nil — including configs constructed programmatically (not from sslmode=disable in a URL). NewDbConfigWithPassword builds cloud Supabase configs without setting TLSConfig, so ToPostgresURL now writes sslmode=disable into the serialized URL. This URL is stored in .env (as POSTGRES_URL) by the bootstrap command and passed to pgdelta/declarative tools. Applications that read this URL (Prisma, pg, etc.) will connect to the cloud DB without TLS — even if their own default would have been require or prefer — potentially exposing credentials in cleartext.
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: The root cause is that getSSLMode cannot distinguish between a pgconn.Config that had sslmode=disable explicitly set in a URL (which pgconn.ParseConfig resolves by setting TLSConfig = nil and removing sslmode from RuntimeParams) and a programmatically-built config (like NewDbConfigWithPassword) where TLSConfig was simply never set. Both result in the same state: TLSConfig == nil, Fallbacks empty, and sslmode absent from RuntimeParams. Changing line 75 alone to return a different value would break round-trip preservation for explicitly-disabled TLS connections.

The correct fix is in NewDbConfigWithPassword (in internal/utils/flags/db_url.go): add an explicit TLSConfig when constructing the cloud Supabase pgconn.Config. Add "crypto/tls" to imports, then set TLSConfig: &tls.Config{ServerName: utils.GetSupabaseDbHost(projectRef)} on the struct literal. With a non-nil TLSConfig, getSSLMode will correctly return "require" (because InsecureSkipVerify is false and there are no plaintext fallbacks), so ToPostgresURL will serialize sslmode=require into the cloud URL and the .env file written by bootstrap will carry a proper TLS requirement to downstream clients such as Prisma and pg.

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.

sslmode from --db-url is dropped when the connection config is round-tripped through ToPostgresURL()

1 participant