diff --git a/go.mod b/go.mod index 7f37726c244..0abf4d1bad2 100644 --- a/go.mod +++ b/go.mod @@ -35,7 +35,7 @@ require ( github.com/spf13/cobra v1.1.3 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 - github.com/tidwall/gjson v1.9.2 + github.com/tidwall/gjson v1.10.2 github.com/tidwall/pretty v1.2.0 golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e golang.org/x/net v0.0.0-20210901185426-6d2eada6345e diff --git a/go.sum b/go.sum index b75e324d93d..67d114c8f42 100644 --- a/go.sum +++ b/go.sum @@ -272,10 +272,10 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tidwall/gjson v1.9.2 h1:SJQc2IgWWKL5V+YGJrr95hjNXFeZzHT2L9Wv1aAb51Q= -github.com/tidwall/gjson v1.9.2/go.mod h1:2tcKM/KQ/GjiTN7mfTL/HdNmef9Q6AZLaSK2RdfvSjw= -github.com/tidwall/match v1.1.0 h1:VfI2e2aXLvytih7WUVyO9uvRC+RcXlaTrMbHuQWnFmk= -github.com/tidwall/match v1.1.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= diff --git a/vendor/github.com/tidwall/gjson/README.md b/vendor/github.com/tidwall/gjson/README.md index b7848de602b..f4898478e3c 100644 --- a/vendor/github.com/tidwall/gjson/README.md +++ b/vendor/github.com/tidwall/gjson/README.md @@ -200,6 +200,8 @@ There are currently the following built-in modifiers: - `@valid`: Ensure the json document is valid. - `@flatten`: Flattens an array. - `@join`: Joins multiple objects into a single object. +- `@keys`: Returns an array of keys for an object. +- `@values`: Returns an array of values for an object. ### Modifier arguments @@ -434,14 +436,15 @@ Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/js and [json-iterator](https://github.com/json-iterator/go) ``` -BenchmarkGJSONGet-8 3000000 372 ns/op 0 B/op 0 allocs/op -BenchmarkGJSONUnmarshalMap-8 900000 4154 ns/op 1920 B/op 26 allocs/op -BenchmarkJSONUnmarshalMap-8 600000 9019 ns/op 3048 B/op 69 allocs/op -BenchmarkJSONDecoder-8 300000 14120 ns/op 4224 B/op 184 allocs/op -BenchmarkFFJSONLexer-8 1500000 3111 ns/op 896 B/op 8 allocs/op -BenchmarkEasyJSONLexer-8 3000000 887 ns/op 613 B/op 6 allocs/op -BenchmarkJSONParserGet-8 3000000 499 ns/op 21 B/op 0 allocs/op -BenchmarkJSONIterator-8 3000000 812 ns/op 544 B/op 9 allocs/op +BenchmarkGJSONGet-16 11644512 311 ns/op 0 B/op 0 allocs/op +BenchmarkGJSONUnmarshalMap-16 1122678 3094 ns/op 1920 B/op 26 allocs/op +BenchmarkJSONUnmarshalMap-16 516681 6810 ns/op 2944 B/op 69 allocs/op +BenchmarkJSONUnmarshalStruct-16 697053 5400 ns/op 928 B/op 13 allocs/op +BenchmarkJSONDecoder-16 330450 10217 ns/op 3845 B/op 160 allocs/op +BenchmarkFFJSONLexer-16 1424979 2585 ns/op 880 B/op 8 allocs/op +BenchmarkEasyJSONLexer-16 3000000 729 ns/op 501 B/op 5 allocs/op +BenchmarkJSONParserGet-16 3000000 366 ns/op 21 B/op 0 allocs/op +BenchmarkJSONIterator-16 3000000 869 ns/op 693 B/op 14 allocs/op ``` JSON document used: @@ -482,4 +485,4 @@ widget.image.hOffset widget.text.onMouseUp ``` -*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8 and can be found [here](https://github.com/tidwall/gjson-benchmarks).* +*These benchmarks were run on a MacBook Pro 16" 2.4 GHz Intel Core i9 using Go 1.17 and can be found [here](https://github.com/tidwall/gjson-benchmarks).* diff --git a/vendor/github.com/tidwall/gjson/SYNTAX.md b/vendor/github.com/tidwall/gjson/SYNTAX.md index 34fdccf47e9..9bc18c8804a 100644 --- a/vendor/github.com/tidwall/gjson/SYNTAX.md +++ b/vendor/github.com/tidwall/gjson/SYNTAX.md @@ -236,6 +236,8 @@ There are currently the following built-in modifiers: - `@valid`: Ensure the json document is valid. - `@flatten`: Flattens an array. - `@join`: Joins multiple objects into a single object. +- `@keys`: Returns an array of keys for an object. +- `@values`: Returns an array of values for an object. #### Modifier arguments diff --git a/vendor/github.com/tidwall/gjson/gjson.go b/vendor/github.com/tidwall/gjson/gjson.go index 973c8af5f7b..279649eeeb8 100644 --- a/vendor/github.com/tidwall/gjson/gjson.go +++ b/vendor/github.com/tidwall/gjson/gjson.go @@ -189,14 +189,15 @@ func (t Result) Time() time.Time { } // Array returns back an array of values. -// If the result represents a non-existent value, then an empty array will be -// returned. If the result is not a JSON array, the return value will be an +// If the result represents a null value or is non-existent, then an empty +// array will be returned. +// If the result is not a JSON array, the return value will be an // array containing one result. func (t Result) Array() []Result { if t.Type == Null { return []Result{} } - if t.Type != JSON { + if !t.IsArray() { return []Result{t} } r := t.arrayOrMap('[', false) @@ -760,7 +761,7 @@ func parseArrayPath(path string) (r arrayPathResult) { // bad query, end now break } - if len(value) > 2 && value[0] == '"' && + if len(value) >= 2 && value[0] == '"' && value[len(value)-1] == '"' { value = value[1 : len(value)-1] if vesc { @@ -1089,9 +1090,9 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { } if rp.wild { if kesc { - pmatch = match.Match(unescape(key), rp.part) + pmatch = matchLimit(unescape(key), rp.part) } else { - pmatch = match.Match(key, rp.part) + pmatch = matchLimit(key, rp.part) } } else { if kesc { @@ -1102,6 +1103,7 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { } hit = pmatch && !rp.more for ; i < len(c.json); i++ { + var num bool switch c.json[i] { default: continue @@ -1149,15 +1151,13 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { return i, true } } - case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - i, val = parseNumber(c.json, i) - if hit { - c.value.Raw = val - c.value.Type = Number - c.value.Num, _ = strconv.ParseFloat(val, 64) - return i, true + case 'n': + if i+1 < len(c.json) && c.json[i+1] != 'u' { + num = true + break } - case 't', 'f', 'n': + fallthrough + case 't', 'f': vc := c.json[i] i, val = parseLiteral(c.json, i) if hit { @@ -1170,12 +1170,33 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { } return i, true } + case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'i', 'I', 'N': + num = true + } + if num { + i, val = parseNumber(c.json, i) + if hit { + c.value.Raw = val + c.value.Type = Number + c.value.Num, _ = strconv.ParseFloat(val, 64) + return i, true + } } break } } return i, false } + +// matchLimit will limit the complexity of the match operation to avoid ReDos +// attacks from arbritary inputs. +// See the github.com/tidwall/match.MatchLimit function for more information. +func matchLimit(str, pattern string) bool { + matched, _ := match.MatchLimit(str, pattern, 10000) + return matched +} + func queryMatches(rp *arrayPathResult, value Result) bool { rpv := rp.query.value if len(rpv) > 0 && rpv[0] == '~' { @@ -1213,9 +1234,9 @@ func queryMatches(rp *arrayPathResult, value Result) bool { case ">=": return value.Str >= rpv case "%": - return match.Match(value.Str, rpv) + return matchLimit(value.Str, rpv) case "!%": - return !match.Match(value.Str, rpv) + return !matchLimit(value.Str, rpv) } case Number: rpvn, _ := strconv.ParseFloat(rpv, 64) @@ -1348,6 +1369,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { } else { ch = c.json[i] } + var num bool switch ch { default: continue @@ -1430,26 +1452,13 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { return i, true } } - case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - i, val = parseNumber(c.json, i) - if rp.query.on { - var qval Result - qval.Raw = val - qval.Type = Number - qval.Num, _ = strconv.ParseFloat(val, 64) - if procQuery(qval) { - return i, true - } - } else if hit { - if rp.alogok { - break - } - c.value.Raw = val - c.value.Type = Number - c.value.Num, _ = strconv.ParseFloat(val, 64) - return i, true + case 'n': + if i+1 < len(c.json) && c.json[i+1] != 'u' { + num = true + break } - case 't', 'f', 'n': + fallthrough + case 't', 'f': vc := c.json[i] i, val = parseLiteral(c.json, i) if rp.query.on { @@ -1477,6 +1486,9 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { } return i, true } + case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'i', 'I', 'N': + num = true case ']': if rp.arrch && rp.part == "#" { if rp.alogok { @@ -1552,6 +1564,26 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { } return i + 1, false } + if num { + i, val = parseNumber(c.json, i) + if rp.query.on { + var qval Result + qval.Raw = val + qval.Type = Number + qval.Num, _ = strconv.ParseFloat(val, 64) + if procQuery(qval) { + return i, true + } + } else if hit { + if rp.alogok { + break + } + c.value.Raw = val + c.value.Type = Number + c.value.Num, _ = strconv.ParseFloat(val, 64) + return i, true + } + } break } } @@ -2071,6 +2103,7 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) { if json[i] <= ' ' { continue } + var num bool switch json[i] { case '"': i++ @@ -2090,15 +2123,13 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) { } } return i, res, true - case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - i, val = parseNumber(json, i) - if hit { - res.Raw = val - res.Type = Number - res.Num, _ = strconv.ParseFloat(val, 64) + case 'n': + if i+1 < len(json) && json[i+1] != 'u' { + num = true + break } - return i, res, true - case 't', 'f', 'n': + fallthrough + case 't', 'f': vc := json[i] i, val = parseLiteral(json, i) if hit { @@ -2111,7 +2142,20 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) { } return i, res, true } + case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'i', 'I', 'N': + num = true + } + if num { + i, val = parseNumber(json, i) + if hit { + res.Raw = val + res.Type = Number + res.Num, _ = strconv.ParseFloat(val, 64) + } + return i, res, true } + } return i, res, false } @@ -2555,6 +2599,8 @@ var modifiers = map[string]func(json, arg string) string{ "flatten": modFlatten, "join": modJoin, "valid": modValid, + "keys": modKeys, + "values": modValues, } // AddModifier binds a custom modifier command to the GJSON syntax. @@ -2711,6 +2757,58 @@ func modFlatten(json, arg string) string { return bytesString(out) } +// @keys extracts the keys from an object. +// {"first":"Tom","last":"Smith"} -> ["first","last"] +func modKeys(json, arg string) string { + v := Parse(json) + if !v.Exists() { + return "[]" + } + obj := v.IsObject() + var out strings.Builder + out.WriteByte('[') + var i int + v.ForEach(func(key, _ Result) bool { + if i > 0 { + out.WriteByte(',') + } + if obj { + out.WriteString(key.Raw) + } else { + out.WriteString("null") + } + i++ + return true + }) + out.WriteByte(']') + return out.String() +} + +// @values extracts the values from an object. +// {"first":"Tom","last":"Smith"} -> ["Tom","Smith"] +func modValues(json, arg string) string { + v := Parse(json) + if !v.Exists() { + return "[]" + } + if v.IsArray() { + return json + } + var out strings.Builder + out.WriteByte('[') + var i int + v.ForEach(func(_, value Result) bool { + if i > 0 { + out.WriteByte(',') + } + out.WriteString(value.Raw) + i++ + return true + }) + out.WriteByte(']') + return out.String() +} + // @join multiple objects into a single object. // [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"} // The arg can be "true" to specify that duplicate keys should be preserved. diff --git a/vendor/github.com/tidwall/gjson/go.mod b/vendor/github.com/tidwall/gjson/go.mod index 30ed7f0c01f..6f64083623f 100644 --- a/vendor/github.com/tidwall/gjson/go.mod +++ b/vendor/github.com/tidwall/gjson/go.mod @@ -3,6 +3,6 @@ module github.com/tidwall/gjson go 1.12 require ( - github.com/tidwall/match v1.1.0 + github.com/tidwall/match v1.1.1 github.com/tidwall/pretty v1.2.0 ) diff --git a/vendor/github.com/tidwall/gjson/go.sum b/vendor/github.com/tidwall/gjson/go.sum index f4dcaab68ad..be39c8c11a0 100644 --- a/vendor/github.com/tidwall/gjson/go.sum +++ b/vendor/github.com/tidwall/gjson/go.sum @@ -1,4 +1,4 @@ -github.com/tidwall/match v1.1.0 h1:VfI2e2aXLvytih7WUVyO9uvRC+RcXlaTrMbHuQWnFmk= -github.com/tidwall/match v1.1.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= diff --git a/vendor/github.com/tidwall/match/match.go b/vendor/github.com/tidwall/match/match.go index 499321da6a3..11da28f1b98 100644 --- a/vendor/github.com/tidwall/match/match.go +++ b/vendor/github.com/tidwall/match/match.go @@ -21,10 +21,47 @@ func Match(str, pattern string) bool { if pattern == "*" { return true } - return match(str, pattern) + return match(str, pattern, 0, nil, -1) == rMatch } -func match(str, pat string) bool { +// MatchLimit is the same as Match but will limit the complexity of the match +// operation. This is to avoid long running matches, specifically to avoid ReDos +// attacks from arbritary inputs. +// +// How it works: +// The underlying match routine is recursive and may call itself when it +// encounters a sandwiched wildcard pattern, such as: `user:*:name`. +// Everytime it calls itself a counter is incremented. +// The operation is stopped when counter > maxcomp*len(str). +func MatchLimit(str, pattern string, maxcomp int) (matched, stopped bool) { + if pattern == "*" { + return true, false + } + counter := 0 + r := match(str, pattern, len(str), &counter, maxcomp) + if r == rStop { + return false, true + } + return r == rMatch, false +} + +type result int + +const ( + rNoMatch result = iota + rMatch + rStop +) + +func match(str, pat string, slen int, counter *int, maxcomp int) result { + // check complexity limit + if maxcomp > -1 { + if *counter > slen*maxcomp { + return rStop + } + *counter++ + } + for len(pat) > 0 { var wild bool pc, ps := rune(pat[0]), 1 @@ -42,7 +79,7 @@ func match(str, pat string) bool { switch pc { case '?': if ss == 0 { - return false + return rNoMatch } case '*': // Ignore repeating stars. @@ -50,39 +87,45 @@ func match(str, pat string) bool { pat = pat[1:] } - // If this is the last character then it must be a match. + // If this star is the last character then it must be a match. if len(pat) == 1 { - return true + return rMatch } // Match and trim any non-wildcard suffix characters. var ok bool str, pat, ok = matchTrimSuffix(str, pat) if !ok { - return false + return rNoMatch } - // perform recursive wildcard search - if match(str, pat[1:]) { - return true + // Check for single star again. + if len(pat) == 1 { + return rMatch + } + + // Perform recursive wildcard search. + r := match(str, pat[1:], slen, counter, maxcomp) + if r != rNoMatch { + return r } if len(str) == 0 { - return false + return rNoMatch } wild = true default: if ss == 0 { - return false + return rNoMatch } if pc == '\\' { pat = pat[ps:] pc, ps = utf8.DecodeRuneInString(pat) if ps == 0 { - return false + return rNoMatch } } if sc != pc { - return false + return rNoMatch } } str = str[ss:] @@ -90,7 +133,10 @@ func match(str, pat string) bool { pat = pat[ps:] } } - return len(str) == 0 + if len(str) == 0 { + return rMatch + } + return rNoMatch } // matchTrimSuffix matches and trims any non-wildcard suffix characters. diff --git a/vendor/modules.txt b/vendor/modules.txt index 24edb30c92a..6e783a5acf4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -147,10 +147,10 @@ github.com/spf13/pflag ## explicit github.com/stretchr/testify/assert github.com/stretchr/testify/require -# github.com/tidwall/gjson v1.9.2 +# github.com/tidwall/gjson v1.10.2 ## explicit github.com/tidwall/gjson -# github.com/tidwall/match v1.1.0 +# github.com/tidwall/match v1.1.1 github.com/tidwall/match # github.com/tidwall/pretty v1.2.0 ## explicit