Repository navigation
Validate response text, command tags and namespace delimiters - #866
Open
danieleggert wants to merge 3 commits into
Open
danieleggert wants to merge 3 commits into
danieleggert wants to merge 3 commits into
Conversation
This was referenced Oct 12, 2026
`.no(.init(text: "No mailbox x\r\n* BYE pwned"))` was written verbatim, injecting an untagged response; likewise tagged, BYE, PREAUTH and continuation responses. RFC 9051: `text = 1*(TEXT-CHAR / UTF8-2 / UTF8-3 / UTF8-4)`. New `ResponseText.Text` replaces the `String`; `init?(_:)` rejects NUL, CR, LF and a leading space (the parser skips it), literals trap. Empty stays valid. The parser now accepts UTF-8 text.
`TaggedCommand(tag: "A1 NOOP\r\nA2 DELETE INBOX\r\nA3", command: .noop)` was written verbatim, injecting a command; likewise `APPEND` start. `TaggedResponse(tag: "* BYE x\r\nA1", …)` injected a response. RFC 3501: `tag = 1*<any ASTRING-CHAR except "+">`. New `Command.Tag` replaces the `String` in `TaggedCommand`, `TaggedResponse`, `Response`, `CommandStreamPart`, `AppendCommand`, `BadCommand` and NIOIMAP's state machine errors; `init?(_:)` validates, literals trap. The parser already enforces the production. IMAPCommands converts its numeric tags.
`NamespaceDescription(string: "x", char: "\r\n", …)` wrote a raw CRLF inside `"…"`, injecting a response. `"\""` encoded as `"""`, and `"\\"` as `"\"` (unterminated); non-ASCII was accepted. RFC 2342: `DQUOTE QUOTED-CHAR DQUOTE / nil`. `init(string:delimiter: responseExtensions:)` (was `char:`) throws `InvalidDelimiterError` for non-ASCII, CR, LF, NUL; the delimiter is stored as a byte, read-only. Quoted-specials are escaped on write and parsed when escaped.
danieleggert
force-pushed
the
harden-4-text-and-tags
branch
from
October 24, 2026 09:37
8f7d28a to
76a8328
Compare
danieleggert
marked this pull request as ready for review
October 24, 2026 09:39
mdiep
requested changes
Oct 28, 2026
| /// is a bug to be caught on first run rather than handled. | ||
| /// | ||
| /// - SeeAlso: [RFC 9051 Section 9](https://datatracker.ietf.org/doc/html/rfc9051#section-9) | ||
| public struct Text: Hashable, Sendable { |
Collaborator
There was a problem hiding this comment.
I don't think this one adds value. I'd just leave it as a String.
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.
[4/6] One of the design goals of this library is that the API types can only represent valid state. But that doesn’t hold true today. These 6 stacked PRs are smaller steps towards that goal. #863 #864 #865 #866 #867 #868
Motivation:
Response text, tags and namespace delimiters were written raw:
.no(.init(text: "No mailbox \(name)"))emits any CRLF inname.Modifications:
ResponseText.Textallows UTF-8 (RFC 9051) but no leading space, which the parser drops.[without a code stays allowed: IMAP can't escape it, and the parser produces it.Command.Tagis shared by commands and responses.NamespaceDescription.initthrows, likeMailboxPath.