Skip to content

Commit e4a77e1

Browse files
committed
Release 2.17.1.2
1 parent e5e4fd5 commit e4a77e1

File tree

4 files changed

+31
-8
lines changed

4 files changed

+31
-8
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
### Unreleased
44

5+
### 2026-03-18 (2.17.1.2)
6+
7+
* Fix a format string injection vulnerability in JSON.parse(doc, allow_duplicate_key: false).
8+
59
### 2025-12-04 (2.17.1)
610

711
* Fix a regression in parsing of unicode surogate pairs (`\uXX\uXX`) that could cause an invalid string to be returned.

ext/json/ext/parser/parser.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -399,14 +399,9 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
399399

400400
#define PARSE_ERROR_FRAGMENT_LEN 32
401401

402-
#ifdef RBIMPL_ATTR_NORETURN
403-
RBIMPL_ATTR_NORETURN()
404-
#endif
405-
static void raise_parse_error(const char *format, JSON_ParserState *state)
402+
static VALUE build_parse_error_message(const char *format, JSON_ParserState *state, long line, long column)
406403
{
407404
unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
408-
long line, column;
409-
cursor_position(state, &line, &column);
410405

411406
const char *ptr = "EOF";
412407
if (state->cursor && state->cursor < state->end) {
@@ -441,11 +436,23 @@ static void raise_parse_error(const char *format, JSON_ParserState *state)
441436
VALUE msg = rb_sprintf(format, ptr);
442437
VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column);
443438
RB_GC_GUARD(msg);
439+
return message;
440+
}
444441

442+
static VALUE parse_error_new(VALUE message, long line, long column)
443+
{
445444
VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message);
446445
rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line));
447446
rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column));
448-
rb_exc_raise(exc);
447+
return exc;
448+
}
449+
450+
NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *state)
451+
{
452+
long line, column;
453+
cursor_position(state, &line, &column);
454+
VALUE message = build_parse_error_message(format, state, line, column);
455+
rb_exc_raise(parse_error_new(message, line, column));
449456
}
450457

451458
#ifdef RBIMPL_ATTR_NORETURN
@@ -889,6 +896,11 @@ static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_k
889896
rb_inspect(duplicate_key)
890897
);
891898

899+
long line, column;
900+
cursor_position(state, &line, &column);
901+
rb_str_concat(message, build_parse_error_message("", state, line, column)) ;
902+
rb_exc_raise(parse_error_new(message, line, column));
903+
892904
raise_parse_error(RSTRING_PTR(message), state);
893905
RB_GC_GUARD(message);
894906
}

lib/json/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module JSON
4-
VERSION = '2.17.1'
4+
VERSION = '2.17.1.2'
55
end

test/json/json_parser_test.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,13 @@ def test_parse_duplicate_key
397397
end
398398
end
399399

400+
def test_parse_duplicate_key_escape
401+
error = assert_raise(ParserError) do
402+
JSON.parse('{"%s%s%s%s":1,"%s%s%s%s":2}', allow_duplicate_key: false)
403+
end
404+
assert_match "%s%s%s%s", error.message
405+
end
406+
400407
def test_some_wrong_inputs
401408
assert_raise(ParserError) { parse('[] bla') }
402409
assert_raise(ParserError) { parse('[] 1') }

0 commit comments

Comments
 (0)