Skip to content

Commit 3edc1b5

Browse files
committed
Fix query sanitizer
...when query text has contains Unicode replacement character. uft8.RuneError actually is a valid character.
1 parent f2b3210 commit 3edc1b5

2 files changed

Lines changed: 52 additions & 24 deletions

File tree

internal/sanitize/sanitize.go

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ type Query struct {
1818
Parts []Part
1919
}
2020

21+
// utf.DecodeRune returns the utf8.RuneError for errors. But that is actually rune U+FFFD -- the unicode replacement
22+
// character. utf8.RuneError is not an error if it is also width 3.
23+
//
24+
// https://github.com/jackc/pgx/issues/1380
25+
const replacementcharacterwidth = 3
26+
2127
func (q *Query) Sanitize(args ...interface{}) (string, error) {
2228
argUse := make([]bool, len(args))
2329
buf := &bytes.Buffer{}
@@ -138,11 +144,13 @@ func rawState(l *sqlLexer) stateFn {
138144
return multilineCommentState
139145
}
140146
case utf8.RuneError:
141-
if l.pos-l.start > 0 {
142-
l.parts = append(l.parts, l.src[l.start:l.pos])
143-
l.start = l.pos
147+
if width != replacementcharacterwidth {
148+
if l.pos-l.start > 0 {
149+
l.parts = append(l.parts, l.src[l.start:l.pos])
150+
l.start = l.pos
151+
}
152+
return nil
144153
}
145-
return nil
146154
}
147155
}
148156
}
@@ -160,11 +168,13 @@ func singleQuoteState(l *sqlLexer) stateFn {
160168
}
161169
l.pos += width
162170
case utf8.RuneError:
163-
if l.pos-l.start > 0 {
164-
l.parts = append(l.parts, l.src[l.start:l.pos])
165-
l.start = l.pos
171+
if width != replacementcharacterwidth {
172+
if l.pos-l.start > 0 {
173+
l.parts = append(l.parts, l.src[l.start:l.pos])
174+
l.start = l.pos
175+
}
176+
return nil
166177
}
167-
return nil
168178
}
169179
}
170180
}
@@ -182,11 +192,13 @@ func doubleQuoteState(l *sqlLexer) stateFn {
182192
}
183193
l.pos += width
184194
case utf8.RuneError:
185-
if l.pos-l.start > 0 {
186-
l.parts = append(l.parts, l.src[l.start:l.pos])
187-
l.start = l.pos
195+
if width != replacementcharacterwidth {
196+
if l.pos-l.start > 0 {
197+
l.parts = append(l.parts, l.src[l.start:l.pos])
198+
l.start = l.pos
199+
}
200+
return nil
188201
}
189-
return nil
190202
}
191203
}
192204
}
@@ -228,11 +240,13 @@ func escapeStringState(l *sqlLexer) stateFn {
228240
}
229241
l.pos += width
230242
case utf8.RuneError:
231-
if l.pos-l.start > 0 {
232-
l.parts = append(l.parts, l.src[l.start:l.pos])
233-
l.start = l.pos
243+
if width != replacementcharacterwidth {
244+
if l.pos-l.start > 0 {
245+
l.parts = append(l.parts, l.src[l.start:l.pos])
246+
l.start = l.pos
247+
}
248+
return nil
234249
}
235-
return nil
236250
}
237251
}
238252
}
@@ -249,11 +263,13 @@ func oneLineCommentState(l *sqlLexer) stateFn {
249263
case '\n', '\r':
250264
return rawState
251265
case utf8.RuneError:
252-
if l.pos-l.start > 0 {
253-
l.parts = append(l.parts, l.src[l.start:l.pos])
254-
l.start = l.pos
266+
if width != replacementcharacterwidth {
267+
if l.pos-l.start > 0 {
268+
l.parts = append(l.parts, l.src[l.start:l.pos])
269+
l.start = l.pos
270+
}
271+
return nil
255272
}
256-
return nil
257273
}
258274
}
259275
}
@@ -283,11 +299,13 @@ func multilineCommentState(l *sqlLexer) stateFn {
283299
l.nested--
284300

285301
case utf8.RuneError:
286-
if l.pos-l.start > 0 {
287-
l.parts = append(l.parts, l.src[l.start:l.pos])
288-
l.start = l.pos
302+
if width != replacementcharacterwidth {
303+
if l.pos-l.start > 0 {
304+
l.parts = append(l.parts, l.src[l.start:l.pos])
305+
l.start = l.pos
306+
}
307+
return nil
289308
}
290-
return nil
291309
}
292310
}
293311
}

internal/sanitize/sanitize_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ func TestNewQuery(t *testing.T) {
8888
sql: "select 42, -- \\nis a Deep Thought's favorite number\r$1",
8989
expected: sanitize.Query{Parts: []sanitize.Part{"select 42, -- \\nis a Deep Thought's favorite number\r", 1}},
9090
},
91+
{
92+
// https://github.com/jackc/pgx/issues/1380
93+
sql: "select 'hello w�rld'",
94+
expected: sanitize.Query{Parts: []sanitize.Part{"select 'hello w�rld'"}},
95+
},
96+
{
97+
// Unterminated quoted string
98+
sql: "select 'hello world",
99+
expected: sanitize.Query{Parts: []sanitize.Part{"select 'hello world"}},
100+
},
91101
}
92102

93103
for i, tt := range successTests {

0 commit comments

Comments
 (0)