Reject NUL, CR and LF in _raw_command arguments before sending - #658
Merged
mjs merged 4 commits intoSep 27, 2026
Merged
Conversation
CR and LF end the command line on the wire, so an argument carrying them, for example a search criterion built from a sender-controlled header, is run by the server as extra commands. _raw_command bypasses imaplib._command, so CPython's check for the same characters does not apply here. Check every argument before the first send, so a bad one never leaves a half-sent command; 8-bit values still go as literals, which may carry CR and LF. Fixes mjs#657
mjs
reviewed
Sep 18, 2026
mjs
left a comment
Owner
There was a problem hiding this comment.
Thanks for this! It's close, just a few little things.
literal is now exported so callers can wrap a value that carries CR or LF, as the new error message suggests. _literal stays as an alias for code that imported the old name. The argument check iterates with itertools.chain instead of building a new list.
search() quoted any value with a space, so a literal holding CR or LF came out as a _quoted string and the new check rejected it, even though the error message says to wrap the value in literal.
Same check as the regex, written like the NUL check next to it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #657
_raw_commandnow checks every argument before anything is sent: NUL is rejected everywhere, and CR or LF are rejected in any argument that would go on the command line. Values that are sent as literals (8-bit, or wrapped in_literal) may still carry CR and LF, since a literal can hold them. The check runs before the firstsendon purpose: a bad argument found after a literal was already sent would leave the server waiting inside a half-sent command. The existing "args must be bytes" check moved into the same pass.This is the same set of characters CPython rejects in
imaplib._command(python/cpython#143921); that check does not reach_raw_command, which writes to the socket directly.Tests in
tests/test_imapclient.py: CRLF in a line argument via_raw_commandand viasearch()raisesValueErrorwith_imap.sendnever called; NUL inside a literal is rejected; CRLF inside a_literalstill goes out as a literal. The three reject cases fail on master. Full suite 267 OK; black, isort, flake8, pylint and mypy clean.