From 07942550cda0e9c122c7b58f421b560bda3a11c1 Mon Sep 17 00:00:00 2001 From: ydah Date: Tue, 6 Oct 2026 20:53:07 +0900 Subject: [PATCH] Reject non-ASCII BINARY strings during JSON generation --- ext/json/ext/generator/generator.c | 17 +--------------- java/src/json/ext/StringEncoder.java | 21 ++------------------ test/json/json_generator_test.rb | 29 +++++++++++++++------------- 3 files changed, 19 insertions(+), 48 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index fbef1980..45f95fb9 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -75,7 +75,7 @@ static void generate_json_bignum(FBuffer *buffer, struct generate_json_data *dat static void generate_json_float(FBuffer *buffer, struct generate_json_data *data, VALUE obj); static void generate_json_fragment(FBuffer *buffer, struct generate_json_data *data, VALUE obj); -static int usascii_encindex, utf8_encindex, binary_encindex; +static int usascii_encindex, utf8_encindex; NORETURN(static void) raise_generator_error_str(VALUE invalid_object, VALUE str) { @@ -865,20 +865,6 @@ NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, } } - if (RB_ENCODING_GET_INLINED(str) == binary_encindex) { - VALUE utf8_string = rb_enc_associate_index(rb_str_dup(str), utf8_encindex); - switch (rb_enc_str_coderange(utf8_string)) { - case ENC_CODERANGE_7BIT: - return utf8_string; - case ENC_CODERANGE_VALID: - // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. - // TODO: Raise in 3.0.0 - rb_warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0"); - return utf8_string; - break; - } - } - return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str); } @@ -2140,7 +2126,6 @@ void Init_generator(void) usascii_encindex = rb_usascii_encindex(); utf8_encindex = rb_utf8_encindex(); - binary_encindex = rb_ascii8bit_encindex(); rb_require("json/ext/generator/state"); diff --git a/java/src/json/ext/StringEncoder.java b/java/src/json/ext/StringEncoder.java index e059fc99..347635ca 100644 --- a/java/src/json/ext/StringEncoder.java +++ b/java/src/json/ext/StringEncoder.java @@ -13,7 +13,6 @@ import java.nio.charset.StandardCharsets; import org.jcodings.Encoding; -import org.jcodings.specific.ASCIIEncoding; import org.jcodings.specific.USASCIIEncoding; import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; @@ -229,28 +228,12 @@ static RubyString ensureValidEncoding(ThreadContext context, RubyString str) { return str; } - return tryWeirdEncodings(context, str, encoding); + return tryWeirdEncodings(context, str); } - private static RubyString tryWeirdEncodings(ThreadContext context, RubyString str, Encoding encoding) { + private static RubyString tryWeirdEncodings(ThreadContext context, RubyString str) { Ruby runtime = context.runtime; - RubyString utf8String; - - if (encoding == ASCIIEncoding.INSTANCE) { - utf8String = str.strDup(runtime); - utf8String.setEncoding(UTF8Encoding.INSTANCE); - switch (utf8String.getCodeRange()) { - case StringSupport.CR_7BIT: - return utf8String; - case StringSupport.CR_VALID: - // For historical reason, we silently reinterpret binary strings as UTF-8 if it would work. - // TODO: Raise in 3.0.0 - runtime.getWarnings().warn("JSON.generate: UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0"); - return utf8String; - } - } - try { str = (RubyString) str.encode(context, runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8Encoding.INSTANCE)); } catch (RaiseException re) { diff --git a/test/json/json_generator_test.rb b/test/json/json_generator_test.rb index 2fbe3f9f..80f761e7 100755 --- a/test/json/json_generator_test.rb +++ b/test/json/json_generator_test.rb @@ -1049,22 +1049,25 @@ def to_s end end - if defined?(JSON::Ext::Generator) and RUBY_PLATFORM != "java" - def test_valid_utf8_in_different_encoding - utf8_string = "€™" - wrong_encoding_string = utf8_string.b - # This behavior is historical. Not necessary desirable. We should deprecated it. - # The pure and java version of the gem already don't behave this way. - assert_warning(/UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0/) do - assert_equal utf8_string.to_json, wrong_encoding_string.to_json - end - - assert_warning(/UTF-8 string passed as BINARY, this will raise an encoding error in json 3.0/) do - assert_equal JSON.dump(utf8_string), JSON.dump(wrong_encoding_string) - end + def test_valid_utf8_in_binary_encoding + string = "€™".b.freeze + assert_raise(JSON::GeneratorError) { string.to_json } + assert_raise(JSON::GeneratorError) { JSON.dump(string) } + [string, [string], { string => 1 }, { value: string }].each do |object| + error = assert_raise(JSON::GeneratorError) { JSON.generate(object) } + assert_same string, error.invalid_object + assert_kind_of Encoding::UndefinedConversionError, error.cause end end + def test_ascii_in_binary_encoding + string = "ascii".b + assert_equal '"ascii"', string.to_json + assert_equal '"ascii"', JSON.dump(string) + assert_equal '["ascii"]', JSON.generate([string]) + assert_equal '{"ascii":1}', JSON.generate(string => 1) + end + def test_nonutf8_encoding assert_equal("\"5\u{b0}\"", "5\xb0".dup.force_encoding(Encoding::ISO_8859_1).to_json) end