diff --git a/fbconn/config.go b/fbconn/config.go index b08e25c..994c8f5 100644 --- a/fbconn/config.go +++ b/fbconn/config.go @@ -603,6 +603,9 @@ func parseKeywordValueSettings(s string) (map[string]string, error) { } if s[end] == '\\' { end++ + if end == len(s) { + return nil, errors.New("unterminated quoted string in connection info string") + } } } if end == len(s) { diff --git a/fbconn/config_test.go b/fbconn/config_test.go index b35d721..99227ff 100644 --- a/fbconn/config_test.go +++ b/fbconn/config_test.go @@ -772,3 +772,31 @@ func TestParseConfigEnv(t *testing.T) { }) } } + +// A backslash as the last byte of an unterminated quoted value must be +// rejected as an unterminated string, not panic with a slice bounds error. +// The quoted-string loop advances past a backslash without bounds-checking +// the resulting index, so `='\` (and `0='\`, reached via the fbxpool path) +// previously panicked with "slice bounds out of range [:2] with length 1". +func TestParseConfigKVQuotedTrailingBackslashDoesNotPanic(t *testing.T) { + tests := []struct { + name string + connString string + }{ + {name: "bare quoted trailing backslash", connString: `='\`}, + {name: "quoted trailing backslash with key", connString: `0='\`}, + {name: "quoted trailing backslash after value", connString: `host='a\`}, + {name: "quoted trailing backslash after escaped pair", connString: `host='a\\b\`}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var err error + assert.NotPanics(t, func() { + _, err = fbconn.ParseConfig(tt.connString) + }) + require.Error(t, err) + assert.Contains(t, err.Error(), "unterminated quoted string in connection info string") + }) + } +}