@@ -1412,9 +1412,11 @@ def logout!
14121412 #
14131413 def starttls ( **options )
14141414 @ssl_ctx_params , @ssl_ctx = build_ssl_ctx ( options )
1415+ handled = false
14151416 error = nil
14161417 ok = send_command ( "STARTTLS" ) do |resp |
14171418 if resp . kind_of? ( TaggedResponse ) && resp . name == "OK"
1419+ handled = true
14181420 clear_cached_capabilities
14191421 clear_responses
14201422 start_tls_session
@@ -1426,6 +1428,13 @@ def starttls(**options)
14261428 disconnect
14271429 raise error
14281430 end
1431+ unless handled
1432+ disconnect
1433+ raise InvalidResponseError ,
1434+ "STARTTLS handler was bypassed, although server responded %p" % [
1435+ ok . raw_data . chomp
1436+ ]
1437+ end
14291438 ok
14301439 end
14311440
@@ -3132,6 +3141,7 @@ def idle(timeout = nil, &response_handler)
31323141
31333142 synchronize do
31343143 tag = Thread . current [ :net_imap_tag ] = generate_tag
3144+ guard_against_tagged_response_skipping_handler! ( tag , "IDLE" )
31353145 put_string ( "#{ tag } IDLE#{ CRLF } " )
31363146
31373147 begin
@@ -3596,6 +3606,7 @@ def send_command(cmd, *args, &block)
35963606 put_string ( " " )
35973607 send_data ( i , tag )
35983608 end
3609+ guard_against_tagged_response_skipping_handler! ( tag , cmd )
35993610 put_string ( CRLF )
36003611 if cmd == "LOGOUT"
36013612 @logout_command_tag = tag
@@ -3611,6 +3622,19 @@ def send_command(cmd, *args, &block)
36113622 end
36123623 end
36133624 end
3625+ rescue InvalidResponseError
3626+ disconnect
3627+ raise
3628+ end
3629+
3630+ def guard_against_tagged_response_skipping_handler! ( tag , cmd )
3631+ return unless ( resp = @tagged_responses [ tag ] ) &.name &.upcase == "OK"
3632+ raise InvalidResponseError , format (
3633+ "Received tagged 'OK' to incomplete %s command (tag=%s). " \
3634+ "This could indicate a malicious server, a man-in-the-middle, or " \
3635+ "client-side command injection. Disconnecting." ,
3636+ cmd , tag
3637+ )
36143638 end
36153639
36163640 def generate_tag
0 commit comments