Skip to content

Commit f2cd7ef

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.15] gh-143921: Narrow the control character check in imaplib commands (GH-153067) (GH-153135)
Only NUL, CR and LF are rejected now. Other control characters are valid in quoted strings and can occur in mailbox names returned by the server, so they are now accepted and sent quoted. (cherry picked from commit d0921ef) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 1289306 commit f2cd7ef

3 files changed

Lines changed: 21 additions & 5 deletions

File tree

Lib/imaplib.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@
129129
# We compile these in _mode_xxx.
130130
_Literal = br'.*{(?P<size>\d+)}$'
131131
_Untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'
132-
_control_chars = re.compile(b'[\x00-\x1F\x7F]')
132+
# Only NUL, CR and LF are unsafe (they cannot be represented even in
133+
# a quoted string); other control characters are sent quoted.
134+
_control_chars = re.compile(b'[\x00\r\n]')
133135
_non_astring_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff%*\\"]')
134136
_non_list_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff\\"]')
135137
_quoted = re.compile(br'"(?:[^"\\]|\\.)*+"')
@@ -1164,7 +1166,7 @@ def _command(self, name, *args):
11641166
if isinstance(arg, str):
11651167
arg = bytes(arg, self._encoding)
11661168
if _control_chars.search(arg):
1167-
raise ValueError("Control characters not allowed in commands")
1169+
raise ValueError("NUL, CR and LF not allowed in commands")
11681170
data = data + b' ' + arg
11691171

11701172
literal = self.literal

Lib/test/test_imaplib.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,10 +1736,20 @@ def test_uppercase_command_names(self):
17361736
client.NONEXISTENT
17371737

17381738
def test_control_characters(self):
1739-
client, _ = self._setup(SimpleIMAPHandler)
1740-
for c0 in support.control_characters_c0():
1739+
client, server = self._setup(SimpleIMAPHandler)
1740+
client.login('user', 'pass')
1741+
for c in '\0\r\n':
17411742
with self.assertRaises(ValueError):
1742-
client.login(f'user{c0}', 'pass')
1743+
client.select(f'a{c}b')
1744+
# Other control characters are valid in a quoted string and can
1745+
# occur in mailbox names returned by the server, so the client
1746+
# must be able to send them back.
1747+
for c in support.control_characters_c0():
1748+
if c in '\0\r\n':
1749+
continue
1750+
typ, _ = client.select(f'a{c}b')
1751+
self.assertEqual(typ, 'OK')
1752+
self.assertEqual(server.is_selected, [f'"a{c}b"'])
17431753

17441754
# property tests
17451755

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Narrow the control character check in :mod:`imaplib` commands: only NUL, CR
2+
and LF are now rejected. Other control characters are valid in quoted
3+
strings and can occur in mailbox names returned by the server, so they are
4+
now accepted and sent quoted.

0 commit comments

Comments
 (0)