json 2.19.4 → 2.19.9

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: 48a111e63867f6bb2c0cb1b2740a898f3102bc07ac196136e26ca9b68f510f89
4
- data.tar.gz: 2f760d8afae80555c483afaaa8bead26f045d26c394cc07843a85deaff7fdd40
3
+ metadata.gz: 5e5fed66aaa650ac7aaf223c4d8e505a2d6ce3fe72599af4931356ae619f020b
4
+ data.tar.gz: 347004780c7a7568685502ade79f145371a49a213d348a65c99ea8fbffc6eee7
5
5
  SHA512:
6
- metadata.gz: 6387b2c483c8f4d7ed304f33a3553a2fde78c04efeff7ae32a1e952380df92d6df87b543c22832907b007f758f09bd2f80ef4db0b5753e46fdf73f0e264e9e02
7
- data.tar.gz: c3c855b9e460768f83516914580c10854ea2b28ee73976e311ce55a0d2265576f65a23c41cccc47c7443d79883658962d1cf923880685d555b3af1ba0ebcb056
6
+ metadata.gz: 0bffdb4cd21e4656a1402a00d95ae890dc75befff379aeef4aad575a7f715a0dbeb87cfef6fefa3cd5235c3302a29fabb510d5db67e527678023cd8a88c34b49
7
+ data.tar.gz: 7e08cfae0c5d404687644528ecfb9438c9ea254f43fd285d116ba1542a4ef78045463b18850324e91ba4d0c735e7d9ed4b583499aadafe7d0bab51dd54a8d086
data/CHANGES.md CHANGED
@@ -2,9 +2,35 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ ### 2026-06-11 (2.19.9)
6
+
7
+ * Fix buffer overflow that could lead to a crash when writing JSON directly into an IO
8
+ with `JSON.generate(object, io)`. [CVE-PENDING].
9
+
10
+ ### 2026-06-03 (2.19.8)
11
+
12
+ * Fix 1-byte buffer overread on EOS errors.
13
+ * Handle invalid types passed as `max_nesting` option.
14
+
15
+ ### 2026-05-28 (2.19.7)
16
+
17
+ * Fix some more edge cases with out of range floats.
18
+ * Ensure the string provided to `JSON.parse` can't be mutated during parsing.
19
+ * Add missing write barriers in `State#dup`.
20
+ * Further validate generator `depth` config.
21
+
22
+ ### 2026-05-28 (2.19.6)
23
+
24
+ * Cleanly handle overly large `depth` generator argument.
25
+ * Add missing write barrier in `ParserConfig`.
26
+
27
+ ### 2026-05-04 (2.19.5)
28
+
29
+ * Cap the parser to emit a maximum of 5 deprecation warnings per document. Emitting more is not helpful.
30
+
5
31
  ### 2026-04-19 (2.19.4)
6
32
 
7
- * Fix parsing of out of range floats (very large exponents that lead ot either `0.0` or `Inf`).
33
+ * Fix parsing of out of range floats (very large exponents that lead to either `0.0` or `Inf`).
8
34
 
9
35
  ### 2026-03-25 (2.19.3)
10
36
 
data/README.md CHANGED
@@ -249,6 +249,17 @@ There are also the methods `Kernel#j` for generate, and `Kernel#jj` for
249
249
  `pretty_generate` output to the console, that work analogous to Core Ruby's `p` and
250
250
  the `pp` library's `pp` methods.
251
251
 
252
+ ## Security
253
+
254
+ When parsing or serializing untrusted input, parser and generator options should never be user controlled.
255
+
256
+ ```ruby
257
+ # Dangerous, DO NOT DO THIS.
258
+ JSON.generate(params[:data], params[:options])
259
+ ```
260
+
261
+ Security vulnerability reports relying on attacker controlled parsing or generator options will be handled as regular bug fixes.
262
+
252
263
  ## Development
253
264
 
254
265
  ### Prerequisites
@@ -37,13 +37,17 @@ static void fbuffer_append_long(FBuffer *fb, long number);
37
37
  static inline void fbuffer_append_char(FBuffer *fb, char newchr);
38
38
  static VALUE fbuffer_finalize(FBuffer *fb);
39
39
 
40
- static void fbuffer_stack_init(FBuffer *fb, size_t initial_length, char *stack_buffer, size_t stack_buffer_size)
40
+ static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size)
41
41
  {
42
- fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
43
- if (stack_buffer) {
42
+ if (RTEST(io)) {
43
+ JSON_ASSERT(fb->type == FBUFFER_HEAP_ALLOCATED);
44
+ fb->io = io;
45
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_IO_BUFFER_SIZE;
46
+ } else {
44
47
  fb->type = FBUFFER_STACK_ALLOCATED;
45
48
  fb->ptr = stack_buffer;
46
49
  fb->capa = stack_buffer_size;
50
+ fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT;
47
51
  }
48
52
  #if JSON_DEBUG
49
53
  fb->requested = 0;
@@ -79,45 +83,40 @@ static void fbuffer_flush(FBuffer *fb)
79
83
  fbuffer_clear(fb);
80
84
  }
81
85
 
82
- static void fbuffer_realloc(FBuffer *fb, size_t required)
86
+ static void fbuffer_realloc(FBuffer *fb, size_t new_capa)
83
87
  {
84
- if (required > fb->capa) {
88
+ if (new_capa > fb->capa) {
85
89
  if (fb->type == FBUFFER_STACK_ALLOCATED) {
86
90
  const char *old_buffer = fb->ptr;
87
- fb->ptr = ALLOC_N(char, required);
91
+ fb->ptr = ALLOC_N(char, new_capa);
88
92
  fb->type = FBUFFER_HEAP_ALLOCATED;
89
93
  MEMCPY(fb->ptr, old_buffer, char, fb->len);
90
94
  } else {
91
- REALLOC_N(fb->ptr, char, required);
95
+ REALLOC_N(fb->ptr, char, new_capa);
92
96
  }
93
- fb->capa = required;
97
+ fb->capa = new_capa;
94
98
  }
95
99
  }
96
100
 
97
101
  static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested)
98
102
  {
99
103
  if (RB_UNLIKELY(fb->io)) {
100
- if (fb->capa < FBUFFER_IO_BUFFER_SIZE) {
101
- fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE);
102
- } else {
104
+ if (fb->capa != 0) {
103
105
  fbuffer_flush(fb);
104
- }
105
-
106
- if (RB_LIKELY(requested < fb->capa)) {
107
- return;
106
+ if (RB_LIKELY(requested < fb->capa)) {
107
+ return;
108
+ }
108
109
  }
109
110
  }
110
111
 
111
- size_t required;
112
+ size_t new_capa = fb->capa ? fb->capa : fb->initial_length;
113
+ size_t needed_capa = requested + fb->len;
112
114
 
113
- if (RB_UNLIKELY(!fb->ptr)) {
114
- fb->ptr = ALLOC_N(char, fb->initial_length);
115
- fb->capa = fb->initial_length;
115
+ while (new_capa < needed_capa) {
116
+ new_capa *= 2;
116
117
  }
117
118
 
118
- for (required = fb->capa; requested > required - fb->len; required <<= 1);
119
-
120
- fbuffer_realloc(fb, required);
119
+ fbuffer_realloc(fb, new_capa);
121
120
  }
122
121
 
123
122
  static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
@@ -131,6 +130,15 @@ static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested)
131
130
  }
132
131
  }
133
132
 
133
+ static inline size_t fbuffer_size_mul_or_raise(size_t a, size_t b)
134
+ {
135
+ size_t result = a * b;
136
+ if (RB_UNLIKELY(a != 0 && (result / a) != b)) {
137
+ rb_raise(rb_eArgError, "Buffer overflow, the resulting document is too large to be generated");
138
+ }
139
+ return result;
140
+ }
141
+
134
142
  static inline void fbuffer_append_reserved(FBuffer *fb, const char *newstr, size_t len)
135
143
  {
136
144
  MEMCPY(fb->ptr + fb->len, newstr, char, len);
@@ -175,7 +183,7 @@ static void fbuffer_append_str_repeat(FBuffer *fb, VALUE str, size_t repeat)
175
183
  size_t len;
176
184
  RSTRING_GETMEM(str, ptr, len);
177
185
 
178
- fbuffer_inc_capa(fb, repeat * len);
186
+ fbuffer_inc_capa(fb, fbuffer_size_mul_or_raise(repeat, len));
179
187
  while (repeat) {
180
188
  #if JSON_DEBUG
181
189
  fb->requested = len;
@@ -819,9 +819,17 @@ static VALUE encode_json_string_rescue(VALUE str, VALUE exception)
819
819
  return Qundef;
820
820
  }
821
821
 
822
+ static inline int json_str_coderange(VALUE str) {
823
+ int coderange = RB_ENC_CODERANGE(str);
824
+ if (coderange == RUBY_ENC_CODERANGE_UNKNOWN) {
825
+ coderange = rb_enc_str_coderange(str);
826
+ }
827
+ return coderange;
828
+ }
829
+
822
830
  static inline bool valid_json_string_p(VALUE str)
823
831
  {
824
- int coderange = rb_enc_str_coderange(str);
832
+ int coderange = json_str_coderange(str);
825
833
 
826
834
  if (RB_LIKELY(coderange == ENC_CODERANGE_7BIT)) {
827
835
  return true;
@@ -834,12 +842,8 @@ static inline bool valid_json_string_p(VALUE str)
834
842
  return false;
835
843
  }
836
844
 
837
- static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
845
+ NOINLINE(static) VALUE convert_invalid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
838
846
  {
839
- if (RB_LIKELY(valid_json_string_p(str))) {
840
- return str;
841
- }
842
-
843
847
  if (!as_json_called && data->state->strict && RTEST(data->state->as_json)) {
844
848
  VALUE coerced_str = json_call_as_json(data->state, str, Qfalse);
845
849
  if (coerced_str != str) {
@@ -875,6 +879,16 @@ static inline VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE
875
879
  return rb_rescue(encode_json_string_try, str, encode_json_string_rescue, str);
876
880
  }
877
881
 
882
+ ALWAYS_INLINE(static) VALUE ensure_valid_encoding(struct generate_json_data *data, VALUE str, bool as_json_called, bool is_key)
883
+ {
884
+ if (RB_LIKELY(valid_json_string_p(str))) {
885
+ return str;
886
+ }
887
+ else {
888
+ return convert_invalid_encoding(data, str, as_json_called, is_key);
889
+ }
890
+ }
891
+
878
892
  static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data *data, VALUE obj)
879
893
  {
880
894
  fbuffer_append_char(buffer, '"');
@@ -893,7 +907,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data
893
907
  search.chunk_end = NULL;
894
908
  #endif /* HAVE_SIMD */
895
909
 
896
- switch (rb_enc_str_coderange(obj)) {
910
+ switch (json_str_coderange(obj)) {
897
911
  case ENC_CODERANGE_7BIT:
898
912
  case ENC_CODERANGE_VALID:
899
913
  if (RB_UNLIKELY(data->state->ascii_only)) {
@@ -1290,10 +1304,8 @@ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_fun
1290
1304
  GET_STATE(self);
1291
1305
 
1292
1306
  char stack_buffer[FBUFFER_STACK_SIZE];
1293
- FBuffer buffer = {
1294
- .io = RTEST(io) ? io : Qfalse,
1295
- };
1296
- fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1307
+ FBuffer buffer = { 0 };
1308
+ fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
1297
1309
 
1298
1310
  struct generate_json_data data = {
1299
1311
  .buffer = &buffer,
@@ -1353,12 +1365,14 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
1353
1365
  if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
1354
1366
 
1355
1367
  MEMCPY(objState, origState, JSON_Generator_State, 1);
1356
- objState->indent = origState->indent;
1357
- objState->space = origState->space;
1358
- objState->space_before = origState->space_before;
1359
- objState->object_nl = origState->object_nl;
1360
- objState->array_nl = origState->array_nl;
1361
- objState->as_json = origState->as_json;
1368
+
1369
+ RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
1370
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space);
1371
+ RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
1372
+ RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
1373
+ RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
1374
+ RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
1375
+
1362
1376
  return obj;
1363
1377
  }
1364
1378
 
@@ -1565,7 +1579,7 @@ static VALUE cState_max_nesting(VALUE self)
1565
1579
 
1566
1580
  static long long_config(VALUE num)
1567
1581
  {
1568
- return RTEST(num) ? FIX2LONG(num) : 0;
1582
+ return RTEST(num) ? NUM2LONG(num) : 0;
1569
1583
  }
1570
1584
 
1571
1585
  // depth must never be negative; reject early with a clear error.
@@ -1576,6 +1590,9 @@ static long depth_config(VALUE num)
1576
1590
  if (RB_UNLIKELY(d < 0)) {
1577
1591
  rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
1578
1592
  }
1593
+ if (RB_UNLIKELY(d > INT_MAX)) {
1594
+ rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
1595
+ }
1579
1596
  return d;
1580
1597
  }
1581
1598
 
@@ -1847,10 +1864,8 @@ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io,
1847
1864
  configure_state(&state, Qfalse, opts);
1848
1865
 
1849
1866
  char stack_buffer[FBUFFER_STACK_SIZE];
1850
- FBuffer buffer = {
1851
- .io = RTEST(io) ? io : Qfalse,
1852
- };
1853
- fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE);
1867
+ FBuffer buffer = { 0 };
1868
+ fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE);
1854
1869
 
1855
1870
  struct generate_json_data data = {
1856
1871
  .buffer = &buffer,
@@ -364,6 +364,7 @@ typedef struct JSON_ParserStateStruct {
364
364
  rvalue_cache name_cache;
365
365
  int in_array;
366
366
  int current_nesting;
367
+ unsigned int emitted_deprecations;
367
368
  } JSON_ParserState;
368
369
 
369
370
  static inline size_t rest(JSON_ParserState *state) {
@@ -384,6 +385,13 @@ static inline char peek(JSON_ParserState *state)
384
385
 
385
386
  static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out)
386
387
  {
388
+ JSON_ASSERT(state->cursor <= state->end);
389
+
390
+ // Redundant but helpful for hardening
391
+ if (RB_UNLIKELY(state->cursor > state->end)) {
392
+ state->cursor = state->end;
393
+ }
394
+
387
395
  const char *cursor = state->cursor;
388
396
  long column = 0;
389
397
  long line = 1;
@@ -945,7 +953,12 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
945
953
  case JSON_IGNORE:
946
954
  break;
947
955
  case JSON_DEPRECATED:
948
- emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
956
+ // Only emit the first few deprecations to avoid spamming.
957
+ if (state->emitted_deprecations < 5) {
958
+ emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
959
+ state->emitted_deprecations++;
960
+ }
961
+
949
962
  break;
950
963
  case JSON_RAISE:
951
964
  raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
@@ -1016,6 +1029,13 @@ ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
1016
1029
  }
1017
1030
  state->cursor++;
1018
1031
  }
1032
+
1033
+ // If the string ended with an unterminated escape sequence, we might
1034
+ // have gone past the end.
1035
+ if (RB_UNLIKELY(state->cursor > state->end)) {
1036
+ state->cursor = state->end;
1037
+ }
1038
+
1019
1039
  return false;
1020
1040
  }
1021
1041
 
@@ -1196,7 +1216,11 @@ static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig
1196
1216
  raise_parse_error_at("invalid number: %s", state, start);
1197
1217
  }
1198
1218
 
1199
- exponent = negative_exponent ? -abs_exponent : abs_exponent;
1219
+ if (RB_UNLIKELY(exponent_digits >= 20 || abs_exponent > (uint64_t)INT64_MAX)) {
1220
+ exponent = negative_exponent ? INT64_MIN : INT64_MAX;
1221
+ } else {
1222
+ exponent = negative_exponent ? -(int64_t)abs_exponent : (int64_t)abs_exponent;
1223
+ }
1200
1224
  }
1201
1225
 
1202
1226
  if (integer) {
@@ -1451,23 +1475,39 @@ static void json_ensure_eof(JSON_ParserState *state)
1451
1475
 
1452
1476
  static VALUE convert_encoding(VALUE source)
1453
1477
  {
1454
- int encindex = RB_ENCODING_GET(source);
1478
+ StringValue(source);
1479
+ int encindex = RB_ENCODING_GET(source);
1455
1480
 
1456
- if (RB_LIKELY(encindex == utf8_encindex)) {
1481
+ if (RB_LIKELY(encindex == utf8_encindex)) {
1482
+ return source;
1483
+ }
1484
+
1485
+ if (encindex == binary_encindex) {
1486
+ // For historical reason, we silently reinterpret binary strings as UTF-8
1487
+ return rb_enc_associate_index(rb_str_dup(source), utf8_encindex);
1488
+ }
1489
+
1490
+ source = rb_funcall(source, i_encode, 1, Encoding_UTF_8);
1491
+ StringValue(source);
1457
1492
  return source;
1458
- }
1493
+ }
1459
1494
 
1460
- if (encindex == binary_encindex) {
1461
- // For historical reason, we silently reinterpret binary strings as UTF-8
1462
- return rb_enc_associate_index(rb_str_dup(source), utf8_encindex);
1463
- }
1495
+ struct parser_config_init_args {
1496
+ JSON_ParserConfig *config;
1497
+ VALUE self;
1498
+ };
1464
1499
 
1465
- return rb_funcall(source, i_encode, 1, Encoding_UTF_8);
1500
+ static void parser_config_wb_write(VALUE self, VALUE *dest, VALUE val)
1501
+ {
1502
+ *dest = val;
1503
+ if (self) RB_OBJ_WRITTEN(self, Qundef, val);
1466
1504
  }
1467
1505
 
1468
1506
  static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1469
1507
  {
1470
- JSON_ParserConfig *config = (JSON_ParserConfig *)data;
1508
+ struct parser_config_init_args *args = (struct parser_config_init_args *)data;
1509
+ JSON_ParserConfig *config = args->config;
1510
+ VALUE self = args->self;
1471
1511
 
1472
1512
  if (key == sym_max_nesting) { config->max_nesting = RTEST(val) ? FIX2INT(val) : 0; }
1473
1513
  else if (key == sym_allow_nan) { config->allow_nan = RTEST(val); }
@@ -1476,15 +1516,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1476
1516
  else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); }
1477
1517
  else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); }
1478
1518
  else if (key == sym_freeze) { config->freeze = RTEST(val); }
1479
- else if (key == sym_on_load) { config->on_load_proc = RTEST(val) ? val : Qfalse; }
1519
+ else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); }
1480
1520
  else if (key == sym_allow_duplicate_key) { config->on_duplicate_key = RTEST(val) ? JSON_IGNORE : JSON_RAISE; }
1481
1521
  else if (key == sym_decimal_class) {
1482
1522
  if (RTEST(val)) {
1483
1523
  if (rb_respond_to(val, i_try_convert)) {
1484
- config->decimal_class = val;
1524
+ parser_config_wb_write(self, &config->decimal_class, val);
1485
1525
  config->decimal_method_id = i_try_convert;
1486
1526
  } else if (rb_respond_to(val, i_new)) {
1487
- config->decimal_class = val;
1527
+ parser_config_wb_write(self, &config->decimal_class, val);
1488
1528
  config->decimal_method_id = i_new;
1489
1529
  } else if (RB_TYPE_P(val, T_CLASS)) {
1490
1530
  VALUE name = rb_class_name(val);
@@ -1493,7 +1533,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1493
1533
  if (last_colon) {
1494
1534
  const char *mod_path_end = last_colon - 1;
1495
1535
  VALUE mod_path = rb_str_substr(name, 0, mod_path_end - name_cstr);
1496
- config->decimal_class = rb_path_to_class(mod_path);
1536
+ parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path));
1497
1537
 
1498
1538
  const char *method_name_beg = last_colon + 1;
1499
1539
  long before_len = method_name_beg - name_cstr;
@@ -1501,7 +1541,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1501
1541
  VALUE method_name = rb_str_substr(name, before_len, len);
1502
1542
  config->decimal_method_id = SYM2ID(rb_str_intern(method_name));
1503
1543
  } else {
1504
- config->decimal_class = rb_mKernel;
1544
+ parser_config_wb_write(self, &config->decimal_class, rb_mKernel);
1505
1545
  config->decimal_method_id = SYM2ID(rb_str_intern(name));
1506
1546
  }
1507
1547
  }
@@ -1511,16 +1551,21 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data)
1511
1551
  return ST_CONTINUE;
1512
1552
  }
1513
1553
 
1514
- static void parser_config_init(JSON_ParserConfig *config, VALUE opts)
1554
+ static void parser_config_init(JSON_ParserConfig *config, VALUE opts, VALUE self)
1515
1555
  {
1516
1556
  config->max_nesting = 100;
1517
1557
 
1558
+ struct parser_config_init_args args = {
1559
+ .config = config,
1560
+ .self = self,
1561
+ };
1562
+
1518
1563
  if (!NIL_P(opts)) {
1519
1564
  Check_Type(opts, T_HASH);
1520
1565
  if (RHASH_SIZE(opts) > 0) {
1521
1566
  // We assume in most cases few keys are set so it's faster to go over
1522
1567
  // the provided keys than to check all possible keys.
1523
- rb_hash_foreach(opts, parser_config_init_i, (VALUE)config);
1568
+ rb_hash_foreach(opts, parser_config_init_i, (VALUE)&args);
1524
1569
  }
1525
1570
 
1526
1571
  }
@@ -1554,17 +1599,21 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
1554
1599
  rb_check_frozen(self);
1555
1600
  GET_PARSER_CONFIG;
1556
1601
 
1557
- parser_config_init(config, opts);
1558
-
1559
- RB_OBJ_WRITTEN(self, Qundef, config->decimal_class);
1602
+ parser_config_init(config, opts, self);
1560
1603
 
1561
1604
  return self;
1562
1605
  }
1563
1606
 
1564
- static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
1607
+ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
1565
1608
  {
1566
- Vsource = convert_encoding(StringValue(Vsource));
1567
- StringValue(Vsource);
1609
+ VALUE Vsource = convert_encoding(src);
1610
+
1611
+ // Ensure the string isn't mutated under us.
1612
+ // The classic API to use is `rb_str_locktmp`, but then we'd
1613
+ // need to use `rb_protect` to make sure we always unlock.
1614
+ if (Vsource == src) {
1615
+ Vsource = rb_str_new_frozen(Vsource);
1616
+ }
1568
1617
 
1569
1618
  VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA];
1570
1619
  rvalue_stack stack = {
@@ -1575,6 +1624,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
1575
1624
 
1576
1625
  long len;
1577
1626
  const char *start;
1627
+
1578
1628
  RSTRING_GETMEM(Vsource, start, len);
1579
1629
 
1580
1630
  VALUE stack_handle = 0;
@@ -1593,6 +1643,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
1593
1643
  // it won't cause a leak.
1594
1644
  rvalue_stack_eagerly_release(stack_handle);
1595
1645
  RB_GC_GUARD(stack_handle);
1646
+ RB_GC_GUARD(Vsource);
1596
1647
  json_ensure_eof(state);
1597
1648
 
1598
1649
  return result;
@@ -1613,12 +1664,9 @@ static VALUE cParserConfig_parse(VALUE self, VALUE Vsource)
1613
1664
 
1614
1665
  static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
1615
1666
  {
1616
- Vsource = convert_encoding(StringValue(Vsource));
1617
- StringValue(Vsource);
1618
-
1619
1667
  JSON_ParserConfig _config = {0};
1620
1668
  JSON_ParserConfig *config = &_config;
1621
- parser_config_init(config, opts);
1669
+ parser_config_init(config, opts, false);
1622
1670
 
1623
1671
  return cParser_parse(config, Vsource);
1624
1672
  }
@@ -307,6 +307,9 @@ module JSON
307
307
  if !opts.key?(:max_nesting) # defaults to 100
308
308
  @max_nesting = 100
309
309
  elsif opts[:max_nesting]
310
+ unless opts[:max_nesting].is_a?(Integer)
311
+ raise TypeError, ":max_nesting must be an Integer, got: #{opts[:max_nesting].class}"
312
+ end
310
313
  @max_nesting = opts[:max_nesting]
311
314
  else
312
315
  @max_nesting = 0
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.19.4'
4
+ VERSION = '2.19.9'
5
5
  end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.19.4
4
+ version: 2.19.9
5
5
  platform: ruby
6
6
  authors:
7
- - Florian Frank
7
+ - Florian Frank
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
@@ -13,78 +13,80 @@ description: This is a JSON implementation as a Ruby extension in C.
13
13
  email: flori@ping.de
14
14
  executables: []
15
15
  extensions:
16
- - ext/json/ext/generator/extconf.rb
17
- - ext/json/ext/parser/extconf.rb
16
+ - ext/json/ext/generator/extconf.rb
17
+ - ext/json/ext/parser/extconf.rb
18
18
  extra_rdoc_files:
19
- - README.md
19
+ - README.md
20
20
  files:
21
- - BSDL
22
- - CHANGES.md
23
- - COPYING
24
- - LEGAL
25
- - README.md
26
- - ext/json/ext/fbuffer/fbuffer.h
27
- - ext/json/ext/generator/extconf.rb
28
- - ext/json/ext/generator/generator.c
29
- - ext/json/ext/json.h
30
- - ext/json/ext/parser/extconf.rb
31
- - ext/json/ext/parser/parser.c
32
- - ext/json/ext/simd/conf.rb
33
- - ext/json/ext/simd/simd.h
34
- - ext/json/ext/vendor/fpconv.c
35
- - ext/json/ext/vendor/jeaiii-ltoa.h
36
- - ext/json/ext/vendor/ryu.h
37
- - json.gemspec
38
- - lib/json.rb
39
- - lib/json/add/bigdecimal.rb
40
- - lib/json/add/complex.rb
41
- - lib/json/add/core.rb
42
- - lib/json/add/date.rb
43
- - lib/json/add/date_time.rb
44
- - lib/json/add/exception.rb
45
- - lib/json/add/ostruct.rb
46
- - lib/json/add/range.rb
47
- - lib/json/add/rational.rb
48
- - lib/json/add/regexp.rb
49
- - lib/json/add/set.rb
50
- - lib/json/add/string.rb
51
- - lib/json/add/struct.rb
52
- - lib/json/add/symbol.rb
53
- - lib/json/add/time.rb
54
- - lib/json/common.rb
55
- - lib/json/ext.rb
56
- - lib/json/ext/generator/state.rb
57
- - lib/json/generic_object.rb
58
- - lib/json/truffle_ruby/generator.rb
59
- - lib/json/version.rb
60
- homepage: https://github.com/ruby/json
21
+ - BSDL
22
+ - CHANGES.md
23
+ - COPYING
24
+ - LEGAL
25
+ - README.md
26
+ - ext/json/ext/fbuffer/fbuffer.h
27
+ - ext/json/ext/generator/extconf.rb
28
+ - ext/json/ext/generator/generator.c
29
+ - ext/json/ext/json.h
30
+ - ext/json/ext/parser/extconf.rb
31
+ - ext/json/ext/parser/parser.c
32
+ - ext/json/ext/simd/conf.rb
33
+ - ext/json/ext/simd/simd.h
34
+ - ext/json/ext/vendor/fpconv.c
35
+ - ext/json/ext/vendor/jeaiii-ltoa.h
36
+ - ext/json/ext/vendor/ryu.h
37
+ - json.gemspec
38
+ - lib/json.rb
39
+ - lib/json/add/bigdecimal.rb
40
+ - lib/json/add/complex.rb
41
+ - lib/json/add/core.rb
42
+ - lib/json/add/date.rb
43
+ - lib/json/add/date_time.rb
44
+ - lib/json/add/exception.rb
45
+ - lib/json/add/ostruct.rb
46
+ - lib/json/add/range.rb
47
+ - lib/json/add/rational.rb
48
+ - lib/json/add/regexp.rb
49
+ - lib/json/add/set.rb
50
+ - lib/json/add/string.rb
51
+ - lib/json/add/struct.rb
52
+ - lib/json/add/symbol.rb
53
+ - lib/json/add/time.rb
54
+ - lib/json/common.rb
55
+ - lib/json/ext.rb
56
+ - lib/json/ext/generator/state.rb
57
+ - lib/json/generic_object.rb
58
+ - lib/json/truffle_ruby/generator.rb
59
+ - lib/json/version.rb
60
+ homepage: "https://github.com/ruby/json"
61
61
  licenses:
62
- - Ruby
62
+ - Ruby
63
63
  metadata:
64
- bug_tracker_uri: https://github.com/ruby/json/issues
65
- changelog_uri: https://github.com/ruby/json/blob/master/CHANGES.md
66
- documentation_uri: https://docs.ruby-lang.org/en/master/JSON.html
67
- homepage_uri: https://github.com/ruby/json
68
- source_code_uri: https://github.com/ruby/json
64
+ bug_tracker_uri: "https://github.com/ruby/json/issues"
65
+ changelog_uri: "https://github.com/ruby/json/blob/master/CHANGES.md"
66
+ documentation_uri: "https://docs.ruby-lang.org/en/master/JSON.html"
67
+ homepage_uri: "https://github.com/ruby/json"
68
+ source_code_uri: "https://github.com/ruby/json"
69
69
  rdoc_options:
70
- - "--title"
71
- - JSON implementation for Ruby
72
- - "--main"
73
- - README.md
70
+ - "--title"
71
+ - JSON implementation for Ruby
72
+ - "--main"
73
+ - README.md
74
74
  require_paths:
75
- - lib
75
+ - lib
76
76
  required_ruby_version: !ruby/object:Gem::Requirement
77
77
  requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: '2.7'
78
+ -
79
+ - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "2.7"
81
82
  required_rubygems_version: !ruby/object:Gem::Requirement
82
83
  requirements:
83
- - - ">="
84
- - !ruby/object:Gem::Version
85
- version: '0'
84
+ -
85
+ - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
86
88
  requirements: []
87
- rubygems_version: 4.0.6
89
+ rubygems_version: 4.1.0.dev
88
90
  specification_version: 4
89
91
  summary: JSON Implementation for Ruby
90
92
  test_files: []