Skip to content

Commit c4a0c5a

Browse files
authored
🔀 Merge pull request #629 from Masamuneee/fix-response-recursion-error
Handle deep response recursion as ResponseParseError
2 parents cc71e9a + fed77b1 commit c4a0c5a

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

lib/net/imap/response_parser.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,8 @@ def response
691691
CRLF!
692692
EOF!
693693
resp
694+
rescue SystemStackError
695+
parse_error("response recursion too deep")
694696
end
695697

696698
# RFC3501 & RFC9051:

test/net/imap/test_response_parser.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,4 +356,64 @@ def assert_deprecated_copyuid_data_warning(check: true)
356356
assert_instance_of Net::IMAP::CopyUIDData, response.data.code.data
357357
end
358358

359+
def deeply_nested_bodystructure(depth)
360+
leaf = '("TEXT" "PLAIN" NIL NIL NIL "7BIT" 1 1)'
361+
depth.times.reduce(leaf) { |part, _| "(#{part} \"MIXED\")" }
362+
end
363+
364+
def deeply_nested_thread(depth)
365+
depth.times.reduce("(1)") { |thread, _| "(#{thread}(1))" }
366+
end
367+
368+
def deeply_nested_extension_value(depth)
369+
depth.times.reduce("1") { |value, _| "(#{value})" }
370+
end
371+
372+
def patch_recursion_entrypoint(parser, method_name)
373+
return if ENV["TEST_RESPONSE_PARSER_STACK_LEVEL"] == "original"
374+
375+
parser.define_singleton_method(method_name) do |*args, **kwargs, &block|
376+
raise SystemStackError, "stack level too deep"
377+
end
378+
end
379+
380+
test "deeply nested BODYSTRUCTURE raises ResponseParseError instead of SystemStackError" do
381+
parser = Net::IMAP::ResponseParser.new
382+
patch_recursion_entrypoint(parser, :body)
383+
response = "* 1 FETCH (BODYSTRUCTURE #{deeply_nested_bodystructure(6000)})\r\n"
384+
385+
error = assert_raise(Net::IMAP::ResponseParseError) do
386+
parser.parse(response)
387+
end
388+
389+
assert_equal "response recursion too deep", error.message
390+
assert_instance_of SystemStackError, error.cause
391+
end
392+
393+
test "deeply nested THREAD raises ResponseParseError instead of SystemStackError" do
394+
parser = Net::IMAP::ResponseParser.new
395+
patch_recursion_entrypoint(parser, :thread_data)
396+
response = "* THREAD #{deeply_nested_thread(6000)}\r\n"
397+
398+
error = assert_raise(Net::IMAP::ResponseParseError) do
399+
parser.parse(response)
400+
end
401+
402+
assert_equal "response recursion too deep", error.message
403+
assert_instance_of SystemStackError, error.cause
404+
end
405+
406+
test "deeply nested STATUS extension raises ResponseParseError instead of SystemStackError" do
407+
parser = Net::IMAP::ResponseParser.new
408+
patch_recursion_entrypoint(parser, :tagged_ext_val)
409+
response = "* STATUS INBOX (X #{deeply_nested_extension_value(11_000)})\r\n"
410+
411+
error = assert_raise(Net::IMAP::ResponseParseError) do
412+
parser.parse(response)
413+
end
414+
415+
assert_equal "response recursion too deep", error.message
416+
assert_instance_of SystemStackError, error.cause
417+
end
418+
359419
end

0 commit comments

Comments
 (0)