json 2.19.6 → 2.19.8
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 +4 -4
- data/CHANGES.md +12 -0
- data/README.md +11 -0
- data/ext/json/ext/generator/generator.c +12 -7
- data/ext/json/ext/parser/parser.c +42 -16
- data/lib/json/truffle_ruby/generator.rb +3 -0
- data/lib/json/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f07e2772537eccf97069c6a7d7290aceba9a18c6b635e8e822bc7b92137c678
|
|
4
|
+
data.tar.gz: cbb25a9d0e3434eba5d9fa2be9af5a69e1fe062c5113c461d854d129122adeed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2783e5483b100728ae73451008d3d5f880cc6e9dc663d773ed9dcdf27c23c3523c345f7ca70ae0e3687ab3b11262b380a7ecf455906d72b996f07eedbb1b14f8
|
|
7
|
+
data.tar.gz: 84ab5ff3feb2d961b4bd5d759d444b006e8ccaa6802465203ed04a8718db43b4473859fdd613c106b0cc53d4e96d1da8b4a16550361f455964a4a1a75ae3cb23
|
data/CHANGES.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
### Unreleased
|
|
4
4
|
|
|
5
|
+
### 2026-06-03 (2.19.8)
|
|
6
|
+
|
|
7
|
+
* Fix 1-byte buffer overread on EOS errors.
|
|
8
|
+
* Handle invalid types passed as `max_nesting` option.
|
|
9
|
+
|
|
10
|
+
### 2026-05-28 (2.19.7)
|
|
11
|
+
|
|
12
|
+
* Fix some more edge cases with out of range floats.
|
|
13
|
+
* Ensure the string provided to `JSON.parse` can't be mutated during parsing.
|
|
14
|
+
* Add missing write barriers in `State#dup`.
|
|
15
|
+
* Further validate generator `depth` config.
|
|
16
|
+
|
|
5
17
|
### 2026-05-28 (2.19.6)
|
|
6
18
|
|
|
7
19
|
* Cleanly handle overly large `depth` generator argument.
|
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
|
|
@@ -1367,12 +1367,14 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
|
|
|
1367
1367
|
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
|
|
1368
1368
|
|
|
1369
1369
|
MEMCPY(objState, origState, JSON_Generator_State, 1);
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1370
|
+
|
|
1371
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->indent);
|
|
1372
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space);
|
|
1373
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->space_before);
|
|
1374
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->object_nl);
|
|
1375
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->array_nl);
|
|
1376
|
+
RB_OBJ_WRITTEN(obj, Qundef, objState->as_json);
|
|
1377
|
+
|
|
1376
1378
|
return obj;
|
|
1377
1379
|
}
|
|
1378
1380
|
|
|
@@ -1579,7 +1581,7 @@ static VALUE cState_max_nesting(VALUE self)
|
|
|
1579
1581
|
|
|
1580
1582
|
static long long_config(VALUE num)
|
|
1581
1583
|
{
|
|
1582
|
-
return RTEST(num) ?
|
|
1584
|
+
return RTEST(num) ? NUM2LONG(num) : 0;
|
|
1583
1585
|
}
|
|
1584
1586
|
|
|
1585
1587
|
// depth must never be negative; reject early with a clear error.
|
|
@@ -1590,6 +1592,9 @@ static long depth_config(VALUE num)
|
|
|
1590
1592
|
if (RB_UNLIKELY(d < 0)) {
|
|
1591
1593
|
rb_raise(rb_eArgError, "depth must be >= 0 (got %ld)", d);
|
|
1592
1594
|
}
|
|
1595
|
+
if (RB_UNLIKELY(d > INT_MAX)) {
|
|
1596
|
+
rb_raise(rb_eArgError, "depth is too large (got %ld)", d);
|
|
1597
|
+
}
|
|
1593
1598
|
return d;
|
|
1594
1599
|
}
|
|
1595
1600
|
|
|
@@ -385,6 +385,13 @@ static inline char peek(JSON_ParserState *state)
|
|
|
385
385
|
|
|
386
386
|
static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out)
|
|
387
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
|
+
|
|
388
395
|
const char *cursor = state->cursor;
|
|
389
396
|
long column = 0;
|
|
390
397
|
long line = 1;
|
|
@@ -1022,6 +1029,13 @@ ALWAYS_INLINE(static) bool string_scan(JSON_ParserState *state)
|
|
|
1022
1029
|
}
|
|
1023
1030
|
state->cursor++;
|
|
1024
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
|
+
|
|
1025
1039
|
return false;
|
|
1026
1040
|
}
|
|
1027
1041
|
|
|
@@ -1202,7 +1216,11 @@ static inline VALUE json_parse_number(JSON_ParserState *state, JSON_ParserConfig
|
|
|
1202
1216
|
raise_parse_error_at("invalid number: %s", state, start);
|
|
1203
1217
|
}
|
|
1204
1218
|
|
|
1205
|
-
|
|
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
|
+
}
|
|
1206
1224
|
}
|
|
1207
1225
|
|
|
1208
1226
|
if (integer) {
|
|
@@ -1457,18 +1475,21 @@ static void json_ensure_eof(JSON_ParserState *state)
|
|
|
1457
1475
|
|
|
1458
1476
|
static VALUE convert_encoding(VALUE source)
|
|
1459
1477
|
{
|
|
1460
|
-
|
|
1478
|
+
StringValue(source);
|
|
1479
|
+
int encindex = RB_ENCODING_GET(source);
|
|
1461
1480
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1481
|
+
if (RB_LIKELY(encindex == utf8_encindex)) {
|
|
1482
|
+
return source;
|
|
1483
|
+
}
|
|
1465
1484
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
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
|
+
}
|
|
1470
1489
|
|
|
1471
|
-
|
|
1490
|
+
source = rb_funcall(source, i_encode, 1, Encoding_UTF_8);
|
|
1491
|
+
StringValue(source);
|
|
1492
|
+
return source;
|
|
1472
1493
|
}
|
|
1473
1494
|
|
|
1474
1495
|
struct parser_config_init_args {
|
|
@@ -1583,10 +1604,16 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
|
|
|
1583
1604
|
return self;
|
|
1584
1605
|
}
|
|
1585
1606
|
|
|
1586
|
-
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE
|
|
1607
|
+
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src)
|
|
1587
1608
|
{
|
|
1588
|
-
Vsource = convert_encoding(
|
|
1589
|
-
|
|
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
|
+
}
|
|
1590
1617
|
|
|
1591
1618
|
VALUE rvalue_stack_buffer[RVALUE_STACK_INITIAL_CAPA];
|
|
1592
1619
|
rvalue_stack stack = {
|
|
@@ -1597,6 +1624,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
|
|
1597
1624
|
|
|
1598
1625
|
long len;
|
|
1599
1626
|
const char *start;
|
|
1627
|
+
|
|
1600
1628
|
RSTRING_GETMEM(Vsource, start, len);
|
|
1601
1629
|
|
|
1602
1630
|
VALUE stack_handle = 0;
|
|
@@ -1615,6 +1643,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
|
|
1615
1643
|
// it won't cause a leak.
|
|
1616
1644
|
rvalue_stack_eagerly_release(stack_handle);
|
|
1617
1645
|
RB_GC_GUARD(stack_handle);
|
|
1646
|
+
RB_GC_GUARD(Vsource);
|
|
1618
1647
|
json_ensure_eof(state);
|
|
1619
1648
|
|
|
1620
1649
|
return result;
|
|
@@ -1635,9 +1664,6 @@ static VALUE cParserConfig_parse(VALUE self, VALUE Vsource)
|
|
|
1635
1664
|
|
|
1636
1665
|
static VALUE cParser_m_parse(VALUE klass, VALUE Vsource, VALUE opts)
|
|
1637
1666
|
{
|
|
1638
|
-
Vsource = convert_encoding(StringValue(Vsource));
|
|
1639
|
-
StringValue(Vsource);
|
|
1640
|
-
|
|
1641
1667
|
JSON_ParserConfig _config = {0};
|
|
1642
1668
|
JSON_ParserConfig *config = &_config;
|
|
1643
1669
|
parser_config_init(config, opts, false);
|
|
@@ -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
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.19.
|
|
4
|
+
version: 2.19.8
|
|
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.
|
|
87
|
+
rubygems_version: 4.0.12
|
|
88
88
|
specification_version: 4
|
|
89
89
|
summary: JSON Implementation for Ruby
|
|
90
90
|
test_files: []
|