Skip to content

Commit 71926d9

Browse files
serhiy-storchakasethmlarsonclaude
authored
[3.13] gh-143921: Reject NUL, CR and LF in IMAP commands (GH-143922, GH-153067) (GH-153137) (GH-153287)
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) (cherry picked from commit 2981822) Co-authored-by: Seth Michael Larson <seth@python.org> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 39264ed commit 71926d9

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
@@ -132,6 +132,9 @@
132132
# We compile these in _mode_xxx.
133133
_Literal = br'.*{(?P<size>\d+)}$'
134134
_Untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'
135+
# Only NUL, CR and LF are unsafe (they cannot be represented even in
136+
# a quoted string); other control characters are sent quoted.
137+
_control_chars = re.compile(b'[\x00\r\n]')
135138
_non_astring_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff%*\\"]')
136139
_non_list_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff\\"]')
137140
_quoted = re.compile(br'"(?:[^"\\]|\\.)*+"')
@@ -1047,6 +1050,8 @@ def _command(self, name, *args):
10471050
if arg is None: continue
10481051
if isinstance(arg, str):
10491052
arg = bytes(arg, self._encoding)
1053+
if _control_chars.search(arg):
1054+
raise ValueError("NUL, CR and LF not allowed in commands")
10501055
data = data + b' ' + arg
10511056

10521057
literal = self.literal

Lib/test/test_imaplib.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,22 @@ def test_uppercase_command_names(self):
16261626
with self.assertRaises(AttributeError):
16271627
client.NONEXISTENT
16281628

1629+
def test_control_characters(self):
1630+
client, server = self._setup(SimpleIMAPHandler)
1631+
client.login('user', 'pass')
1632+
for c in '\0\r\n':
1633+
with self.assertRaises(ValueError):
1634+
client.select(f'a{c}b')
1635+
# Other control characters are valid in a quoted string and can
1636+
# occur in mailbox names returned by the server, so the client
1637+
# must be able to send them back.
1638+
for c in support.control_characters_c0():
1639+
if c in '\0\r\n':
1640+
continue
1641+
typ, _ = client.select(f'a{c}b')
1642+
self.assertEqual(typ, 'OK')
1643+
self.assertEqual(server.is_selected, [f'"a{c}b"'])
1644+
16291645

16301646
class NewIMAPTests(NewIMAPTestsMixin, unittest.TestCase):
16311647
imap_class = imaplib.IMAP4
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)