87 lines
3.1 KiB
Diff
87 lines
3.1 KiB
Diff
From 83dbd3f615bd8e7a31571124059eb3264f73c559 Mon Sep 17 00:00:00 2001
|
|
From: Jean Boussier <jean.boussier@gmail.com>
|
|
Date: Wed, 18 Mar 2026 08:56:17 +0100
|
|
Subject: [PATCH] Fix a format string injection vulnerability
|
|
|
|
In `JSON.parse(doc, allow_duplicate_key: false)`.
|
|
---
|
|
ext/json/parser/parser.c | 26 +++++++++++++++++++-------
|
|
test/json/json_parser_test.rb | 7 +++++++
|
|
2 files changed, 26 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
|
|
index 8f9729ef28..a05c5e9657 100644
|
|
--- a/ext/json/parser/parser.c
|
|
+++ b/ext/json/parser/parser.c
|
|
@@ -400,14 +400,9 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
|
|
|
|
#define PARSE_ERROR_FRAGMENT_LEN 32
|
|
|
|
-#ifdef RBIMPL_ATTR_NORETURN
|
|
-RBIMPL_ATTR_NORETURN()
|
|
-#endif
|
|
-static void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
+static VALUE build_parse_error_message(const char *format, JSON_ParserState *state, long line, long column)
|
|
{
|
|
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
|
|
- long line, column;
|
|
- cursor_position(state, &line, &column);
|
|
|
|
const char *ptr = "EOF";
|
|
if (state->cursor && state->cursor < state->end) {
|
|
@@ -442,11 +437,23 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
VALUE msg = rb_sprintf(format, ptr);
|
|
VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column);
|
|
RB_GC_GUARD(msg);
|
|
+ return message;
|
|
+}
|
|
|
|
+static VALUE parse_error_new(VALUE message, long line, long column)
|
|
+{
|
|
VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message);
|
|
rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line));
|
|
rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column));
|
|
- rb_exc_raise(exc);
|
|
+ return exc;
|
|
+}
|
|
+
|
|
+NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state)
|
|
+{
|
|
+ long line, column;
|
|
+ cursor_position(state, &line, &column);
|
|
+ VALUE message = build_parse_error_message(format, state, line, column);
|
|
+ rb_exc_raise(parse_error_new(message, line, column));
|
|
}
|
|
|
|
#ifdef RBIMPL_ATTR_NORETURN
|
|
@@ -896,6 +903,11 @@ static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_k
|
|
rb_inspect(duplicate_key)
|
|
);
|
|
|
|
+ long line, column;
|
|
+ cursor_position(state, &line, &column);
|
|
+ rb_str_concat(message, build_parse_error_message("", state, line, column)) ;
|
|
+ rb_exc_raise(parse_error_new(message, line, column));
|
|
+
|
|
raise_parse_error(RSTRING_PTR(message), state);
|
|
RB_GC_GUARD(message);
|
|
}
|
|
diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb
|
|
index ec9391909d..61ea35d1f9 100644
|
|
--- a/test/json/json_parser_test.rb
|
|
+++ b/test/json/json_parser_test.rb
|
|
@@ -411,6 +411,13 @@ def test_parse_duplicate_key
|
|
end
|
|
end
|
|
|
|
+ def test_parse_duplicate_key_escape
|
|
+ error = assert_raise(ParserError) do
|
|
+ JSON.parse('{"%s%s%s%s":1,"%s%s%s%s":2}', allow_duplicate_key: false)
|
|
+ end
|
|
+ assert_match "%s%s%s%s", error.message
|
|
+ end
|
|
+
|
|
def test_some_wrong_inputs
|
|
assert_raise(ParserError) { parse('[] bla') }
|
|
assert_raise(ParserError) { parse('[] 1') }
|