]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-143921: Narrow the control character check in imaplib commands (GH-153067)
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 5 Jul 2026 15:25:36 +0000 (18:25 +0300)
committerGitHub <noreply@github.com>
Sun, 5 Jul 2026 15:25:36 +0000 (18:25 +0300)
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.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Lib/imaplib.py
Lib/test/test_imaplib.py
Misc/NEWS.d/next/Library/2026-07-05-10-24-30.gh-issue-143921.wQx3Tn.rst [new file with mode: 0644]

index 085c8d9a5f224ca3b80a9627bc577974fc0df86c..8e271209a664faa25b2c866b032ef2821e1519a6 100644 (file)
@@ -129,7 +129,9 @@ Untagged_status = re.compile(
 # We compile these in _mode_xxx.
 _Literal = br'.*{(?P<size>\d+)}$'
 _Untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'
-_control_chars = re.compile(b'[\x00-\x1F\x7F]')
+# Only NUL, CR and LF are unsafe (they cannot be represented even in
+# a quoted string); other control characters are sent quoted.
+_control_chars = re.compile(b'[\x00\r\n]')
 _non_astring_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff%*\\"]')
 _non_list_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff\\"]')
 _quoted = re.compile(br'"(?:[^"\\]|\\.)*+"')
@@ -1170,7 +1172,7 @@ class IMAP4:
             if isinstance(arg, str):
                 arg = bytes(arg, self._encoding)
             if _control_chars.search(arg):
-                raise ValueError("Control characters not allowed in commands")
+                raise ValueError("NUL, CR and LF not allowed in commands")
             data = data + b' ' + arg
 
         literal = self.literal
index b186dca27aaac017137161b5678a6c7d4a161fb6..ef283267ff08ef0c2b3488a71bc745a6a27fe4ca 100644 (file)
@@ -1751,10 +1751,20 @@ class NewIMAPTestsMixin:
             client.NONEXISTENT
 
     def test_control_characters(self):
-        client, _ = self._setup(SimpleIMAPHandler)
-        for c0 in support.control_characters_c0():
+        client, server = self._setup(SimpleIMAPHandler)
+        client.login('user', 'pass')
+        for c in '\0\r\n':
             with self.assertRaises(ValueError):
-                client.login(f'user{c0}', 'pass')
+                client.select(f'a{c}b')
+        # Other control characters are valid in a quoted string and can
+        # occur in mailbox names returned by the server, so the client
+        # must be able to send them back.
+        for c in support.control_characters_c0():
+            if c in '\0\r\n':
+                continue
+            typ, _ = client.select(f'a{c}b')
+            self.assertEqual(typ, 'OK')
+            self.assertEqual(server.is_selected, [f'"a{c}b"'])
 
     # property tests
 
diff --git a/Misc/NEWS.d/next/Library/2026-07-05-10-24-30.gh-issue-143921.wQx3Tn.rst b/Misc/NEWS.d/next/Library/2026-07-05-10-24-30.gh-issue-143921.wQx3Tn.rst
new file mode 100644 (file)
index 0000000..04e8bd6
--- /dev/null
@@ -0,0 +1,4 @@
+Narrow the control character check in :mod:`imaplib` commands: only NUL, CR
+and LF are now rejected. 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.