Skip to content

Commit 21874f9

Browse files
authored
x.json2: fix "\\" scanner bug, disallow (ch < 0x20) unescaped control characters (#23954)
1 parent b2ff9d5 commit 21874f9

3 files changed

Lines changed: 26 additions & 8 deletions

File tree

vlib/x/json2/scanner.v

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,18 @@ fn (mut s Scanner) text_scan() Token {
127127
break
128128
}
129129
ch := s.text[s.pos]
130-
if (s.pos - 1 >= 0 && s.text[s.pos - 1] != `\\`) && ch == `"` {
130+
if ch == `"` {
131131
has_closed = true
132132
break
133-
} else if (s.pos - 1 >= 0 && s.text[s.pos - 1] != `\\`) && ch in important_escapable_chars {
134-
return s.error('character must be escaped with a backslash')
135-
} else if (s.pos == s.text.len - 1 && ch == `\\`) || ch == u8(0) {
136-
return s.error('invalid backslash escape')
137-
} else if s.pos + 1 < s.text.len && ch == `\\` {
133+
} else if ch in important_escapable_chars {
134+
return s.error('character must be escaped with a backslash, replace with: \\${valid_unicode_escapes[important_escapable_chars.index(ch)]}')
135+
} else if ch < 0x20 {
136+
return s.error('character must be escaped with a unicode escape, replace with: \\u${ch:04x}')
137+
} else if ch == `\\` {
138+
if s.pos == s.text.len - 1 {
139+
return s.error('incomplete backslash escape at end of JSON input')
140+
}
141+
138142
peek := s.text[s.pos + 1]
139143
if peek in valid_unicode_escapes {
140144
chrs << unicode_transform_escapes[int(peek)]

vlib/x/json2/scanner_test.v

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,21 @@ fn test_str_invalid_must_be_escape() {
4646
}
4747
tok := sc.scan()
4848
assert tok.kind == .error
49-
assert tok.lit.bytestr() == 'character must be escaped with a backslash'
49+
assert tok.lit.bytestr() == 'character must be escaped with a backslash, replace with: \\${valid_unicode_escapes[important_escapable_chars.index(ch)]}'
50+
}
51+
}
52+
53+
fn test_str_control_must_be_escape() {
54+
for ch := u8(0); ch < 0x20; ch++ {
55+
if ch in important_escapable_chars {
56+
continue
57+
}
58+
mut sc := Scanner{
59+
text: [u8(`"`), `t`, ch, `"`]
60+
}
61+
tok := sc.scan()
62+
assert tok.kind == .error
63+
assert tok.lit.bytestr() == 'character must be escaped with a unicode escape, replace with: \\u${ch:04x}'
5064
}
5165
}
5266

vlib/x/json2/tests/decoder_test.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fn test_raw_decode_string() {
66
}
77

88
fn test_raw_decode_string_escape() {
9-
jstr := json.raw_decode('"\u001b"')!
9+
jstr := json.raw_decode('"\\u001b"')!
1010
str := jstr.str()
1111
assert str.len == 1
1212
assert str[0] == 27

0 commit comments

Comments
 (0)