lib/json/pure/parser.rb in json_pure-1.0.4 vs lib/json/pure/parser.rb in json_pure-1.1.0

- old
+ new

@@ -5,12 +5,13 @@ # This class implements the JSON parser that is used to parse a JSON string # into a Ruby data structure. class Parser < StringScanner STRING = /" ((?:[^\x0-\x1f"\\] | \\["\\\/bfnrt] | - \\u[0-9a-fA-F]{4})*) - "/x + \\u[0-9a-fA-F]{4} | + \\[\x20-\xff])*) + "/nx INTEGER = /(-?0|-?[1-9]\d*)/ FLOAT = /(-? (?:0|[1-9]\d*) (?: \.\d+(?i:e[+-]?\d+) | @@ -43,12 +44,24 @@ )mx UNPARSED = Object.new # Creates a new JSON::Pure::Parser instance for the string _source_. - def initialize(source) + # + # It will be configured by the _opts_ hash. _opts_ can have the following + # keys: + # * *max_nesting*: The maximum depth of nesting allowed in the parsed data + # structures. Disable depth checking with :max_nesting => false. + def initialize(source, opts = {}) super + if !opts.key?(:max_nesting) # defaults to 19 + @max_nesting = 19 + elsif opts[:max_nesting] + @max_nesting = opts[:max_nesting] + else + @max_nesting = 0 + end @create_id = JSON.create_id end alias source string @@ -59,13 +72,15 @@ obj = nil until eos? case when scan(OBJECT_OPEN) obj and raise ParserError, "source '#{peek(20)}' not in JSON!" + @current_nesting = 1 obj = parse_object when scan(ARRAY_OPEN) obj and raise ParserError, "source '#{peek(20)}' not in JSON!" + @current_nesting = 1 obj = parse_array when skip(IGNORE) ; else raise ParserError, "source '#{peek(20)}' not in JSON!" @@ -76,25 +91,27 @@ end private # Unescape characters in strings. - UNESCAPE_MAP = { + UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr } + UNESCAPE_MAP.update({ ?" => '"', ?\\ => '\\', ?/ => '/', ?b => "\b", ?f => "\f", ?n => "\n", ?r => "\r", ?t => "\t", - } + ?u => nil, + }) def parse_string if scan(STRING) return '' if self[1].empty? - self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+))) do |c| + self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c| if u = UNESCAPE_MAP[c[1]] u else # \uXXXX bytes = '' i = 0 @@ -125,19 +142,27 @@ when scan(NULL) nil when (string = parse_string) != UNPARSED string when scan(ARRAY_OPEN) - parse_array + @current_nesting += 1 + ary = parse_array + @current_nesting -= 1 + ary when scan(OBJECT_OPEN) - parse_object + @current_nesting += 1 + obj = parse_object + @current_nesting -= 1 + obj else UNPARSED end end def parse_array + raise NestingError, "nesting of #@current_nesting is to deep" if + @max_nesting.nonzero? && @current_nesting > @max_nesting result = [] delim = false until eos? case when (value = parse_value) != UNPARSED @@ -164,9 +189,11 @@ end result end def parse_object + raise NestingError, "nesting of #@current_nesting is to deep" if + @max_nesting.nonzero? && @current_nesting > @max_nesting result = {} delim = false until eos? case when (string = parse_string) != UNPARSED