diff --git a/apps/cli-go/internal/db/diff/explicit_test.go b/apps/cli-go/internal/db/diff/explicit_test.go index fb8d02a3b1..4863a68a6f 100644 --- a/apps/cli-go/internal/db/diff/explicit_test.go +++ b/apps/cli-go/internal/db/diff/explicit_test.go @@ -23,7 +23,7 @@ func TestResolveExplicitDatabaseRef(t *testing.T) { ref, err := resolveExplicitDatabaseRef(context.Background(), "local", fsys, nil, nil) require.NoError(t, err) - assert.Equal(t, "postgresql://postgres:postgres@127.0.0.1:54322/postgres?connect_timeout=10", ref) + assert.Equal(t, "postgresql://postgres:postgres@127.0.0.1:54322/postgres?connect_timeout=10&sslmode=disable", ref) }) t.Run("passes through database url", func(t *testing.T) { @@ -45,7 +45,7 @@ func TestResolveExplicitDatabaseRef(t *testing.T) { }, nil) require.NoError(t, err) - assert.Equal(t, "postgresql://postgres:secret@db.abcdefghijklmnopqrst.supabase.co:5432/postgres?connect_timeout=10", ref) + assert.Equal(t, "postgresql://postgres:secret@db.abcdefghijklmnopqrst.supabase.co:5432/postgres?connect_timeout=10&sslmode=disable", ref) }) t.Run("rejects unknown target", func(t *testing.T) { diff --git a/apps/cli-go/internal/utils/connect.go b/apps/cli-go/internal/utils/connect.go index e19aaaf523..e5066a3794 100644 --- a/apps/cli-go/internal/utils/connect.go +++ b/apps/cli-go/internal/utils/connect.go @@ -43,6 +43,9 @@ func toPostgresURL(config pgconn.Config, userinfo *url.Userinfo) string { timeoutSecond = 10 } queryParams := fmt.Sprintf("connect_timeout=%d", timeoutSecond) + if _, ok := config.RuntimeParams["sslmode"]; !ok { + queryParams += fmt.Sprintf("&sslmode=%s", getSSLMode(config)) + } for k, v := range config.RuntimeParams { queryParams += fmt.Sprintf("&%s=%s", k, url.QueryEscape(v)) } @@ -61,6 +64,26 @@ func toPostgresURL(config pgconn.Config, userinfo *url.Userinfo) string { ) } +func getSSLMode(config pgconn.Config) string { + if mode, ok := config.RuntimeParams["sslmode"]; ok { + return mode + } + if config.TLSConfig == nil { + if len(config.Fallbacks) > 0 && config.Fallbacks[0].TLSConfig != nil { + return "allow" + } + return "disable" + } + if len(config.Fallbacks) > 0 && config.Fallbacks[0].TLSConfig == nil { + return "prefer" + } + if !config.TLSConfig.InsecureSkipVerify { + return "verify-full" + } + return "require" +} + + var ErrPrimaryNotFound = errors.New("primary database not found") func GetPoolerConfigPrimary(ctx context.Context, ref string) (api.SupavisorConfigResponse, error) { diff --git a/apps/cli-go/internal/utils/connect_test.go b/apps/cli-go/internal/utils/connect_test.go index a7b772d70f..9a9fcfbbb3 100644 --- a/apps/cli-go/internal/utils/connect_test.go +++ b/apps/cli-go/internal/utils/connect_test.go @@ -2,6 +2,7 @@ package utils import ( "context" + "fmt" "io" "net" "net/http" @@ -392,7 +393,7 @@ func TestPostgresURL(t *testing.T) { "options": "test", }, }) - assert.Equal(t, `postgresql://postgres:%21%40%23$%25%5E&%2A%28%29@[2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432/?connect_timeout=10&options=test`, url) + assert.Equal(t, `postgresql://postgres:%21%40%23$%25%5E&%2A%28%29@[2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432/?connect_timeout=10&sslmode=disable&options=test`, url) } func TestPostgresURLWithoutPassword(t *testing.T) { @@ -408,6 +409,41 @@ func TestPostgresURLWithoutPassword(t *testing.T) { url := ToPostgresURLWithoutPassword(config) // Same as ToPostgresURL but with the password omitted from the userinfo, so a // credential is never written to stdout by the db __shadow seam. - assert.Equal(t, `postgresql://postgres@[2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432/?connect_timeout=10&options=test`, url) + assert.Equal(t, `postgresql://postgres@[2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432/?connect_timeout=10&sslmode=disable&options=test`, url) assert.NotContains(t, url, "%21%40%23") } + +func TestPostgresURL_SSLModePreserved(t *testing.T) { + modes := []struct { + mode string + expectedMode string + }{ + {"disable", "disable"}, + {"allow", "allow"}, + {"prefer", "prefer"}, + {"require", "require"}, + {"verify-full", "verify-full"}, + } + + for _, tc := range modes { + t.Run(tc.mode, func(t *testing.T) { + inputURL := fmt.Sprintf("postgresql://postgres:password@localhost:5432/postgres?sslmode=%s", tc.mode) + config, err := pgconn.ParseConfig(inputURL) + require.NoError(t, err) + + resURL := ToPostgresURL(*config) + assert.Contains(t, resURL, fmt.Sprintf("sslmode=%s", tc.expectedMode)) + + reparsedConfig, err := pgconn.ParseConfig(resURL) + require.NoError(t, err) + + assert.Equal(t, config.TLSConfig != nil, reparsedConfig.TLSConfig != nil) + assert.Equal(t, len(config.Fallbacks), len(reparsedConfig.Fallbacks)) + if config.TLSConfig != nil { + assert.Equal(t, config.TLSConfig.InsecureSkipVerify, reparsedConfig.TLSConfig.InsecureSkipVerify) + } + }) + } +} + +