From a945a9d5ea9b65ea1db86f825e8777d78ad4deca Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 9 Sep 2026 12:37:17 -0400 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=85=20Improve=20TLS/STARTTLS=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This identified a minor `#inspect` bug when TLS fails to connect (or after the connection has disconnected). --- test/net/imap/test_imap_tls.rb | 40 ++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/test/net/imap/test_imap_tls.rb b/test/net/imap/test_imap_tls.rb index 0fbceed8..4f7f3eb8 100644 --- a/test/net/imap/test_imap_tls.rb +++ b/test/net/imap/test_imap_tls.rb @@ -30,6 +30,30 @@ def test_imaps_unknown_ca end end + def assert_tls_unstarted(imap) = assert_tls_stage imap, :unstarted + def assert_tls_incomplete(imap) = assert_tls_stage imap, :incomplete + def assert_tls_unverified(imap) = assert_tls_stage imap, :unverified + def assert_tls_verified(imap) = assert_tls_stage imap, :verified + + INSPECT_INCLUDES = { + unstarted: " PLAINTEXT (TLS NOT STARTED) disconnected", + incomplete: " TLS (NOT ESTABLISHED) disconnected", + unverified: " TLS (NOT VERIFIED) disconnected", + verified: " TLS disconnected", + } + + def assert_tls_stage(imap, stage) + verified = stage == :verified + assert imap.ssl_ctx_params.frozen?, "#ssl_ctx_params should be frozen" + assert_kind_of Hash, imap.ssl_ctx_params, + "#ssl_ctx_params should be a Hash" + assert_kind_of OpenSSL::SSL::SSLContext, imap.ssl_ctx, + "#ssl_ctx should be an OpenSSL::SSL::SSLContext" + assert_equal verified, imap.tls_verified?, + "#tls_verified? should be #{verified}" + assert_include imap.inspect, INSPECT_INCLUDES.fetch(stage) + end + def test_imaps_with_ca_file # Assert verified *after* the imaps_test and assert_nothing_raised blocks. # Otherwise, failures can't logout and need to wait for the timeout. @@ -50,7 +74,7 @@ def test_imaps_with_ca_file end end assert_equal true, verified - assert_equal true, imap.tls_verified? + assert_tls_verified imap assert_equal({ca_file: CA_FILE}, imap.ssl_ctx_params) assert_equal(CA_FILE, imap.ssl_ctx.ca_file) assert_equal(OpenSSL::SSL::VERIFY_PEER, imap.ssl_ctx.verify_mode) @@ -77,7 +101,7 @@ def test_imaps_verify_none end end assert_equal false, verified - assert_equal false, imap.tls_verified? + assert_tls_unverified imap assert_equal({verify_mode: OpenSSL::SSL::VERIFY_NONE}, imap.ssl_ctx_params) assert_equal(nil, imap.ssl_ctx.ca_file) @@ -111,7 +135,8 @@ def test_starttls_unknown_ca end assert_kind_of(OpenSSL::SSL::SSLError, ex) assert_local_backtrace ex - assert_equal false, imap.tls_verified? + pend "Fix TLSSocket#connect detection" do assert_tls_incomplete imap end + assert_tls_unverified imap # TODO: should be incomplete assert_equal({}, imap.ssl_ctx_params) assert_equal(nil, imap.ssl_ctx.ca_file) assert_equal(OpenSSL::SSL::VERIFY_PEER, imap.ssl_ctx.verify_mode) @@ -131,8 +156,7 @@ def test_starttls assert_equal false, initial_verified assert_equal false, initial_params assert_equal nil, initial_ctx - assert_equal true, imap.tls_verified? - assert_include imap.inspect, " TLS disconnected" + assert_tls_verified imap assert_equal({ca_file: CA_FILE}, imap.ssl_ctx_params) rescue SystemCallError skip $! @@ -167,8 +191,7 @@ def test_starttls_stripping_not_ok imap.disconnect if imap && !imap.disconnected? end - assert_equal false, imap.tls_verified? - assert_include imap.inspect, " PLAINTEXT (TLS NOT STARTED) " + assert_tls_unstarted imap assert_equal({ca_file: CA_FILE}, imap.ssl_ctx_params) assert_equal(CA_FILE, imap.ssl_ctx.ca_file) assert_equal(OpenSSL::SSL::VERIFY_PEER, imap.ssl_ctx.verify_mode) @@ -214,8 +237,7 @@ def test_starttls_stripping_ok_sent_before_response ensure imap.disconnect if imap && !imap.disconnected? end - assert_equal false, imap.tls_verified? - assert_include imap.inspect, " PLAINTEXT (TLS NOT STARTED) " + assert_tls_unstarted imap assert_equal({ca_file: CA_FILE}, imap.ssl_ctx_params) assert_equal(CA_FILE, imap.ssl_ctx.ca_file) assert_equal(OpenSSL::SSL::VERIFY_PEER, imap.ssl_ctx.verify_mode) From 6fe079675bc18ad7fa9377aae4ca89235ff69217 Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 9 Sep 2026 11:09:33 -0400 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=90=9B=20Fix=20#inspect=20when=20TLS?= =?UTF-8?q?=20connect=20fails?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TLS socket's session is not necessarily `nil` in this scenario. Since `connect` failed, `#inspect` should report "NOT ESTABLISHED". --- lib/net/imap.rb | 5 +++-- test/net/imap/test_imap_tls.rb | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 7b82b48c..7202fe7a 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -1240,7 +1240,7 @@ def initialize(host, port: nil, ssl: nil, response_handlers: nil, @greeting = nil @capabilities = nil @enabled = Set.new - @tls_verified = false + @tls_connected = @tls_verified = false @connection_state = ConnectionState::NotAuthenticated.new # Client Protocol Receiver @@ -1302,7 +1302,7 @@ def inspect if tls_verified? "TLS" elsif ssl_ctx && @sock.kind_of?(OpenSSL::SSL::SSLSocket) - "TLS (#{@sock.session ? "NOT VERIFIED" : "NOT ESTABLISHED"})" + "TLS (#{@tls_connected ? "NOT VERIFIED" : "NOT ESTABLISHED"})" else "PLAINTEXT#{" (TLS NOT STARTED)" if ssl_ctx}" end @@ -4129,6 +4129,7 @@ def start_tls_session @sock.sync_close = true @sock.hostname = @host if @sock.respond_to? :hostname= ssl_socket_connect(@sock, open_timeout) + @tls_connected = true if ssl_ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE @sock.post_connection_check(@host) @tls_verified = true diff --git a/test/net/imap/test_imap_tls.rb b/test/net/imap/test_imap_tls.rb index 4f7f3eb8..b79ba930 100644 --- a/test/net/imap/test_imap_tls.rb +++ b/test/net/imap/test_imap_tls.rb @@ -135,8 +135,7 @@ def test_starttls_unknown_ca end assert_kind_of(OpenSSL::SSL::SSLError, ex) assert_local_backtrace ex - pend "Fix TLSSocket#connect detection" do assert_tls_incomplete imap end - assert_tls_unverified imap # TODO: should be incomplete + assert_tls_incomplete imap assert_equal({}, imap.ssl_ctx_params) assert_equal(nil, imap.ssl_ctx.ca_file) assert_equal(OpenSSL::SSL::VERIFY_PEER, imap.ssl_ctx.verify_mode)