--- parser3/src/classes/json.C 2014/05/04 01:38:01 1.30 +++ parser3/src/classes/json.C 2015/07/22 22:10:38 1.36 @@ -18,7 +18,7 @@ #include "pa_vxdoc.h" #endif -volatile const char * IDENT_JSON_C="$Id: json.C,v 1.30 2014/05/04 01:38:01 misha Exp $"; +volatile const char * IDENT_JSON_C="$Id: json.C,v 1.36 2015/07/22 22:10:38 moko Exp $"; // class @@ -205,7 +205,7 @@ static const char* json_error_message(in "nesting limit", "data limit", "comment not allowed by config", - "unexpected char", + "unexpected character", "missing unicode low surrogate", "unexpected unicode low surrogate", "error comma out of structure", @@ -216,6 +216,67 @@ static const char* json_error_message(in extern String::Language get_untaint_lang(const String& lang_name); +#define SOURCE_MAX_LEN 60 + +void json_exception_with_source(Request& r, const char* msg, const char* json, int offset){ + int i; + + int line=0; + int start=0; + int end=strlen(json); + + if(offset>end) + offset=end; + + for(i = 0; i < offset; i++){ + if(json[i]=='\n'){ + line++; + } + } + + if(offset > SOURCE_MAX_LEN/2) + start = offset - SOURCE_MAX_LEN/2; + + for(i = offset-1; i>=start; i--){ + if(json[i]=='\n'){ + start=i+1; + break; + } + } + + if(start+SOURCE_MAX_LEN < end) + end=start+SOURCE_MAX_LEN; + + for(i = offset+1; i0){ + String s_source(pa_strdup(source,source_offset)); + source_offset=s_source.length(r.charsets.source()); + } + } + + throw Exception("json.parse", 0, "%s at line %d\n%s\n%*s", msg, line+1, source, source_offset+1, "^"); +} + static void _parse(Request& r, MethodParams& params) { const String& json_string=params.as_string(0, "json must be string"); @@ -282,13 +343,20 @@ static void _parse(Request& r, MethodPar if(int result = json_parser_init(&parser, &config, (json_parser_callback)&json_callback, &json)) throw Exception("json.parse", 0, "%s", json_error_message(result)); + if(!*json_cstr) + throw Exception("json.parse", 0, "empty string is not valid json"); + + const char *first_quote=strchr(json_cstr,'"'); + if(first_quote && first_quote>json_cstr && *(--first_quote) == '\\') + json_exception_with_source(r, "illegal quote escape, json may be tainted", json_cstr, first_quote-json_cstr); + uint32_t processed; if(int result = json_parser_string(&parser, json_cstr, strlen(json_cstr), &processed)) - throw Exception("json.parse", 0, "%s at byte %d", json_error_message(result), processed); + json_exception_with_source(r, json_error_message(result), json_cstr, processed); if (!json_parser_is_done(&parser)) - throw Exception("json.parse", 0, "unexpected end of json data"); - + json_exception_with_source(r, "unexpected end of json data", json_cstr, processed); + json_parser_free(&parser); if (json.result) r.write_no_lang(*json.result); @@ -369,7 +437,7 @@ const String& value_json_string(String:: Value* method=options.methods->get(v.type()); if(!method){ method=options.methods->first_that(based_on, &v); - options.methods->put(key, method ? method : VVoid::get()); + options.methods->put(v.type(), method ? method : VVoid::get()); } if(method && !method->is_void()) { Junction* junction=method->get_junction(); @@ -427,6 +495,11 @@ static void _string(Request& r, MethodPa if(!json.set_file_format(svalue)) throw Exception(PARSER_RUNTIME, &svalue, "must be 'base64', 'text' or 'stat'"); valid_options++; + } else if(key == "void" && value->is_string()){ + const String& svalue=value->as_string(); + if(!json.set_void_format(svalue)) + throw Exception(PARSER_RUNTIME, &svalue, "must be 'string' or 'null'"); + valid_options++; #ifdef XML } else if(key == "xdoc" && (vvalue = value->get_hash())){ json.xdoc_options=new XDocOutputOptions(); @@ -447,9 +520,11 @@ static void _string(Request& r, MethodPa // special handling for $._default if(VHash* vhash=static_cast(params[1].as(VHASH_TYPE))) if(Value* value=vhash->get_default()) { + if(!value->is_string()){ Junction* junction=value->get_junction(); if(!junction || !junction->method || !junction->method->params_names || junction->method->params_names->count() != 3) - throw Exception(PARSER_RUNTIME, 0, "$.%s must be parser method with 3 parameters", HASH_DEFAULT_ELEMENT_NAME); + throw Exception(PARSER_RUNTIME, 0, "$.%s must be string or parser method with 3 parameters", HASH_DEFAULT_ELEMENT_NAME); + } json.default_method=value; } @@ -457,7 +532,7 @@ static void _string(Request& r, MethodPa json.methods=methods; } - const String& result_string=value_json_string(String::Body(), params[0], json); + const String& result_string=value_json_string(String::Body(), r.process_to_value(params[0]), json); String::Body result_body=result_string.cstr_to_string_body_untaint(String::L_JSON, r.connection(false), &r.charsets); r.write_pass_lang(*new String(result_body, String::L_AS_IS)); }