Skip to content

Commit 2981822

Browse files
serhiy-storchakasethmlarsonclaude
authored
[3.14] gh-143921: Reject NUL, CR and LF in IMAP commands (GH-143922, GH-153067) (GH-153137)
Combined backport of GH-143922, which rejected all control characters, and GH-153067, which narrowed the check to NUL, CR and LF. Other control characters are valid in quoted strings and are sent quoted. (cherry picked from commit 6262704) (cherry picked from commit d0921ef) Co-authored-by: Seth Michael Larson <seth@python.org> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 406208a commit 2981822

3 files changed

Lines changed: 23 additions & 0 deletions

File tree

Lib/imaplib.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@
131131
# We compile these in _mode_xxx.
132132
_Literal = br'.*{(?P<size>\d+)}$'
133133
_Untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'
134+
# Only NUL, CR and LF are unsafe (they cannot be represented even in
135+
# a quoted string); other control characters are sent quoted.
136+
_control_chars = re.compile(b'[\x00\r\n]')
134137
_non_astring_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff%*\\"]')
135138
_non_list_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff\\"]')
136139
_quoted = re.compile(br'"(?:[^"\\]|\\.)*+"')
@@ -1155,6 +1158,8 @@ def _command(self, name, *args):
11551158
if arg is None: continue
11561159
if isinstance(arg, str):
11571160
arg = bytes(arg, self._encoding)
1161+
if _control_chars.search(arg):
1162+
raise ValueError("NUL, CR and LF not allowed in commands")
11581163
data = data + b' ' + arg
11591164

11601165
literal = self.literal

Lib/test/test_imaplib.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,22 @@ def test_uppercase_command_names(self):
17411741
with self.assertRaises(AttributeError):
17421742
client.NONEXISTENT
17431743

1744+
def test_control_characters(self):
1745+
client, server = self._setup(SimpleIMAPHandler)
1746+
client.login('user', 'pass')
1747+
for c in '\0\r\n':
1748+
with self.assertRaises(ValueError):
1749+
client.select(f'a{c}b')
1750+
# Other control characters are valid in a quoted string and can
1751+
# occur in mailbox names returned by the server, so the client
1752+
# must be able to send them back.
1753+
for c in support.control_characters_c0():
1754+
if c in '\0\r\n':
1755+
continue
1756+
typ, _ = client.select(f'a{c}b')
1757+
self.assertEqual(typ, 'OK')
1758+
self.assertEqual(server.is_selected, [f'"a{c}b"'])
1759+
17441760
# property tests
17451761

17461762
def test_file_property_should_not_be_accessed(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Reject NUL, CR and LF characters in IMAP commands. Other control
2+
characters are allowed and sent quoted.

0 commit comments

Comments
 (0)