From c6595fe3201ae8963ec8636c5531a2352ca86014 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 23 Jul 2026 15:06:29 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bug=20in=20#712,=20testing?= =?UTF-8?q?=20if=20string=20is=20quotable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This bug was introduced by a742a109. When I extracted the `UNQUOTABLE_CHARS` regexp into a constant, I didn't copied it exactly. It was converted from a character class into a sequence of bytes: ```ruby # It should've been this: UNQUOTABLE_CHARS = /[\0\r\n]/n # But I incorrectly copied it as this: UNQUOTABLE_CHARS = /\0\r\n/n ``` If that regexp were the only guard, this regexp would've allowed CRLF injections! Fortunately, quoted strings are now sent using `Net::IMAP::QuotedString` (since #698, released in v0.6.4.1), and that _does_ correctly validate that the input is a valid quoted string. So, the string isn't _sent_ as invalid quoted data. Unfortunately, this does result in an exception for strings that could be valid literals. And, because it crashes while sending command arguments, rather than during validation, the connection will be broken. --- lib/net/imap/command_data.rb | 2 +- test/net/imap/test_imap.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/net/imap/command_data.rb b/lib/net/imap/command_data.rb index a0e04edb..1fd0dc30 100644 --- a/lib/net/imap/command_data.rb +++ b/lib/net/imap/command_data.rb @@ -58,7 +58,7 @@ def send_data(data, tag = nil) end end - UNQUOTABLE_CHARS = /\0\r\n/ + UNQUOTABLE_CHARS = /[\0\r\n]/ ASTRING_SPECIALS = /[(){ \x00-\x1f\x7f%*"\\]/ private_constant :UNQUOTABLE_CHARS, :ASTRING_SPECIALS diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb index d005bc50..b178681c 100644 --- a/test/net/imap/test_imap.rb +++ b/test/net/imap/test_imap.rb @@ -1045,6 +1045,10 @@ def imap.test_args(*args) = send_command("TEST", *args) send_args.call ["\xDE\xAD\xBE\xEF".b] assert_equal "({4}\r\n\xDE\xAD\xBE\xEF)".b, server.commands.pop.args + send_args.call ["hi\rthere\n", "huh?\r\nfake out"] + assert_equal "({9}\r\nhi\rthere\n {14}\r\nhuh?\r\nfake out)".b, + server.commands.pop.args + # enable automatic non-synchronizing literals imap.config.max_non_synchronizing_literal = 1024 buff = bytes = nil