summary refs log tree commit
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2015-05-06 11:26:22 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2015-05-06 11:26:45 -0700
commit8cd61062954f70e0a03e2855704e95ff4bdd4f6e (patch)
tree14a87f0096c0e9fcb0437f5063ef7e599c7d164c
parent97fd715f5e50e3ddb6597a4ab2781c64d2d114b1 (diff)
downloadrack-8cd61062954f70e0a03e2855704e95ff4bdd4f6e.tar.gz
updating OkJson due to CVE-2014-9490
-rw-r--r--lib/rack/utils/okjson.rb181
1 files changed, 91 insertions, 90 deletions
diff --git a/lib/rack/utils/okjson.rb b/lib/rack/utils/okjson.rb
index dcf80e55..0d1049e1 100644
--- a/lib/rack/utils/okjson.rb
+++ b/lib/rack/utils/okjson.rb
@@ -21,16 +21,14 @@
 # THE SOFTWARE.
 
 # See https://github.com/kr/okjson for updates.
-# Imported from the above repo @ d4e8643ad92e14b37d11326855499c7e4108ed17
-# Namespace modified for vendoring under Rack::Utils
 
 require 'stringio'
 
 # Some parts adapted from
-# http://golang.org/src/pkg/json/decode.go and
-# http://golang.org/src/pkg/utf8/utf8.go
+# https://golang.org/src/encoding/json/decode.go and
+# https://golang.org/src/unicode/utf8/utf8.go
 module Rack::Utils::OkJson
-  Upstream = 'LTD7LBKLZWFF7OZK'
+  Upstream = '43'
   extend self
 
 
@@ -52,12 +50,49 @@ module Rack::Utils::OkJson
   end
 
 
+  # Encodes x into a json text. It may contain only
+  # Array, Hash, String, Numeric, true, false, nil.
+  # (Note, this list excludes Symbol.)
+  # X itself must be an Array or a Hash.
+  # No other value can be encoded, and an error will
+  # be raised if x contains any other value, such as
+  # Nan, Infinity, Symbol, and Proc, or if a Hash key
+  # is not a String.
+  # Strings contained in x must be valid UTF-8.
+  def encode(x)
+    case x
+    when Hash    then objenc(x)
+    when Array   then arrenc(x)
+    else
+      raise Error, 'root value must be an Array or a Hash'
+    end
+  end
+
+
+  def valenc(x)
+    case x
+    when Hash    then objenc(x)
+    when Array   then arrenc(x)
+    when String  then strenc(x)
+    when Numeric then numenc(x)
+    when true    then "true"
+    when false   then "false"
+    when nil     then "null"
+    else
+      raise Error, "cannot encode #{x.class}: #{x.inspect}"
+    end
+  end
+
+
+private
+
+
   # Parses a "json text" in the sense of RFC 4627.
   # Returns the parsed value and any trailing tokens.
   # Note: this is almost the same as valparse,
   # except that it does not accept atomic values.
   def textparse(ts)
-    if ts.length < 0
+    if ts.length <= 0
       raise Error, 'empty'
     end
 
@@ -74,7 +109,7 @@ module Rack::Utils::OkJson
   # Parses a "value" in the sense of RFC 4627.
   # Returns the parsed value and any trailing tokens.
   def valparse(ts)
-    if ts.length < 0
+    if ts.length <= 0
       raise Error, 'empty'
     end
 
@@ -203,21 +238,19 @@ module Rack::Utils::OkJson
   # it is the lexeme.
   def tok(s)
     case s[0]
-    when ?{  then ['{', s[0,1], s[0,1]]
-    when ?}  then ['}', s[0,1], s[0,1]]
-    when ?:  then [':', s[0,1], s[0,1]]
-    when ?,  then [',', s[0,1], s[0,1]]
-    when ?[  then ['[', s[0,1], s[0,1]]
-    when ?]  then [']', s[0,1], s[0,1]]
-    when ?n  then nulltok(s)
-    when ?t  then truetok(s)
-    when ?f  then falsetok(s)
-    when ?"  then strtok(s)
-    when Spc then [:space, s[0,1], s[0,1]]
-    when ?\t then [:space, s[0,1], s[0,1]]
-    when ?\n then [:space, s[0,1], s[0,1]]
-    when ?\r then [:space, s[0,1], s[0,1]]
-    else          numtok(s)
+    when ?{ then ['{', s[0,1], s[0,1]]
+    when ?} then ['}', s[0,1], s[0,1]]
+    when ?: then [':', s[0,1], s[0,1]]
+    when ?, then [',', s[0,1], s[0,1]]
+    when ?[ then ['[', s[0,1], s[0,1]]
+    when ?] then [']', s[0,1], s[0,1]]
+    when ?n then nulltok(s)
+    when ?t then truetok(s)
+    when ?f then falsetok(s)
+    when ?" then strtok(s)
+    when Spc, ?\t, ?\n, ?\r then [:space, s[0,1], s[0,1]]
+    else
+      numtok(s)
     end
   end
 
@@ -230,12 +263,12 @@ module Rack::Utils::OkJson
   def numtok(s)
     m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
     if m && m.begin(0) == 0
-      if m[3] && !m[2]
-        [:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))]
+      if !m[2] && !m[3]
+        [:val, m[0], Integer(m[0])]
       elsif m[2]
         [:val, m[0], Float(m[0])]
       else
-        [:val, m[0], Integer(m[0])]
+        [:val, m[0], Integer(m[1])*(10**m[3][1..-1].to_i(10))]
       end
     else
       []
@@ -267,17 +300,14 @@ module Rack::Utils::OkJson
   def unquote(q)
     q = q[1...-1]
     a = q.dup # allocate a big enough string
-    rubydoesenc = false
     # In ruby >= 1.9, a[w] is a codepoint, not a byte.
-    if a.class.method_defined?(:force_encoding)
+    if rubydoesenc?
       a.force_encoding('UTF-8')
-      rubydoesenc = true
     end
     r, w = 0, 0
     while r < q.length
       c = q[r]
-      case true
-      when c == ?\\
+      if c == ?\\
         r += 1
         if r >= q.length
           raise Error, "string literal ends with a \"\\\": \"#{q}\""
@@ -310,7 +340,7 @@ module Rack::Utils::OkJson
               end
             end
           end
-          if rubydoesenc
+          if rubydoesenc?
             a[w] = '' << uchar
             w += 1
           else
@@ -319,7 +349,7 @@ module Rack::Utils::OkJson
         else
           raise Error, "invalid escape char #{q[r]} in \"#{q}\""
         end
-      when c == ?", c < Spc
+      elsif c == ?" || c < Spc
         raise Error, "invalid character in string literal \"#{q}\""
       else
         # Copy anything else byte-for-byte.
@@ -340,15 +370,14 @@ module Rack::Utils::OkJson
   # bytes in string a at position i.
   # Returns the number of bytes written.
   def ucharenc(a, i, u)
-    case true
-    when u <= Uchar1max
+    if u <= Uchar1max
       a[i] = (u & 0xff).chr
       1
-    when u <= Uchar2max
+    elsif u <= Uchar2max
       a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
       a[i+1] = (Utagx | (u&Umaskx)).chr
       2
-    when u <= Uchar3max
+    elsif u <= Uchar3max
       a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
       a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
       a[i+2] = (Utagx | (u&Umaskx)).chr
@@ -385,50 +414,15 @@ module Rack::Utils::OkJson
 
 
   def nibble(c)
-    case true
-    when ?0 <= c && c <= ?9 then c.ord - ?0.ord
-    when ?a <= c && c <= ?z then c.ord - ?a.ord + 10
-    when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
+    if ?0 <= c && c <= ?9 then c.ord - ?0.ord
+    elsif ?a <= c && c <= ?z then c.ord - ?a.ord + 10
+    elsif ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
     else
       raise Error, "invalid hex code #{c}"
     end
   end
 
 
-  # Encodes x into a json text. It may contain only
-  # Array, Hash, String, Numeric, true, false, nil.
-  # (Note, this list excludes Symbol.)
-  # X itself must be an Array or a Hash.
-  # No other value can be encoded, and an error will
-  # be raised if x contains any other value, such as
-  # Nan, Infinity, Symbol, and Proc, or if a Hash key
-  # is not a String.
-  # Strings contained in x must be valid UTF-8.
-  def encode(x)
-    case x
-    when Hash    then objenc(x)
-    when Array   then arrenc(x)
-    else
-      raise Error, 'root value must be an Array or a Hash'
-    end
-  end
-
-
-  def valenc(x)
-    case x
-    when Hash    then objenc(x)
-    when Array   then arrenc(x)
-    when String  then strenc(x)
-    when Numeric then numenc(x)
-    when true    then "true"
-    when false   then "false"
-    when nil     then "null"
-    else
-      raise Error, "cannot encode #{x.class}: #{x.inspect}"
-    end
-  end
-
-
   def objenc(x)
     '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
   end
@@ -453,9 +447,6 @@ module Rack::Utils::OkJson
     t.putc(?")
     r = 0
 
-    # In ruby >= 1.9, s[r] is a codepoint, not a byte.
-    rubydoesenc = s.class.method_defined?(:encoding)
-
     while r < s.length
       case s[r]
       when ?"  then t.print('\\"')
@@ -467,15 +458,20 @@ module Rack::Utils::OkJson
       when ?\t then t.print('\\t')
       else
         c = s[r]
-        case true
-        when rubydoesenc
+        # In ruby >= 1.9, s[r] is a codepoint, not a byte.
+        if rubydoesenc?
           begin
-            c.ord # will raise an error if c is invalid UTF-8
+            # c.ord will raise an error if c is invalid UTF-8
+            if c.ord < Spc.ord
+              c = "\\u%04x" % [c.ord]
+            end
             t.write(c)
           rescue
             t.write(Ustrerr)
           end
-        when Spc <= c && c <= ?~
+        elsif c < Spc
+          t.write("\\u%04x" % c)
+        elsif Spc <= c && c <= ?~
           t.putc(c)
         else
           n = ucharcopy(t, s, r) # ensure valid UTF-8 output
@@ -567,6 +563,11 @@ module Rack::Utils::OkJson
   end
 
 
+  def rubydoesenc?
+    ::String.method_defined?(:force_encoding)
+  end
+
+
   class Utf8Error < ::StandardError
   end
 
@@ -575,15 +576,15 @@ module Rack::Utils::OkJson
   end
 
 
-  Utagx = 0x80 # 1000 0000
-  Utag2 = 0xc0 # 1100 0000
-  Utag3 = 0xe0 # 1110 0000
-  Utag4 = 0xf0 # 1111 0000
-  Utag5 = 0xF8 # 1111 1000
-  Umaskx = 0x3f # 0011 1111
-  Umask2 = 0x1f # 0001 1111
-  Umask3 = 0x0f # 0000 1111
-  Umask4 = 0x07 # 0000 0111
+  Utagx = 0b1000_0000
+  Utag2 = 0b1100_0000
+  Utag3 = 0b1110_0000
+  Utag4 = 0b1111_0000
+  Utag5 = 0b1111_1000
+  Umaskx = 0b0011_1111
+  Umask2 = 0b0001_1111
+  Umask3 = 0b0000_1111
+  Umask4 = 0b0000_0111
   Uchar1max = (1<<7) - 1
   Uchar2max = (1<<11) - 1
   Uchar3max = (1<<16) - 1