util: Fix UnmarshalJSON parses invalid json in some cases

Fixes: #2331

Signed-off-by: katsew <y.katsew@gmail.com>
This commit is contained in:
katsew
2020-04-27 21:37:38 +09:00
committed by Patrick East
parent 9d1b42d07a
commit 65a2b187c4
5 changed files with 40 additions and 11 deletions
+7 -7
View File
@@ -39,13 +39,13 @@ func TestRegexFindAllStringSubmatch(t *testing.T) {
rules []string
expected interface{}
}{
{"finds no matches", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-", -1) }`}, `[[]]]`},
{"single match without captures", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-ab-", -1) }`}, `[[["ab", ""]]]]`},
{"single match with a capture", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-axxb-", -1) }`}, `[[["axxb", "xx"]]]]`},
{"multiple matches with captures-1", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-ab-axb-", -1) }`}, `[[["ab", ""], ["axb", "x"]]]]`},
{"multiple matches with captures-2", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-axxb-ab-", -1) }`}, `[[["axxb", "xx"], ["ab", ""]]]]`},
{"multiple patterns, matches, and captures", []string{`p[x] { x = regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", -1) }`}, `[[["som", "o", "m"], ["ri", "i", ""]]]]`},
{"multiple patterns, matches, and captures with specified number of matches", []string{`p[x] { x = regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", 1) }`}, `[[["som", "o", "m"]]]]`},
{"finds no matches", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-", -1) }`}, `[[]]`},
{"single match without captures", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-ab-", -1) }`}, `[[["ab", ""]]]`},
{"single match with a capture", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-axxb-", -1) }`}, `[[["axxb", "xx"]]]`},
{"multiple matches with captures-1", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-ab-axb-", -1) }`}, `[[["ab", ""], ["axb", "x"]]]`},
{"multiple matches with captures-2", []string{`p[x] { x = regex.find_all_string_submatch_n("a(x*)b", "-axxb-ab-", -1) }`}, `[[["axxb", "xx"], ["ab", ""]]]`},
{"multiple patterns, matches, and captures", []string{`p[x] { x = regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", -1) }`}, `[[["som", "o", "m"], ["ri", "i", ""]]]`},
{"multiple patterns, matches, and captures with specified number of matches", []string{`p[x] { x = regex.find_all_string_submatch_n("[^aouiye]([aouiye])([^aouiye])?", "somestri", 1) }`}, `[[["som", "o", "m"]]]`},
}
for _, tc := range tests {
+3 -2
View File
@@ -369,6 +369,7 @@ func TestTopDownJWTEncodeSignHeaderErrors(t *testing.T) {
runTopDownTestCase(t, data, tc.note, tc.rules, tc.expected)
}
}
func TestTopDownJWTEncodeSignRaw(t *testing.T) {
const examplePayload = `{"iss":"joe",` + "\r\n" + ` "exp":1300819380,` + "\r\n" + ` "http://example.com/is_root":true}`
@@ -414,7 +415,7 @@ func TestTopDownJWTEncodeSignRaw(t *testing.T) {
"`" + "" + "`",
"`" + symmetricKey + "`",
`"eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ..sXoGQMWwM-SmX495-htA7kndgbkwz1PnqsDeY275gnI""`,
`"eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ..sXoGQMWwM-SmX495-htA7kndgbkwz1PnqsDeY275gnI"`,
"",
},
{
@@ -423,7 +424,7 @@ func TestTopDownJWTEncodeSignRaw(t *testing.T) {
"`" + "e" + "`",
"`" + symmetricKey + "`",
`"eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ.ZQ.oO8Vnc4Jv7-J231a1bEcQrgXfKbNW-kEvVY7BP1v5rM""`,
`"eyJ0eXAiOiJ0ZXh0L3BsYWluIiwNCiAiYWxnIjoiSFMyNTYifQ.ZQ.oO8Vnc4Jv7-J231a1bEcQrgXfKbNW-kEvVY7BP1v5rM"`,
"",
},
{
+1 -1
View File
@@ -1414,7 +1414,7 @@ func TestTopDownJSONBuiltins(t *testing.T) {
expected interface{}
}{
{"marshal", []string{`p = x { json.marshal([{"foo": {1,2,3}}], x) }`}, `"[{\"foo\":[1,2,3]}]"`},
{"unmarshal", []string{`p = x { json.unmarshal("[{\"foo\":[1,2,3]}]", x) }`}, `[{"foo": [1,2,3]}]"`},
{"unmarshal", []string{`p = x { json.unmarshal("[{\"foo\":[1,2,3]}]", x) }`}, `[{"foo": [1,2,3]}]`},
{"unmarshal-non-string", []string{`p = x { json.unmarshal(data.a[0], x) }`}, &Error{Code: TypeErr, Message: "operand 1 must be string but got number"}},
{"yaml round-trip", []string{`p = y { yaml.marshal([{"foo": {1,2,3}}], x); yaml.unmarshal(x, y) }`}, `[{"foo": [1,2,3]}]`},
{"yaml unmarshal error", []string{`p { yaml.unmarshal("[1,2,3", _) } `}, &Error{Code: BuiltinErr, Message: "yaml: line 1: did not find"}},
+15 -1
View File
@@ -7,6 +7,7 @@ package util
import (
"bytes"
"encoding/json"
"fmt"
"io"
"reflect"
@@ -21,7 +22,20 @@ import (
func UnmarshalJSON(bs []byte, x interface{}) (err error) {
buf := bytes.NewBuffer(bs)
decoder := NewJSONDecoder(buf)
return decoder.Decode(x)
if err := decoder.Decode(x); err != nil {
return err
}
// Since decoder.Decode validates only the first json structure in bytes,
// check if decoder has more bytes to consume to validate whole input bytes.
tok, err := decoder.Token()
if tok != nil {
return fmt.Errorf("error: invalid character '%s' after top-level value", tok)
}
if err != nil && err != io.EOF {
return err
}
return nil
}
// NewJSONDecoder returns a new decoder that reads from r.
+14
View File
@@ -13,6 +13,20 @@ import (
"github.com/open-policy-agent/opa/util"
)
func TestInvalidJSONInput(t *testing.T) {
cases := [][]byte{
[]byte("{ \"k\": 1 }\n{}}"),
[]byte("{ \"k\": 1 }\n!!!}"),
}
for _, tc := range cases {
var x interface{}
err := util.UnmarshalJSON(tc, &x)
if err == nil {
t.Errorf("should be an error")
}
}
}
func TestRoundTrip(t *testing.T) {
cases := []interface{}{
nil,