summaryrefslogtreecommitdiff
path: root/ext/json
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2024-10-24 16:51:25 +0200
committerHiroshi SHIBATA <[email protected]>2024-11-01 13:04:24 +0900
commitf2b8829df0092409c944aafeac03f72ab2a6e7ac ()
tree69914ee130dcb936246fcf7d48fe1711d954561c /ext/json
parentcc2e67a138d258290f727f5797bdc14fbc5a6e52 (diff)
Deprecate unsafe default options of `JSON.load`
[Feature #19528] Ref: https://bugs.ruby-lang.org/issues/19528 `load` is understood as the default method for serializer kind of libraries, and the default options of `JSON.load` has caused many security vulnerabilities over the years. The plan is to do like YAML/Psych, deprecate these default options and direct users toward using `JSON.unsafe_load` so at least it's obvious it should be used against untrusted data.
-rw-r--r--ext/json/lib/json/common.rb187
-rw-r--r--ext/json/parser/parser.c135
-rw-r--r--ext/json/parser/parser.h1
-rw-r--r--ext/json/parser/parser.rl15
4 files changed, 257 insertions, 81 deletions
@@ -49,18 +49,9 @@ module JSON
# level (absolute namespace path?). If there doesn't exist a constant at
# the given path, an ArgumentError is raised.
def deep_const_get(path) # :nodoc:
- path.to_s.split(/::/).inject(Object) do |p, c|
- case
- when c.empty? then p
- when p.const_defined?(c, true) then p.const_get(c)
- else
- begin
- p.const_missing(c)
- rescue NameError => e
- raise ArgumentError, "can't get const #{path}: #{e}"
- end
- end
- end
end
# Set the module _generator_ to be used by JSON.
@@ -69,7 +60,7 @@ module JSON
@generator = generator
generator_methods = generator::GeneratorMethods
for const in generator_methods.constants
- klass = deep_const_get(const)
modul = generator_methods.const_get(const)
klass.class_eval do
instance_methods(false).each do |m|
@@ -404,6 +395,20 @@ module JSON
# :startdoc:
class << self
# Sets or returns default options for the JSON.load method.
# Initially:
# opts = JSON.load_default_options
@@ -411,11 +416,162 @@ module JSON
attr_accessor :load_default_options
end
self.load_default_options = {
- :max_nesting => false,
:allow_nan => true,
:allow_blank => true,
- :create_additions => true,
}
# :call-seq:
# JSON.load(source, proc = nil, options = {}) -> object
@@ -439,6 +595,7 @@ module JSON
# BEWARE: This method is meant to serialise data from trusted user input,
# like from your own database server or clients under your control, it could
# be dangerous to allow untrusted users to pass JSON sources into it.
# - Argument +opts+, if given, contains a \Hash of options for the parsing.
# See {Parsing Options}[#module-JSON-label-Parsing+Options].
# The default options can be changed via method JSON.load_default_options=.
@@ -474,6 +474,9 @@ case 26:
if (!NIL_P(klassname)) {
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
*result = rb_funcall(klass, i_json_create, 1, *result);
}
}
@@ -486,7 +489,7 @@ case 26:
-#line 490 "parser.c"
enum {JSON_value_start = 1};
enum {JSON_value_first_final = 29};
enum {JSON_value_error = 0};
@@ -494,7 +497,7 @@ enum {JSON_value_error = 0};
enum {JSON_value_en_main = 1};
-#line 287 "parser.rl"
static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -502,14 +505,14 @@ static char *JSON_parse_value(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 506 "parser.c"
{
cs = JSON_value_start;
}
-#line 294 "parser.rl"
-#line 513 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -543,14 +546,14 @@ st0:
cs = 0;
goto _out;
tr2:
-#line 239 "parser.rl"
{
char *np = JSON_parse_string(json, p, pe, result);
if (np == NULL) { p--; {p++; cs = 29; goto _out;} } else {p = (( np))-1;}
}
goto st29;
tr3:
-#line 244 "parser.rl"
{
char *np;
if(pe > p + 8 && !strncmp(MinusInfinity, p, 9)) {
@@ -570,7 +573,7 @@ tr3:
}
goto st29;
tr7:
-#line 262 "parser.rl"
{
char *np;
np = JSON_parse_array(json, p, pe, result, current_nesting + 1);
@@ -578,7 +581,7 @@ tr7:
}
goto st29;
tr11:
-#line 268 "parser.rl"
{
char *np;
np = JSON_parse_object(json, p, pe, result, current_nesting + 1);
@@ -586,7 +589,7 @@ tr11:
}
goto st29;
tr25:
-#line 232 "parser.rl"
{
if (json->allow_nan) {
*result = CInfinity;
@@ -596,7 +599,7 @@ tr25:
}
goto st29;
tr27:
-#line 225 "parser.rl"
{
if (json->allow_nan) {
*result = CNaN;
@@ -606,19 +609,19 @@ tr27:
}
goto st29;
tr31:
-#line 219 "parser.rl"
{
*result = Qfalse;
}
goto st29;
tr34:
-#line 216 "parser.rl"
{
*result = Qnil;
}
goto st29;
tr37:
-#line 222 "parser.rl"
{
*result = Qtrue;
}
@@ -627,9 +630,9 @@ st29:
if ( ++p == pe )
goto _test_eof29;
case 29:
-#line 274 "parser.rl"
{ p--; {p++; cs = 29; goto _out;} }
-#line 633 "parser.c"
switch( (*p) ) {
case 13: goto st29;
case 32: goto st29;
@@ -870,7 +873,7 @@ case 28:
_out: {}
}
-#line 295 "parser.rl"
if (json->freeze) {
OBJ_FREEZE(*result);
@@ -884,7 +887,7 @@ case 28:
}
-#line 888 "parser.c"
enum {JSON_integer_start = 1};
enum {JSON_integer_first_final = 3};
enum {JSON_integer_error = 0};
@@ -892,7 +895,7 @@ enum {JSON_integer_error = 0};
enum {JSON_integer_en_main = 1};
-#line 315 "parser.rl"
static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -900,15 +903,15 @@ static char *JSON_parse_integer(JSON_Parser *json, char *p, char *pe, VALUE *res
int cs = EVIL;
-#line 904 "parser.c"
{
cs = JSON_integer_start;
}
-#line 322 "parser.rl"
json->memo = p;
-#line 912 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -942,14 +945,14 @@ case 3:
goto st0;
goto tr4;
tr4:
-#line 312 "parser.rl"
{ p--; {p++; cs = 4; goto _out;} }
goto st4;
st4:
if ( ++p == pe )
goto _test_eof4;
case 4:
-#line 953 "parser.c"
goto st0;
st5:
if ( ++p == pe )
@@ -968,7 +971,7 @@ case 5:
_out: {}
}
-#line 324 "parser.rl"
if (cs >= JSON_integer_first_final) {
long len = p - json->memo;
@@ -983,7 +986,7 @@ case 5:
}
-#line 987 "parser.c"
enum {JSON_float_start = 1};
enum {JSON_float_first_final = 8};
enum {JSON_float_error = 0};
@@ -991,7 +994,7 @@ enum {JSON_float_error = 0};
enum {JSON_float_en_main = 1};
-#line 349 "parser.rl"
static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *result)
@@ -999,15 +1002,15 @@ static char *JSON_parse_float(JSON_Parser *json, char *p, char *pe, VALUE *resul
int cs = EVIL;
-#line 1003 "parser.c"
{
cs = JSON_float_start;
}
-#line 356 "parser.rl"
json->memo = p;
-#line 1011 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1065,14 +1068,14 @@ case 8:
goto st0;
goto tr9;
tr9:
-#line 343 "parser.rl"
{ p--; {p++; cs = 9; goto _out;} }
goto st9;
st9:
if ( ++p == pe )
goto _test_eof9;
case 9:
-#line 1076 "parser.c"
goto st0;
st5:
if ( ++p == pe )
@@ -1133,7 +1136,7 @@ case 7:
_out: {}
}
-#line 358 "parser.rl"
if (cs >= JSON_float_first_final) {
VALUE mod = Qnil;
@@ -1186,7 +1189,7 @@ case 7:
-#line 1190 "parser.c"
enum {JSON_array_start = 1};
enum {JSON_array_first_final = 17};
enum {JSON_array_error = 0};
@@ -1194,7 +1197,7 @@ enum {JSON_array_error = 0};
enum {JSON_array_en_main = 1};
-#line 438 "parser.rl"
static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *result, int current_nesting)
@@ -1208,14 +1211,14 @@ static char *JSON_parse_array(JSON_Parser *json, char *p, char *pe, VALUE *resul
*result = NIL_P(array_class) ? rb_ary_new() : rb_class_new_instance(0, 0, array_class);
-#line 1212 "parser.c"
{
cs = JSON_array_start;
}
-#line 451 "parser.rl"
-#line 1219 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1254,7 +1257,7 @@ case 2:
goto st2;
goto st0;
tr2:
-#line 415 "parser.rl"
{
VALUE v = Qnil;
char *np = JSON_parse_value(json, p, pe, &v, current_nesting);
@@ -1274,7 +1277,7 @@ st3:
if ( ++p == pe )
goto _test_eof3;
case 3:
-#line 1278 "parser.c"
switch( (*p) ) {
case 13: goto st3;
case 32: goto st3;
@@ -1374,14 +1377,14 @@ case 12:
goto st3;
goto st12;
tr4:
-#line 430 "parser.rl"
{ p--; {p++; cs = 17; goto _out;} }
goto st17;
st17:
if ( ++p == pe )
goto _test_eof17;
case 17:
-#line 1385 "parser.c"
goto st0;
st13:
if ( ++p == pe )
@@ -1437,7 +1440,7 @@ case 16:
_out: {}
}
-#line 452 "parser.rl"
if(cs >= JSON_array_first_final) {
return p + 1;
@@ -1598,7 +1601,7 @@ static VALUE json_string_unescape(char *string, char *stringEnd, int intern, int
}
-#line 1602 "parser.c"
enum {JSON_string_start = 1};
enum {JSON_string_first_final = 8};
enum {JSON_string_error = 0};
@@ -1606,7 +1609,7 @@ enum {JSON_string_error = 0};
enum {JSON_string_en_main = 1};
-#line 630 "parser.rl"
static int
@@ -1627,15 +1630,15 @@ static char *JSON_parse_string(JSON_Parser *json, char *p, char *pe, VALUE *resu
VALUE match_string;
-#line 1631 "parser.c"
{
cs = JSON_string_start;
}
-#line 650 "parser.rl"
json->memo = p;
-#line 1639 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -1660,7 +1663,7 @@ case 2:
goto st0;
goto st2;
tr2:
-#line 617 "parser.rl"
{
*result = json_string_unescape(json->memo + 1, p, json->parsing_name || json-> freeze, json->parsing_name && json->symbolize_names);
if (NIL_P(*result)) {
@@ -1670,14 +1673,14 @@ tr2:
{p = (( p + 1))-1;}
}
}
-#line 627 "parser.rl"
{ p--; {p++; cs = 8; goto _out;} }
goto st8;
st8:
if ( ++p == pe )
goto _test_eof8;
case 8:
-#line 1681 "parser.c"
goto st0;
st3:
if ( ++p == pe )
@@ -1753,7 +1756,7 @@ case 7:
_out: {}
}
-#line 652 "parser.rl"
if (json->create_additions && RTEST(match_string = json->match_string)) {
VALUE klass;
@@ -1888,10 +1891,16 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
tmp = ID2SYM(i_create_additions);
if (option_given_p(opts, tmp)) {
- json->create_additions = RTEST(rb_hash_aref(opts, tmp));
- } else {
- json->create_additions = 0;
}
if (json->symbolize_names && json->create_additions) {
rb_raise(rb_eArgError,
"options :symbolize_names and :create_additions cannot be "
@@ -1946,7 +1955,7 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
-#line 1950 "parser.c"
enum {JSON_start = 1};
enum {JSON_first_final = 10};
enum {JSON_error = 0};
@@ -1954,7 +1963,7 @@ enum {JSON_error = 0};
enum {JSON_en_main = 1};
-#line 858 "parser.rl"
/*
@@ -1972,16 +1981,16 @@ static VALUE cParser_parse(VALUE self)
GET_PARSER;
-#line 1976 "parser.c"
{
cs = JSON_start;
}
-#line 875 "parser.rl"
p = json->source;
pe = p + json->len;
-#line 1985 "parser.c"
{
if ( p == pe )
goto _test_eof;
@@ -2015,7 +2024,7 @@ st0:
cs = 0;
goto _out;
tr2:
-#line 850 "parser.rl"
{
char *np = JSON_parse_value(json, p, pe, &result, 0);
if (np == NULL) { p--; {p++; cs = 10; goto _out;} } else {p = (( np))-1;}
@@ -2025,7 +2034,7 @@ st10:
if ( ++p == pe )
goto _test_eof10;
case 10:
-#line 2029 "parser.c"
switch( (*p) ) {
case 13: goto st10;
case 32: goto st10;
@@ -2114,7 +2123,7 @@ case 9:
_out: {}
}
-#line 878 "parser.rl"
if (cs >= JSON_first_final && p == pe) {
return result;
@@ -26,6 +26,7 @@ typedef struct JSON_ParserStruct {
char symbolize_names;
char freeze;
char create_additions;
} JSON_Parser;
#define GET_PARSER \
@@ -196,6 +196,9 @@ static char *JSON_parse_object(JSON_Parser *json, char *p, char *pe, VALUE *resu
if (!NIL_P(klassname)) {
VALUE klass = rb_funcall(mJSON, i_deep_const_get, 1, klassname);
if (RTEST(rb_funcall(klass, i_json_creatable_p, 0))) {
*result = rb_funcall(klass, i_json_create, 1, *result);
}
}
@@ -783,10 +786,16 @@ static VALUE cParser_initialize(int argc, VALUE *argv, VALUE self)
}
tmp = ID2SYM(i_create_additions);
if (option_given_p(opts, tmp)) {
- json->create_additions = RTEST(rb_hash_aref(opts, tmp));
- } else {
- json->create_additions = 0;
}
if (json->symbolize_names && json->create_additions) {
rb_raise(rb_eArgError,
"options :symbolize_names and :create_additions cannot be "