oj 3.16.17 → 3.17.3
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/CHANGELOG.md +16 -0
- data/README.md +0 -16
- data/ext/oj/compat.c +3 -3
- data/ext/oj/custom.c +2 -2
- data/ext/oj/dump.c +8 -8
- data/ext/oj/fast.c +36 -16
- data/ext/oj/intern.c +1 -1
- data/ext/oj/mimic_json.c +1 -0
- data/ext/oj/oj.c +32 -1
- data/ext/oj/oj.h +38 -37
- data/ext/oj/parse.c +22 -2
- data/ext/oj/parser.c +79 -40
- data/ext/oj/rxclass.c +1 -1
- data/ext/oj/rxclass.h +1 -1
- data/ext/oj/safe.c +230 -0
- data/ext/oj/safe.h +79 -0
- data/ext/oj/sparse.c +3 -0
- data/ext/oj/usual.c +18 -5
- data/ext/oj/wab.c +1 -1
- data/lib/oj/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7458dcdf494ef6b1b283ca86d51fba0b3102ecf5cec13f43682878c01708c80
|
|
4
|
+
data.tar.gz: ab8099b8b275aa5acab45a012bbc96ef0c860e041be2c4ae7d6f7cd331da1755
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60d445fd27bbea120359c21d0c1b0ac1d4fbbd678c8fd28efe620706d382a2f0cc434645967c9bac0b6d08d1065fe99dc4b15e2623d98ca0e15a61eb591a993b
|
|
7
|
+
data.tar.gz: b7736fd3a7b27f98ef3e4df05464000849403ff33a3f82ecd6d8c51b40d0c2753d23ed14c078eb26506d504de991e35ebe68e5703225c80aab6ebbd28b7a80dc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 3.17.3 - 2026-06-04
|
|
4
|
+
|
|
5
|
+
- Fixed issue in intern.c and fast.c.
|
|
6
|
+
|
|
7
|
+
## 3.17.2 - 2026-05-27
|
|
8
|
+
|
|
9
|
+
- Fixed multiple issues related to extreme sizes.
|
|
10
|
+
|
|
11
|
+
## 3.17.1 - 2026-05-15
|
|
12
|
+
|
|
13
|
+
- Fixed "quoted string not terminated" error.
|
|
14
|
+
|
|
15
|
+
## 3.17.0 - 2026-04-19
|
|
16
|
+
|
|
17
|
+
- A "safe" parser has been added as a variation of the Oj:Parser thanks to @meinac.
|
|
18
|
+
|
|
3
19
|
## 3.16.17 - 2026-04-12
|
|
4
20
|
|
|
5
21
|
- Rails optimize for Hash and Array now overrides `as_json` for those
|
data/README.md
CHANGED
|
@@ -46,11 +46,6 @@ gem 'oj'
|
|
|
46
46
|
|
|
47
47
|
See the Quickstart sections of the [Rails](pages/Rails.md) and [json](pages/JsonGem.md) docs.
|
|
48
48
|
|
|
49
|
-
## multi_json
|
|
50
|
-
|
|
51
|
-
Code which uses [multi_json](https://github.com/intridea/multi_json)
|
|
52
|
-
will automatically prefer Oj if it is installed.
|
|
53
|
-
|
|
54
49
|
## Support
|
|
55
50
|
|
|
56
51
|
[Get supported Oj with a Tidelift Subscription.](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme) Security updates are [supported](https://tidelift.com/security).
|
|
@@ -83,17 +78,6 @@ See [{file:CHANGELOG.md}](CHANGELOG.md) and [{file:RELEASE_NOTES.md}](RELEASE_NO
|
|
|
83
78
|
|
|
84
79
|
- *RubyGems* *repo*: https://rubygems.org/gems/oj
|
|
85
80
|
|
|
86
|
-
Follow [@peterohler on Twitter](http://twitter.com/peterohler) for announcements and news about the Oj gem.
|
|
87
|
-
|
|
88
|
-
#### Performance Comparisons
|
|
89
|
-
|
|
90
|
-
- [Oj Strict Mode Performance](http://www.ohler.com/dev/oj_misc/performance_strict.html) compares Oj strict mode parser performance to other JSON parsers.
|
|
91
|
-
|
|
92
|
-
- [Oj Compat Mode Performance](http://www.ohler.com/dev/oj_misc/performance_compat.html) compares Oj compat mode parser performance to other JSON parsers.
|
|
93
|
-
|
|
94
|
-
- [Oj Object Mode Performance](http://www.ohler.com/dev/oj_misc/performance_object.html) compares Oj object mode parser performance to other marshallers.
|
|
95
|
-
|
|
96
|
-
- [Oj Callback Performance](http://www.ohler.com/dev/oj_misc/performance_callback.html) compares Oj callback parser performance to other JSON parsers.
|
|
97
81
|
|
|
98
82
|
#### Links of Interest
|
|
99
83
|
|
data/ext/oj/compat.c
CHANGED
|
@@ -27,7 +27,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
|
|
|
27
27
|
volatile VALUE rkey = oj_calc_hash_key(pi, kval);
|
|
28
28
|
|
|
29
29
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
|
30
|
-
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str,
|
|
30
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
|
31
31
|
|
|
32
32
|
if (Qnil != clas) {
|
|
33
33
|
rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
|
|
@@ -84,7 +84,7 @@ static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig
|
|
|
84
84
|
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
|
85
85
|
|
|
86
86
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
|
87
|
-
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str,
|
|
87
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
|
88
88
|
|
|
89
89
|
if (Qnil != clas) {
|
|
90
90
|
pi->stack.head->val = rb_funcall(clas, oj_json_create_id, 1, rstr);
|
|
@@ -155,7 +155,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
|
|
|
155
155
|
volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
|
|
156
156
|
|
|
157
157
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
|
158
|
-
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str,
|
|
158
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
|
159
159
|
|
|
160
160
|
if (Qnil != clas) {
|
|
161
161
|
rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
|
data/ext/oj/custom.c
CHANGED
|
@@ -915,7 +915,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
|
|
|
915
915
|
volatile VALUE rkey = oj_calc_hash_key(pi, kval);
|
|
916
916
|
|
|
917
917
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
|
918
|
-
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str,
|
|
918
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
|
919
919
|
|
|
920
920
|
if (Qnil != clas) {
|
|
921
921
|
rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
|
|
@@ -1020,7 +1020,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
|
|
|
1020
1020
|
volatile VALUE rstr = rb_utf8_str_new(str, len);
|
|
1021
1021
|
|
|
1022
1022
|
if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
|
|
1023
|
-
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str,
|
|
1023
|
+
VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
|
|
1024
1024
|
|
|
1025
1025
|
if (Qnil != clas) {
|
|
1026
1026
|
rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
|
data/ext/oj/dump.c
CHANGED
|
@@ -40,7 +40,7 @@ static size_t ascii_friendly_size(const uint8_t *str, size_t len);
|
|
|
40
40
|
static const char hex_chars[17] = "0123456789abcdef";
|
|
41
41
|
|
|
42
42
|
// JSON standard except newlines are no escaped
|
|
43
|
-
static char newline_friendly_chars[
|
|
43
|
+
static char newline_friendly_chars[257] = "\
|
|
44
44
|
66666666221622666666666666666666\
|
|
45
45
|
11211111111111111111111111111111\
|
|
46
46
|
11111111111111111111111111112111\
|
|
@@ -51,7 +51,7 @@ static char newline_friendly_chars[256] = "\
|
|
|
51
51
|
11111111111111111111111111111111";
|
|
52
52
|
|
|
53
53
|
// JSON standard
|
|
54
|
-
static char hibit_friendly_chars[
|
|
54
|
+
static char hibit_friendly_chars[257] = "\
|
|
55
55
|
66666666222622666666666666666666\
|
|
56
56
|
11211111111111111111111111111111\
|
|
57
57
|
11111111111111111111111111112111\
|
|
@@ -62,7 +62,7 @@ static char hibit_friendly_chars[256] = "\
|
|
|
62
62
|
11111111111111111111111111111111";
|
|
63
63
|
|
|
64
64
|
// JSON standard but escape forward slashes `/`
|
|
65
|
-
static char slash_friendly_chars[
|
|
65
|
+
static char slash_friendly_chars[257] = "\
|
|
66
66
|
66666666222622666666666666666666\
|
|
67
67
|
11211111111111121111111111111111\
|
|
68
68
|
11111111111111111111111111112111\
|
|
@@ -74,7 +74,7 @@ static char slash_friendly_chars[256] = "\
|
|
|
74
74
|
|
|
75
75
|
// High bit set characters are always encoded as unicode. Worse case is 3
|
|
76
76
|
// bytes per character in the output. That makes this conservative.
|
|
77
|
-
static char ascii_friendly_chars[
|
|
77
|
+
static char ascii_friendly_chars[257] = "\
|
|
78
78
|
66666666222622666666666666666666\
|
|
79
79
|
11211111111111111111111111111111\
|
|
80
80
|
11111111111111111111111111112111\
|
|
@@ -85,7 +85,7 @@ static char ascii_friendly_chars[256] = "\
|
|
|
85
85
|
33333333333333333333333333333333";
|
|
86
86
|
|
|
87
87
|
// XSS safe mode
|
|
88
|
-
static char xss_friendly_chars[
|
|
88
|
+
static char xss_friendly_chars[257] = "\
|
|
89
89
|
66666666222622666666666666666666\
|
|
90
90
|
11211161111111121111111111116161\
|
|
91
91
|
11111111111111111111111111112111\
|
|
@@ -96,7 +96,7 @@ static char xss_friendly_chars[256] = "\
|
|
|
96
96
|
33333333333333333333333333333333";
|
|
97
97
|
|
|
98
98
|
// JSON XSS combo
|
|
99
|
-
static char hixss_friendly_chars[
|
|
99
|
+
static char hixss_friendly_chars[257] = "\
|
|
100
100
|
66666666222622666666666666666666\
|
|
101
101
|
11211111111111111111111111111111\
|
|
102
102
|
11111111111111111111111111112111\
|
|
@@ -107,7 +107,7 @@ static char hixss_friendly_chars[256] = "\
|
|
|
107
107
|
11611111111111111111111111111111";
|
|
108
108
|
|
|
109
109
|
// Rails XSS combo
|
|
110
|
-
static char rails_xss_friendly_chars[
|
|
110
|
+
static char rails_xss_friendly_chars[257] = "\
|
|
111
111
|
66666666222622666666666666666666\
|
|
112
112
|
11211161111111111111111111116161\
|
|
113
113
|
11111111111111111111111111112111\
|
|
@@ -118,7 +118,7 @@ static char rails_xss_friendly_chars[256] = "\
|
|
|
118
118
|
11611111111111111111111111111111";
|
|
119
119
|
|
|
120
120
|
// Rails HTML non-escape
|
|
121
|
-
static char rails_friendly_chars[
|
|
121
|
+
static char rails_friendly_chars[257] = "\
|
|
122
122
|
66666666222622666666666666666666\
|
|
123
123
|
11211111111111111111111111111111\
|
|
124
124
|
11111111111111111111111111112111\
|
data/ext/oj/fast.c
CHANGED
|
@@ -80,21 +80,10 @@ static void each_leaf(Doc doc, VALUE self);
|
|
|
80
80
|
static int move_step(Doc doc, const char *path, int loc);
|
|
81
81
|
static Leaf get_doc_leaf(Doc doc, const char *path);
|
|
82
82
|
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
|
|
83
|
-
static void each_value(Doc doc, Leaf leaf);
|
|
83
|
+
static void each_value(Doc doc, Leaf leaf, VALUE self);
|
|
84
84
|
|
|
85
85
|
VALUE oj_doc_class = Qundef;
|
|
86
86
|
|
|
87
|
-
// This is only for CentOS 5.4 with Ruby 1.9.3-p0.
|
|
88
|
-
#ifndef HAVE_STPCPY
|
|
89
|
-
char *stpcpy(char *dest, const char *src) {
|
|
90
|
-
size_t cnt = strlen(src);
|
|
91
|
-
|
|
92
|
-
strcpy(dest, src);
|
|
93
|
-
|
|
94
|
-
return dest + cnt;
|
|
95
|
-
}
|
|
96
|
-
#endif
|
|
97
|
-
|
|
98
87
|
inline static void next_non_white(ParseInfo pi) {
|
|
99
88
|
for (; 1; pi->s++) {
|
|
100
89
|
switch (*pi->s) {
|
|
@@ -246,6 +235,19 @@ static void skip_comment(ParseInfo pi) {
|
|
|
246
235
|
#define NUM_MAX (FIXNUM_MAX >> 8)
|
|
247
236
|
#endif
|
|
248
237
|
|
|
238
|
+
static void validate_integer_size(size_t limit, char *head, char *tail) {
|
|
239
|
+
size_t total = (size_t)(tail - head);
|
|
240
|
+
bool has_sign = (head[0] == '-' || head[0] == '+');
|
|
241
|
+
size_t digit_count = total - (has_sign ? 1 : 0);
|
|
242
|
+
|
|
243
|
+
if (digit_count > limit) {
|
|
244
|
+
rb_raise(oj_parse_error_class,
|
|
245
|
+
"integer exceeds :max_integer_digits (%lu > %lu)",
|
|
246
|
+
(unsigned long)digit_count,
|
|
247
|
+
(unsigned long)limit);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
249
251
|
static void leaf_fixnum_value(Leaf leaf) {
|
|
250
252
|
char *s = leaf->str;
|
|
251
253
|
int64_t n = 0;
|
|
@@ -265,7 +267,12 @@ static void leaf_fixnum_value(Leaf leaf) {
|
|
|
265
267
|
}
|
|
266
268
|
}
|
|
267
269
|
if (big) {
|
|
268
|
-
|
|
270
|
+
size_t limit = oj_default_options.max_integer_digits;
|
|
271
|
+
char c = *s;
|
|
272
|
+
|
|
273
|
+
if (0 < limit) {
|
|
274
|
+
validate_integer_size(limit, leaf->str, s);
|
|
275
|
+
}
|
|
269
276
|
|
|
270
277
|
*s = '\0';
|
|
271
278
|
leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
|
|
@@ -950,6 +957,9 @@ static void each_leaf(Doc doc, VALUE self) {
|
|
|
950
957
|
}
|
|
951
958
|
} else {
|
|
952
959
|
rb_yield(self);
|
|
960
|
+
if (NULL == DATA_PTR(self)) {
|
|
961
|
+
rb_raise(rb_eIOError, "Document closed.");
|
|
962
|
+
}
|
|
953
963
|
}
|
|
954
964
|
}
|
|
955
965
|
|
|
@@ -1043,19 +1053,22 @@ static int move_step(Doc doc, const char *path, int loc) {
|
|
|
1043
1053
|
return loc;
|
|
1044
1054
|
}
|
|
1045
1055
|
|
|
1046
|
-
static void each_value(Doc doc, Leaf leaf) {
|
|
1056
|
+
static void each_value(Doc doc, Leaf leaf, VALUE self) {
|
|
1047
1057
|
if (COL_VAL == leaf->value_type) {
|
|
1048
1058
|
if (0 != leaf->elements) {
|
|
1049
1059
|
Leaf first = leaf->elements->next;
|
|
1050
1060
|
Leaf e = first;
|
|
1051
1061
|
|
|
1052
1062
|
do {
|
|
1053
|
-
each_value(doc, e);
|
|
1063
|
+
each_value(doc, e, self);
|
|
1054
1064
|
e = e->next;
|
|
1055
1065
|
} while (e != first);
|
|
1056
1066
|
}
|
|
1057
1067
|
} else {
|
|
1058
1068
|
rb_yield(leaf_value(doc, leaf));
|
|
1069
|
+
if (NULL == DATA_PTR(self)) {
|
|
1070
|
+
rb_raise(rb_eIOError, "Document closed.");
|
|
1071
|
+
}
|
|
1059
1072
|
}
|
|
1060
1073
|
}
|
|
1061
1074
|
|
|
@@ -1491,12 +1504,19 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
|
1491
1504
|
Leaf first = (*doc->where)->elements->next;
|
|
1492
1505
|
Leaf e = first;
|
|
1493
1506
|
|
|
1507
|
+
if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
|
|
1508
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
1509
|
+
}
|
|
1494
1510
|
doc->where++;
|
|
1495
1511
|
do {
|
|
1496
1512
|
*doc->where = e;
|
|
1497
1513
|
rb_yield(self);
|
|
1514
|
+
if (NULL == DATA_PTR(self)) {
|
|
1515
|
+
rb_raise(rb_eIOError, "Document closed.");
|
|
1516
|
+
}
|
|
1498
1517
|
e = e->next;
|
|
1499
1518
|
} while (e != first);
|
|
1519
|
+
doc->where--;
|
|
1500
1520
|
}
|
|
1501
1521
|
if (0 < wlen) {
|
|
1502
1522
|
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
|
@@ -1540,7 +1560,7 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
|
1540
1560
|
path = StringValuePtr(*argv);
|
|
1541
1561
|
}
|
|
1542
1562
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1543
|
-
each_value(doc, leaf);
|
|
1563
|
+
each_value(doc, leaf, self);
|
|
1544
1564
|
}
|
|
1545
1565
|
}
|
|
1546
1566
|
return Qnil;
|
data/ext/oj/intern.c
CHANGED
data/ext/oj/mimic_json.c
CHANGED
data/ext/oj/oj.c
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
#include "rails.h"
|
|
21
21
|
#include "simd.h"
|
|
22
22
|
|
|
23
|
+
#define MAX_INDENT 16
|
|
24
|
+
|
|
23
25
|
typedef struct _yesNoOpt {
|
|
24
26
|
VALUE sym;
|
|
25
27
|
char *attr;
|
|
@@ -122,6 +124,7 @@ static VALUE empty_string_sym;
|
|
|
122
124
|
static VALUE escape_mode_sym;
|
|
123
125
|
static VALUE except_sym;
|
|
124
126
|
static VALUE integer_range_sym;
|
|
127
|
+
static VALUE max_integer_digits_sym;
|
|
125
128
|
static VALUE fast_sym;
|
|
126
129
|
static VALUE float_prec_sym;
|
|
127
130
|
static VALUE float_format_sym;
|
|
@@ -206,6 +209,7 @@ struct _options oj_default_options = {
|
|
|
206
209
|
0, // cache_str
|
|
207
210
|
0, // int_range_min
|
|
208
211
|
0, // int_range_max
|
|
212
|
+
0, // max_integer_digits
|
|
209
213
|
oj_json_class, // create_id
|
|
210
214
|
10, // create_id_len
|
|
211
215
|
9, // sec_prec
|
|
@@ -334,6 +338,11 @@ static VALUE only_array_from_string(const char *str) {
|
|
|
334
338
|
* - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less
|
|
335
339
|
* than this are cached)
|
|
336
340
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
|
341
|
+
* - *:max_integer_digits* [_Fixnum_] Maximum number of decimal digits allowed in a
|
|
342
|
+
* parsed integer. When the limit is exceeded a parse error is raised. 0 (the
|
|
343
|
+
* default) disables the limit. Setting a reasonable limit is recommended when
|
|
344
|
+
* parsing untrusted input to mitigate CPU-DoS attacks. Only applies to the
|
|
345
|
+
* legacy parsers (Oj.load, Oj::Doc, JSON.parse mimic); Oj::Parser is unaffected.
|
|
337
346
|
* - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false
|
|
338
347
|
* (trace is off)
|
|
339
348
|
* - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default
|
|
@@ -448,6 +457,7 @@ static VALUE get_def_opts(VALUE self) {
|
|
|
448
457
|
} else {
|
|
449
458
|
rb_hash_aset(opts, integer_range_sym, Qnil);
|
|
450
459
|
}
|
|
460
|
+
rb_hash_aset(opts, max_integer_digits_sym, LONG2NUM((long)oj_default_options.max_integer_digits));
|
|
451
461
|
switch (oj_default_options.escape_mode) {
|
|
452
462
|
case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
|
|
453
463
|
case JSONEsc: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
|
|
@@ -591,6 +601,8 @@ static VALUE get_def_opts(VALUE self) {
|
|
|
591
601
|
* - *:cache_keys* [_Boolean_] if true then hash keys are cached
|
|
592
602
|
* - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less than this are cached)
|
|
593
603
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
|
604
|
+
* - *:max_integer_digits* [_Fixnum_] Maximum decimal digits in a parsed integer
|
|
605
|
+
* (0 = unlimited). Use to mitigate CPU-DoS via huge integer values in JSON.
|
|
594
606
|
* - *:trace* [_Boolean_] turn trace on or off.
|
|
595
607
|
* - *:safe* [_Boolean_] turn safe mimic on or off.
|
|
596
608
|
*/
|
|
@@ -756,7 +768,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
756
768
|
case T_FIXNUM:
|
|
757
769
|
copts->dump_opts.indent_size = 0;
|
|
758
770
|
*copts->dump_opts.indent_str = '\0';
|
|
759
|
-
|
|
771
|
+
if (MAX_INDENT < FIX2INT(v)) {
|
|
772
|
+
rb_raise(rb_eArgError, "indent is limited to %d characters.", MAX_INDENT);
|
|
773
|
+
}
|
|
774
|
+
copts->indent = FIX2INT(v);
|
|
760
775
|
break;
|
|
761
776
|
case T_STRING:
|
|
762
777
|
if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
|
|
@@ -1071,6 +1086,20 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
1071
1086
|
} else if (Qfalse != v) {
|
|
1072
1087
|
rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
|
|
1073
1088
|
}
|
|
1089
|
+
} else if (max_integer_digits_sym == k) {
|
|
1090
|
+
if (Qnil == v || Qfalse == v) {
|
|
1091
|
+
copts->max_integer_digits = 0;
|
|
1092
|
+
} else if (T_FIXNUM == rb_type(v)) {
|
|
1093
|
+
long n = FIX2LONG(v);
|
|
1094
|
+
|
|
1095
|
+
if (n < 0) {
|
|
1096
|
+
rb_raise(rb_eArgError, ":max_integer_digits must be >= 0.");
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
copts->max_integer_digits = (size_t)n;
|
|
1100
|
+
} else {
|
|
1101
|
+
rb_raise(rb_eArgError, ":max_integer_digits must be a non-negative Integer.");
|
|
1102
|
+
}
|
|
1074
1103
|
} else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
|
|
1075
1104
|
if (Qnil == v) {
|
|
1076
1105
|
return ST_CONTINUE;
|
|
@@ -2153,6 +2182,8 @@ void Init_oj(void) {
|
|
|
2153
2182
|
rb_gc_register_address(&escape_mode_sym);
|
|
2154
2183
|
integer_range_sym = ID2SYM(rb_intern("integer_range"));
|
|
2155
2184
|
rb_gc_register_address(&integer_range_sym);
|
|
2185
|
+
max_integer_digits_sym = ID2SYM(rb_intern("max_integer_digits"));
|
|
2186
|
+
rb_gc_register_address(&max_integer_digits_sym);
|
|
2156
2187
|
fast_sym = ID2SYM(rb_intern("fast"));
|
|
2157
2188
|
rb_gc_register_address(&fast_sym);
|
|
2158
2189
|
float_format_sym = ID2SYM(rb_intern("float_format"));
|
data/ext/oj/oj.h
CHANGED
|
@@ -124,43 +124,44 @@ typedef struct _dumpOpts {
|
|
|
124
124
|
} *DumpOpts;
|
|
125
125
|
|
|
126
126
|
typedef struct _options {
|
|
127
|
-
int indent;
|
|
128
|
-
char circular;
|
|
129
|
-
char auto_define;
|
|
130
|
-
char sym_key;
|
|
131
|
-
char escape_mode;
|
|
132
|
-
char mode;
|
|
133
|
-
char class_cache;
|
|
134
|
-
char time_format;
|
|
135
|
-
char bigdec_as_num;
|
|
136
|
-
char bigdec_load;
|
|
137
|
-
char compat_bigdec;
|
|
138
|
-
char to_hash;
|
|
139
|
-
char to_json;
|
|
140
|
-
char as_json;
|
|
141
|
-
char raw_json;
|
|
142
|
-
char nilnil;
|
|
143
|
-
char empty_string;
|
|
144
|
-
char allow_gc;
|
|
145
|
-
char quirks_mode;
|
|
146
|
-
char allow_invalid;
|
|
147
|
-
char create_ok;
|
|
148
|
-
char allow_nan;
|
|
149
|
-
char trace;
|
|
150
|
-
char safe;
|
|
151
|
-
char sec_prec_set;
|
|
152
|
-
char ignore_under;
|
|
153
|
-
char cache_keys;
|
|
154
|
-
char cache_str;
|
|
155
|
-
int64_t int_range_min;
|
|
156
|
-
int64_t int_range_max;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
char
|
|
162
|
-
|
|
163
|
-
VALUE
|
|
127
|
+
int indent; // indention for dump, default 2
|
|
128
|
+
char circular; // YesNo
|
|
129
|
+
char auto_define; // YesNo
|
|
130
|
+
char sym_key; // YesNo
|
|
131
|
+
char escape_mode; // Escape_Mode
|
|
132
|
+
char mode; // Mode
|
|
133
|
+
char class_cache; // YesNo
|
|
134
|
+
char time_format; // TimeFormat
|
|
135
|
+
char bigdec_as_num; // YesNo
|
|
136
|
+
char bigdec_load; // BigLoad
|
|
137
|
+
char compat_bigdec; // boolean (0 or 1)
|
|
138
|
+
char to_hash; // YesNo
|
|
139
|
+
char to_json; // YesNo
|
|
140
|
+
char as_json; // YesNo
|
|
141
|
+
char raw_json; // YesNo
|
|
142
|
+
char nilnil; // YesNo
|
|
143
|
+
char empty_string; // YesNo
|
|
144
|
+
char allow_gc; // allow GC during parse
|
|
145
|
+
char quirks_mode; // allow single JSON values instead of documents
|
|
146
|
+
char allow_invalid; // YesNo - allow invalid unicode
|
|
147
|
+
char create_ok; // YesNo allow create_id
|
|
148
|
+
char allow_nan; // YesNo for parsing only
|
|
149
|
+
char trace; // YesNo
|
|
150
|
+
char safe; // YesNo
|
|
151
|
+
char sec_prec_set; // boolean (0 or 1)
|
|
152
|
+
char ignore_under; // YesNo - ignore attrs starting with _ if true in object and custom modes
|
|
153
|
+
char cache_keys; // YesNo
|
|
154
|
+
char cache_str; // string short than or equal to this are cache
|
|
155
|
+
int64_t int_range_min; // dump numbers below as string
|
|
156
|
+
int64_t int_range_max; // dump numbers above as string
|
|
157
|
+
size_t max_integer_digits; // 0 = unlimited; max decimal digits for parsed integers
|
|
158
|
+
const char *create_id; // 0 or string
|
|
159
|
+
size_t create_id_len; // length of create_id
|
|
160
|
+
int sec_prec; // second precision when dumping time
|
|
161
|
+
char float_prec; // float precision, linked to float_fmt
|
|
162
|
+
char float_fmt[7]; // float format for dumping, if empty use Ruby
|
|
163
|
+
VALUE hash_class; // class to use in place of Hash on load
|
|
164
|
+
VALUE array_class; // class to use in place of Array on load
|
|
164
165
|
struct _dumpOpts dump_opts;
|
|
165
166
|
struct _rxClass str_rx;
|
|
166
167
|
VALUE *ignore; // Qnil terminated array of classes or NULL
|
data/ext/oj/parse.c
CHANGED
|
@@ -394,7 +394,7 @@ void oj_scanner_init(void) {
|
|
|
394
394
|
static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
395
395
|
struct _buf buf;
|
|
396
396
|
const char *s;
|
|
397
|
-
|
|
397
|
+
size_t cnt = pi->cur - start;
|
|
398
398
|
uint32_t code;
|
|
399
399
|
Val parent = stack_peek(&pi->stack);
|
|
400
400
|
|
|
@@ -669,7 +669,7 @@ static void read_num(ParseInfo pi) {
|
|
|
669
669
|
// A trailing . is not a valid decimal but if encountered allow it
|
|
670
670
|
// except when mimicking the JSON gem or in strict mode.
|
|
671
671
|
if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
|
|
672
|
-
|
|
672
|
+
size_t pos = pi->cur - ni.str;
|
|
673
673
|
|
|
674
674
|
if (1 == pos || (2 == pos && ni.neg)) {
|
|
675
675
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
|
@@ -971,6 +971,20 @@ static long double exp_plus[] = {
|
|
|
971
971
|
1.0e39, 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
|
|
972
972
|
};
|
|
973
973
|
|
|
974
|
+
static void validate_integer_size(size_t limit, NumInfo ni) {
|
|
975
|
+
size_t digit_count = ni->len - (ni->neg ? 1 : 0);
|
|
976
|
+
|
|
977
|
+
if (digit_count > limit) {
|
|
978
|
+
oj_set_error_at(ni->pi,
|
|
979
|
+
(Qnil != ni->pi->err_class) ? ni->pi->err_class : oj_parse_error_class,
|
|
980
|
+
__FILE__,
|
|
981
|
+
__LINE__,
|
|
982
|
+
"integer exceeds :max_integer_digits (%lu > %lu)",
|
|
983
|
+
(unsigned long)digit_count,
|
|
984
|
+
(unsigned long)limit);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
974
988
|
VALUE
|
|
975
989
|
oj_num_as_value(NumInfo ni) {
|
|
976
990
|
VALUE rnum = Qnil;
|
|
@@ -984,6 +998,12 @@ oj_num_as_value(NumInfo ni) {
|
|
|
984
998
|
} else if (ni->nan) {
|
|
985
999
|
rnum = rb_float_new(0.0 / 0.0);
|
|
986
1000
|
} else if (1 == ni->div && 0 == ni->exp && !ni->has_exp) { // fixnum
|
|
1001
|
+
size_t limit = (NULL != ni->pi) ? ni->pi->options.max_integer_digits : 0;
|
|
1002
|
+
|
|
1003
|
+
if (0 < limit) {
|
|
1004
|
+
validate_integer_size(limit, ni);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
987
1007
|
if (ni->big) {
|
|
988
1008
|
if (256 > ni->len) {
|
|
989
1009
|
char buf[256];
|