From 33c1c54e1b21ea2578680415110da859946f095f Mon Sep 17 00:00:00 2001 From: ydah Date: Thu, 1 Oct 2026 12:24:08 +0900 Subject: [PATCH] Prevent ResumableParser from stalling on invalid object keys --- ext/json/ext/parser/parser.c | 2 +- test/json/resumable_parser_test.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 5cba2feb..39fbd213 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -1815,7 +1815,7 @@ ALWAYS_INLINE(static) bool json_parse_any(JSON_ParserState *state, JSON_ParserCo if (RB_LIKELY(peek(state) == '"')) { VALUE string = json_parse_string(state, config, true); if (UNDEF_P(string)) { - if (resumable) { + if (resumable && eos(state)) { state->cursor = start; return false; } else { diff --git a/test/json/resumable_parser_test.rb b/test/json/resumable_parser_test.rb index 2777d50c..76db73a5 100644 --- a/test/json/resumable_parser_test.rb +++ b/test/json/resumable_parser_test.rb @@ -162,6 +162,7 @@ def test_parse_byte_by_byte_object assert_resumed_parsing('{ }') assert_resumed_parsing('{"test" : true}') assert_resumed_parsing('{ "test":12, "value" : { "key": 42} }') + assert_resumed_parsing('{"te\u0000st":true}') end def test_parse_byte_by_byte_string @@ -226,6 +227,18 @@ def test_nul_byte_is_a_syntax_error assert_parse_error "{\"a\":1,\x00}" # object key after ',' end + def test_nul_after_backslash_in_object_key_is_a_syntax_error + assert_parse_error "{\"key\\\x00\":1}" + assert_parse_error "{\"a\":1,\"key\\\x00\":2}" + end + + def test_nul_after_backslash_in_object_key_across_feeds + @parser << "{\"key\\" + refute @parser.parse + @parser << "\x00\":1}" + assert_raise(JSON::ParserError) { @parser.parse } + end + def test_incomplete_input_at_structural_positions_resumes # Counterpart of test_nul_byte_is_a_syntax_error: a genuine EOS at the same positions must # stay incomplete (return false), not raise -- this is what distinguishes EOS from a NUL.