From c4be790c0e20bfa4def3103392f404de201b3487 Mon Sep 17 00:00:00 2001 From: Erik Dubbelboer Date: Wed, 15 Jul 2015 16:12:05 +0200 Subject: encoding/json: check if Number is valid json.Number is a special case which didn't have any checks and could result in invalid JSON. Fixes #10281 Change-Id: Ie3e726e4d6bf6a6aba535d36f6107013ceac913a Reviewed-on: https://go-review.googlesource.com/12250 Reviewed-by: Russ Cox --- src/encoding/json/decode.go | 121 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) (limited to 'src/encoding/json/decode.go') diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index bd939b4258..ef08b0c274 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -174,6 +174,124 @@ func (n Number) Int64() (int64, error) { return strconv.ParseInt(string(n), 10, 64) } +// IsValid returns if the number is a valid JSON number literal. +func (n Number) IsValid() bool { + // This function implements the JSON numbers grammar. + // See https://tools.ietf.org/html/rfc7159#section-6 + // and http://json.org/number.gif + + l := len(n) + if l == 0 { + return false + } + + i := 0 + c := n[i] + i++ + + // Optional - + if c == '-' { + if i == l { + return false + } + + c = n[i] + i++ + } + + // 1-9 + if c >= '1' && c <= '9' { + // Eat digits. + for ; i < l; i++ { + c = n[i] + if c < '0' || c > '9' { + break + } + } + i++ + } else if c != '0' { + // If it's not 0 or 1-9 it's invalid. + return false + } else { + if i == l { + // Just 0 + return true + } + + // Skip the 0 + c = n[i] + i++ + } + + // . followed by 1 or more digits. + if c == '.' { + if i == l { + // Just 1. is invalid. + return false + } + + // . needs to be followed by at least one digit. + c = n[i] + i++ + if c < '0' || c > '9' { + return false + } + + // Eat digits. + for ; i < l; i++ { + c = n[i] + if c < '0' || c > '9' { + break + } + } + i++ + } + + // e or E followed by an optional - or + and + // 1 or more digits. + if c == 'e' || c == 'E' { + if i == l { + // Just 1e is invalid. + return false + } + + c = n[i] + i++ + + // Optional - or + + if c == '-' || c == '+' { + if i == l { + // Just 1e+ is invalid. + return false + } + + c = n[i] + i++ + } + + // Need to have a digit. + if c < '0' || c > '9' { + return false + } + + // Eat digits. + for ; i < l; i++ { + c = n[i] + if c < '0' || c > '9' { + break + } + } + i++ + } + + // Make sure we are at the end. + if i <= l { + return false + } + + return true +} + // decodeState represents the state while decoding a JSON value. type decodeState struct { data []byte @@ -781,6 +899,9 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool default: if v.Kind() == reflect.String && v.Type() == numberType { v.SetString(s) + if !Number(s).IsValid() { + d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) + } break } if fromQuoted { -- cgit v1.3-6-g1900