From 2ae0bef09cb9926702711a96a46ba9c6b4b81a1b Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 13 Nov 2025 08:45:43 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=8D=20Simplify=20Net::IMAP#inspect=20w?= =?UTF-8?q?ith=20basic=20state?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most importantly, this no longer prints out *every* instance variable, some of which could contain sensitive data, and others are just _very_ verbose. Now `Net::IMAP#inspect` only prints the host:port, TLS state, and IMAP connection state. It will be extended to include more in the future. --- lib/net/imap.rb | 25 +++++++++++ test/net/imap/test_imap_inspect.rb | 69 ++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 test/net/imap/test_imap_inspect.rb diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 00f1588b..841653ea 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -1120,6 +1120,31 @@ def initialize(host, port: nil, ssl: nil, response_handlers: nil, start_imap_connection end + # Returns a string representation of +self+, showing basic client state + # information. + # + # imap = Net::IMAP.new(hostname, ssl: true) + # imap.inspect #=> "#" + # + # imap.authenticate(:oauthbearer, "user", token) + # imap.inspect #=> "#" + # + # imap.select("INBOX") + # imap.inspect #=> "#" + # + # imap.logout + # imap.inspect #=> "#" + # + def inspect + tls_state = tls_verified? ? "TLS" : + ssl_ctx ? "TLS (NOT VERIFIED)" : + "PLAINTEXT" + conn_state = disconnected? ? "disconnected" : connection_state.to_sym + "#<%s:0x%08x %s:%s %s %s>" % [ + self.class.name, __id__, host, port, tls_state, conn_state + ] + end + # Returns true after the TLS negotiation has completed and the remote # hostname has been verified. Returns false when TLS has been established # but peer verification was disabled. diff --git a/test/net/imap/test_imap_inspect.rb b/test/net/imap/test_imap_inspect.rb new file mode 100644 index 00000000..97e6391f --- /dev/null +++ b/test/net/imap/test_imap_inspect.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require "net/imap" +require "test/unit" +require_relative "fake_server" + +class IMAPInspectTest < Net::IMAP::TestCase + include Net::IMAP::FakeServer::TestHelper + + def format_inspect(client, details) + "#" % [ + "%08x" % client.__id__, # NOTE: this is different from `super` + client.host, + client.port, + details, + ] + end + + test "#inspect for every connection state (plaintext)" do + with_fake_server(preauth: false) do |server, imap| + assert_equal(format_inspect(imap, "PLAINTEXT not_authenticated"), + imap.inspect) + # AUTHENTICATE, SELECT, CLOSE + imap.authenticate :plain, "test_user", "test-password" + assert_equal(format_inspect(imap, "PLAINTEXT authenticated"), + imap.inspect) + imap.select "INBOX" + assert_equal(format_inspect(imap, "PLAINTEXT selected"), + imap.inspect) + imap.close + assert_equal(format_inspect(imap, "PLAINTEXT authenticated"), + imap.inspect) + imap.logout + assert_equal(format_inspect(imap, "PLAINTEXT logout"), + imap.inspect) + imap.disconnect + assert_equal(format_inspect(imap, "PLAINTEXT disconnected"), + imap.inspect) + end + end + + test "#inspect for TLS verified" do + with_fake_server(implicit_tls: true) do |server, imap| + assert_equal(format_inspect(imap, "TLS authenticated"), + imap.inspect) + imap.logout + assert_equal(format_inspect(imap, "TLS logout"), + imap.inspect) + imap.disconnect + assert_equal(format_inspect(imap, "TLS disconnected"), + imap.inspect) + end + end + + test "#inspect for TLS unverified" do + with_fake_server(preauth: false) do |server, imap| + imap.starttls verify_mode: OpenSSL::SSL::VERIFY_NONE + assert_equal(format_inspect(imap, "TLS (NOT VERIFIED) not_authenticated"), + imap.inspect) + imap.logout + assert_equal(format_inspect(imap, "TLS (NOT VERIFIED) logout"), + imap.inspect) + imap.disconnect + assert_equal(format_inspect(imap, "TLS (NOT VERIFIED) disconnected"), + imap.inspect) + end + end + +end