json 2.17.1.2 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a9a3645975d3e5df1aee69354bb7cefe81940009e677758de0f52617e82e871
4
- data.tar.gz: 4dfdd1d03081d3a714798c7f9ac89d58285b9d6f7aa82edd430d6118120faad2
3
+ metadata.gz: 51eab66896e862b679d424133f11e1367d5d8e71add943e67cf0673d0d562fcd
4
+ data.tar.gz: 7b69d4a42137897fe9a45bd60a21b759d133c112cb4ba16020099f27074ac2fd
5
5
  SHA512:
6
- metadata.gz: 85cce8ba1edd888d298eebf4279c3381dbbe103b48f59a998db05026028b388317c39fc3c306d32f3e0ada010f0cdff42c978c1173b0af90c93e42d16e78246f
7
- data.tar.gz: 8dd8ecf39713290c9fbaf0d1d574636e7c8392252edd7accd4fb2d0bf679f7eeb89efdd099e3d20a57df91ff96bf7bf0b30338d71a22f7cf666799713a3dd3e3
6
+ metadata.gz: ea3b026c8ccd6cb477858bf06f07f8b5adc5bcf7b52a175487c19dc2835ef63db2e4f87074a00ec2fe2c70e588c205f679116536da40f15e767f35351a52fc5c
7
+ data.tar.gz: f58144a5329ad95128e00bbc5670280f6a699e04cf05060bdfc7acaf112e62eb44c14c36328a9683d86e138c6eb2f3599f5ec35347867ac99ab9f0e16813c4df
data/CHANGES.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
- ### 2026-03-18 (2.17.1.2)
5
+ ### 2025-12-11 (2.18.0)
6
6
 
7
- * Fix a format string injection vulnerability in JSON.parse(doc, allow_duplicate_key: false).
7
+ * Add `:allow_control_characters` parser options, to allow JSON strings containing unescaped ASCII control characters (e.g. newlines).
8
8
 
9
9
  ### 2025-12-04 (2.17.1)
10
10
 
@@ -7,7 +7,7 @@ static VALUE CNaN, CInfinity, CMinusInfinity;
7
7
 
8
8
  static ID i_new, i_try_convert, i_uminus, i_encode;
9
9
 
10
- static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_symbolize_names, sym_freeze,
10
+ static VALUE sym_max_nesting, sym_allow_nan, sym_allow_trailing_comma, sym_allow_control_characters, sym_symbolize_names, sym_freeze,
11
11
  sym_decimal_class, sym_on_load, sym_allow_duplicate_key;
12
12
 
13
13
  static int binary_encindex;
@@ -335,6 +335,7 @@ typedef struct JSON_ParserStruct {
335
335
  int max_nesting;
336
336
  bool allow_nan;
337
337
  bool allow_trailing_comma;
338
+ bool allow_control_characters;
338
339
  bool symbolize_names;
339
340
  bool freeze;
340
341
  } JSON_ParserConfig;
@@ -399,9 +400,14 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
399
400
 
400
401
  #define PARSE_ERROR_FRAGMENT_LEN 32
401
402
 
402
- static VALUE build_parse_error_message(const char *format, JSON_ParserState *state, long line, long column)
403
+ #ifdef RBIMPL_ATTR_NORETURN
404
+ RBIMPL_ATTR_NORETURN()
405
+ #endif
406
+ static void raise_parse_error(const char *format, JSON_ParserState *state)
403
407
  {
404
408
  unsigned char buffer[PARSE_ERROR_FRAGMENT_LEN + 3];
409
+ long line, column;
410
+ cursor_position(state, &line, &column);
405
411
 
406
412
  const char *ptr = "EOF";
407
413
  if (state->cursor && state->cursor < state->end) {
@@ -436,23 +442,11 @@ static VALUE build_parse_error_message(const char *format, JSON_ParserState *sta
436
442
  VALUE msg = rb_sprintf(format, ptr);
437
443
  VALUE message = rb_enc_sprintf(enc_utf8, "%s at line %ld column %ld", RSTRING_PTR(msg), line, column);
438
444
  RB_GC_GUARD(msg);
439
- return message;
440
- }
441
445
 
442
- static VALUE parse_error_new(VALUE message, long line, long column)
443
- {
444
446
  VALUE exc = rb_exc_new_str(rb_path2class("JSON::ParserError"), message);
445
447
  rb_ivar_set(exc, rb_intern("@line"), LONG2NUM(line));
446
448
  rb_ivar_set(exc, rb_intern("@column"), LONG2NUM(column));
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));
449
+ rb_exc_raise(exc);
456
450
  }
457
451
 
458
452
  #ifdef RBIMPL_ATTR_NORETURN
@@ -759,9 +753,15 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
759
753
  break;
760
754
  default:
761
755
  if ((unsigned char)*pe < 0x20) {
762
- raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
756
+ if (!config->allow_control_characters) {
757
+ if (*pe == '\n') {
758
+ raise_parse_error_at("Invalid unescaped newline character (\\n) in string: %s", state, pe - 1);
759
+ }
760
+ raise_parse_error_at("invalid ASCII control character in string: %s", state, pe - 1);
761
+ }
762
+ } else {
763
+ raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
763
764
  }
764
- raise_parse_error_at("invalid escape character in string: %s", state, pe - 1);
765
765
  break;
766
766
  }
767
767
  }
@@ -896,11 +896,6 @@ static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_k
896
896
  rb_inspect(duplicate_key)
897
897
  );
898
898
 
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
-
904
899
  raise_parse_error(RSTRING_PTR(message), state);
905
900
  RB_GC_GUARD(message);
906
901
  }
@@ -1018,7 +1013,9 @@ static VALUE json_parse_escaped_string(JSON_ParserState *state, JSON_ParserConfi
1018
1013
  break;
1019
1014
  }
1020
1015
  default:
1021
- raise_parse_error("invalid ASCII control character in string: %s", state);
1016
+ if (!config->allow_control_characters) {
1017
+ raise_parse_error("invalid ASCII control character in string: %s", state);
1018
+ }
1022
1019
  break;
1023
1020
  }
1024
1021
 
@@ -1439,14 +1436,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1439
1436
  {
1440
1437
  JSON_ParserConfig *config = (JSON_ParserConfig *)data;
1441
1438
 
1442
- if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
1443
- else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
1444
- else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
1445
- else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
1446
- else if (key == sym_freeze) { config->freeze = RTEST(val); }
1447
- else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
1448
- else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1449
- else if (key == sym_decimal_class) {
1439
+ if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
1440
+ else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
1441
+ else if (key == sym_allow_trailing_comma) { config->allow_trailing_comma = RTEST(val); }
1442
+ else if (key == sym_allow_control_characters) { config->allow_control_characters = RTEST(val); }
1443
+ else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
1444
+ else if (key == sym_freeze) { config->freeze = RTEST(val); }
1445
+ else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
1446
+ else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1447
+ else if (key == sym_decimal_class) {
1450
1448
  if (RTEST(val)) {
1451
1449
  if (rb_respond_to(val, i_try_convert)) {
1452
1450
  config->decimal_class = val;
@@ -1659,6 +1657,7 @@ void Init_parser(void)
1659
1657
  sym_max_nesting = ID2SYM(rb_intern("max_nesting"));
1660
1658
  sym_allow_nan = ID2SYM(rb_intern("allow_nan"));
1661
1659
  sym_allow_trailing_comma = ID2SYM(rb_intern("allow_trailing_comma"));
1660
+ sym_allow_control_characters = ID2SYM(rb_intern("allow_control_characters"));
1662
1661
  sym_symbolize_names = ID2SYM(rb_intern("symbolize_names"));
1663
1662
  sym_freeze = ID2SYM(rb_intern("freeze"));
1664
1663
  sym_on_load = ID2SYM(rb_intern("on_load"));
@@ -500,11 +500,6 @@ module JSON
500
500
 
501
501
  private
502
502
 
503
- def json_shift(state)
504
- state.object_nl.empty? or return ''
505
- state.indent * state.depth
506
- end
507
-
508
503
  def json_transform(state)
509
504
  depth = state.depth += 1
510
505
 
data/lib/json/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JSON
4
- VERSION = '2.17.1.2'
4
+ VERSION = '2.18.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.1.2
4
+ version: 2.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -84,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  - !ruby/object:Gem::Version
85
85
  version: '0'
86
86
  requirements: []
87
- rubygems_version: 4.0.3
87
+ rubygems_version: 3.6.9
88
88
  specification_version: 4
89
89
  summary: JSON Implementation for Ruby
90
90
  test_files: []