From e3e8ab7e8bc0b421be9dd4d004a8d8b2e1ea8abf Mon Sep 17 00:00:00 2001 From: nagachika Date: Sat, 14 Nov 2026 21:01:45 +0900 Subject: [PATCH] script_safe: fix escaping of `"`, `\` and control characters Since 889c06a6fa, `search_script_safe_escape` returns the raw table value (`ESCAPE_LEN_1`) and leaves flushing to the escape function, but `escape_UTF8_char` still matched `case 1` and never flushed. With `script_safe: true` (without `ascii_only`), escaped characters hit no case, and the cursor skipped over both them and any pending unescaped bytes, silently dropping data: JSON.generate(["\""], script_safe: true) # => "[\"\"]" JSON.generate("a\"b\\c\nd", script_safe: true) # => "\"d\"" Match `ESCAPE_LEN_1` and flush before appending the escape, like `full_escape_UTF8_char` does. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/generator/generator.c | 4 +++- test/json/json_generator_test.rb | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 739bdaa4..03b09890 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -329,7 +329,9 @@ static inline void escape_UTF8_char(search_state *search, unsigned char ch_len) { const unsigned char ch = (unsigned char)*search->ptr; switch (ch_len) { - case 1: { + case ESCAPE_LEN_1: { + search_flush(search); + switch (ch) { case '"': fbuffer_append(search->buffer, "\\\"", 2); break; case '\\': fbuffer_append(search->buffer, "\\\\", 2); break; diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 33f3ebe8..15434c80 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -793,6 +793,14 @@ def run_script_safe_test(ascii_only) data = '\n" + json = '"\\n\\u003c/script>"' + assert_equal json, generate(data, script_safe: true, ascii_only: ascii_only) end def test_backslash