Bump github.com/tidwall/gjson from 1.8.1 to 1.9.3 (#1906)

Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.8.1 to 1.9.3.
- [Release notes](https://github.com/tidwall/gjson/releases)
- [Commits](https://github.com/tidwall/gjson/compare/v1.8.1...v1.9.3)

---
updated-dependencies:
- dependency-name: github.com/tidwall/gjson
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2021-11-01 11:31:28 +11:00 committed by GitHub
parent 0dbee117d8
commit 32db1dda57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 206 additions and 131 deletions

6
go.mod
View File

@ -32,9 +32,8 @@ require (
github.com/spf13/pflag v1.0.3 github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.7.0 github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
github.com/tidwall/gjson v1.8.1 github.com/tidwall/gjson v1.9.3
github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect
github.com/vektah/gqlparser/v2 v2.0.1
github.com/vektra/mockery/v2 v2.2.1 github.com/vektra/mockery/v2 v2.2.1
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
@ -83,9 +82,10 @@ require (
github.com/spf13/jwalterweatherman v1.0.0 // indirect github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/objx v0.2.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect github.com/subosito/gotenv v1.2.0 // indirect
github.com/tidwall/match v1.0.3 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/urfave/cli/v2 v2.1.1 // indirect github.com/urfave/cli/v2 v2.1.1 // indirect
github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e // indirect github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e // indirect
github.com/vektah/gqlparser/v2 v2.0.1 // indirect
go.uber.org/atomic v1.6.0 // indirect go.uber.org/atomic v1.6.0 // indirect
golang.org/x/mod v0.4.1 // indirect golang.org/x/mod v0.4.1 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect

9
go.sum
View File

@ -652,12 +652,11 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= github.com/tidwall/gjson v1.9.3 h1:hqzS9wAHMO+KVBBkLxYdkEeeFHuqr95GfClRLKlgK0E=
github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=

View File

@ -123,11 +123,12 @@ nil, for JSON null
To directly access the value: To directly access the value:
```go ```go
result.Type // can be String, Number, True, False, Null, or JSON result.Type // can be String, Number, True, False, Null, or JSON
result.Str // holds the string result.Str // holds the string
result.Num // holds the float64 number result.Num // holds the float64 number
result.Raw // holds the raw json result.Raw // holds the raw json
result.Index // index of raw value in original json, zero means index unknown result.Index // index of raw value in original json, zero means index unknown
result.Indexes // indexes of all the elements that match on a path containing the '#' query character.
``` ```
There are a variety of handy functions that work on a result: There are a variety of handy functions that work on a result:

View File

@ -291,8 +291,8 @@ gjson.AddModifier("case", func(json, arg string) string {
### Multipaths ### Multipaths
Starting with v1.3.0, GJSON added the ability to join multiple paths together Starting with v1.3.0, GJSON added the ability to join multiple paths together
to form new documents. Wrapping comma-separated paths between `{...}` or to form new documents. Wrapping comma-separated paths between `[...]` or
`[...]` will result in a new array or object, respectively. `{...}` will result in a new array or object, respectively.
For example, using the given multipath For example, using the given multipath

View File

@ -64,6 +64,9 @@ type Result struct {
Num float64 Num float64
// Index of raw value in original json, zero means index unknown // Index of raw value in original json, zero means index unknown
Index int Index int
// Indexes of all the elements that match on a path containing the '#'
// query character.
Indexes []int
} }
// String returns a string representation of the value. // String returns a string representation of the value.
@ -281,7 +284,8 @@ func (t Result) ForEach(iterator func(key, value Result) bool) {
} }
} }
// Map returns back an map of values. The result should be a JSON array. // Map returns back a map of values. The result should be a JSON object.
// If the result is not a JSON object, the return value will be an empty map.
func (t Result) Map() map[string]Result { func (t Result) Map() map[string]Result {
if t.Type != JSON { if t.Type != JSON {
return map[string]Result{} return map[string]Result{}
@ -1085,9 +1089,9 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
} }
if rp.wild { if rp.wild {
if kesc { if kesc {
pmatch = match.Match(unescape(key), rp.part) pmatch = matchLimit(unescape(key), rp.part)
} else { } else {
pmatch = match.Match(key, rp.part) pmatch = matchLimit(key, rp.part)
} }
} else { } else {
if kesc { if kesc {
@ -1172,6 +1176,15 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
} }
return i, false 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 { func queryMatches(rp *arrayPathResult, value Result) bool {
rpv := rp.query.value rpv := rp.query.value
if len(rpv) > 0 && rpv[0] == '~' { if len(rpv) > 0 && rpv[0] == '~' {
@ -1209,9 +1222,9 @@ func queryMatches(rp *arrayPathResult, value Result) bool {
case ">=": case ">=":
return value.Str >= rpv return value.Str >= rpv
case "%": case "%":
return match.Match(value.Str, rpv) return matchLimit(value.Str, rpv)
case "!%": case "!%":
return !match.Match(value.Str, rpv) return !matchLimit(value.Str, rpv)
} }
case Number: case Number:
rpvn, _ := strconv.ParseFloat(rpv, 64) rpvn, _ := strconv.ParseFloat(rpv, 64)
@ -1261,6 +1274,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
var alog []int var alog []int
var partidx int var partidx int
var multires []byte var multires []byte
var queryIndexes []int
rp := parseArrayPath(path) rp := parseArrayPath(path)
if !rp.arrch { if !rp.arrch {
n, ok := parseUint(rp.part) n, ok := parseUint(rp.part)
@ -1281,6 +1295,10 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
multires = append(multires, '[') multires = append(multires, '[')
} }
} }
var tmp parseContext
tmp.value = qval
fillIndex(c.json, &tmp)
parentIndex := tmp.value.Index
var res Result var res Result
if qval.Type == JSON { if qval.Type == JSON {
res = qval.Get(rp.query.path) res = qval.Get(rp.query.path)
@ -1312,6 +1330,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
multires = append(multires, ',') multires = append(multires, ',')
} }
multires = append(multires, raw...) multires = append(multires, raw...)
queryIndexes = append(queryIndexes, res.Index+parentIndex)
} }
} else { } else {
c.value = res c.value = res
@ -1476,6 +1495,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
c.pipe = right c.pipe = right
c.piped = true c.piped = true
} }
var indexes = make([]int, 0, 64)
var jsons = make([]byte, 0, 64) var jsons = make([]byte, 0, 64)
jsons = append(jsons, '[') jsons = append(jsons, '[')
for j, k := 0, 0; j < len(alog); j++ { for j, k := 0, 0; j < len(alog); j++ {
@ -1490,6 +1510,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
} }
if idx < len(c.json) && c.json[idx] != ']' { if idx < len(c.json) && c.json[idx] != ']' {
_, res, ok := parseAny(c.json, idx, true) _, res, ok := parseAny(c.json, idx, true)
parentIndex := res.Index
if ok { if ok {
res := res.Get(rp.alogkey) res := res.Get(rp.alogkey)
if res.Exists() { if res.Exists() {
@ -1501,6 +1522,8 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
raw = res.String() raw = res.String()
} }
jsons = append(jsons, []byte(raw)...) jsons = append(jsons, []byte(raw)...)
indexes = append(indexes,
res.Index+parentIndex)
k++ k++
} }
} }
@ -1509,6 +1532,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
jsons = append(jsons, ']') jsons = append(jsons, ']')
c.value.Type = JSON c.value.Type = JSON
c.value.Raw = string(jsons) c.value.Raw = string(jsons)
c.value.Indexes = indexes
return i + 1, true return i + 1, true
} }
if rp.alogok { if rp.alogok {
@ -1524,8 +1548,9 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
if !c.value.Exists() { if !c.value.Exists() {
if len(multires) > 0 { if len(multires) > 0 {
c.value = Result{ c.value = Result{
Raw: string(append(multires, ']')), Raw: string(append(multires, ']')),
Type: JSON, Type: JSON,
Indexes: queryIndexes,
} }
} else if rp.query.all { } else if rp.query.all {
c.value = Result{ c.value = Result{
@ -1806,6 +1831,7 @@ func Get(json, path string) Result {
if len(path) > 0 && (path[0] == '|' || path[0] == '.') { if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
res := Get(rjson, path[1:]) res := Get(rjson, path[1:])
res.Index = 0 res.Index = 0
res.Indexes = nil
return res return res
} }
return Parse(rjson) return Parse(rjson)
@ -2046,7 +2072,10 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) {
res.Raw = val res.Raw = val
res.Type = JSON res.Type = JSON
} }
return i, res, true var tmp parseContext
tmp.value = res
fillIndex(json, &tmp)
return i, tmp.value, true
} }
if json[i] <= ' ' { if json[i] <= ' ' {
continue continue
@ -2455,7 +2484,8 @@ func parseInt(s string) (n int64, ok bool) {
// safeInt validates a given JSON number // safeInt validates a given JSON number
// ensures it lies within the minimum and maximum representable JSON numbers // ensures it lies within the minimum and maximum representable JSON numbers
func safeInt(f float64) (n int64, ok bool) { func safeInt(f float64) (n int64, ok bool) {
// https://tc39.es/ecma262/#sec-number.min_safe_integer || https://tc39.es/ecma262/#sec-number.max_safe_integer // https://tc39.es/ecma262/#sec-number.min_safe_integer
// https://tc39.es/ecma262/#sec-number.max_safe_integer
if f < -9007199254740991 || f > 9007199254740991 { if f < -9007199254740991 || f > 9007199254740991 {
return 0, false return 0, false
} }

View File

@ -1,7 +1,9 @@
// Package match provides a simple pattern matcher with unicode support. // Package match provides a simple pattern matcher with unicode support.
package match package match
import "unicode/utf8" import (
"unicode/utf8"
)
// Match returns true if str matches pattern. This is a very // Match returns true if str matches pattern. This is a very
// simple wildcard match where '*' matches on any number characters // simple wildcard match where '*' matches on any number characters
@ -16,127 +18,170 @@ import "unicode/utf8"
// '\\' c matches character c // '\\' c matches character c
// //
func Match(str, pattern string) bool { func Match(str, pattern string) bool {
return deepMatch(str, pattern)
}
func deepMatch(str, pattern string) bool {
if pattern == "*" { if pattern == "*" {
return true return true
} }
for len(pattern) > 1 && pattern[0] == '*' && pattern[1] == '*' { return match(str, pattern, 0, nil, -1) == rMatch
pattern = pattern[1:]
}
for len(pattern) > 0 {
if pattern[0] > 0x7f {
return deepMatchRune(str, pattern)
}
switch pattern[0] {
default:
if len(str) == 0 {
return false
}
if str[0] > 0x7f {
return deepMatchRune(str, pattern)
}
if str[0] != pattern[0] {
return false
}
case '?':
if len(str) == 0 {
return false
}
case '*':
return deepMatch(str, pattern[1:]) ||
(len(str) > 0 && deepMatch(str[1:], pattern))
}
str = str[1:]
pattern = pattern[1:]
}
return len(str) == 0 && len(pattern) == 0
} }
func deepMatchRune(str, pattern 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 == "*" { if pattern == "*" {
return true return true, false
} }
for len(pattern) > 1 && pattern[0] == '*' && pattern[1] == '*' { counter := 0
pattern = pattern[1:] 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++
} }
var sr, pr rune for len(pat) > 0 {
var srsz, prsz int var wild bool
pc, ps := rune(pat[0]), 1
// read the first rune ahead of time if pc > 0x7f {
if len(str) > 0 { pc, ps = utf8.DecodeRuneInString(pat)
if str[0] > 0x7f {
sr, srsz = utf8.DecodeRuneInString(str)
} else {
sr, srsz = rune(str[0]), 1
} }
} else { var sc rune
sr, srsz = utf8.RuneError, 0 var ss int
}
if len(pattern) > 0 {
if pattern[0] > 0x7f {
pr, prsz = utf8.DecodeRuneInString(pattern)
} else {
pr, prsz = rune(pattern[0]), 1
}
} else {
pr, prsz = utf8.RuneError, 0
}
// done reading
for pr != utf8.RuneError {
switch pr {
default:
if srsz == utf8.RuneError {
return false
}
if sr != pr {
return false
}
case '?':
if srsz == utf8.RuneError {
return false
}
case '*':
return deepMatchRune(str, pattern[prsz:]) ||
(srsz > 0 && deepMatchRune(str[srsz:], pattern))
}
str = str[srsz:]
pattern = pattern[prsz:]
// read the next runes
if len(str) > 0 { if len(str) > 0 {
if str[0] > 0x7f { sc, ss = rune(str[0]), 1
sr, srsz = utf8.DecodeRuneInString(str) if sc > 0x7f {
} else { sc, ss = utf8.DecodeRuneInString(str)
sr, srsz = rune(str[0]), 1
} }
} else {
sr, srsz = utf8.RuneError, 0
} }
if len(pattern) > 0 { switch pc {
if pattern[0] > 0x7f { case '?':
pr, prsz = utf8.DecodeRuneInString(pattern) if ss == 0 {
} else { return rNoMatch
pr, prsz = rune(pattern[0]), 1 }
case '*':
// Ignore repeating stars.
for len(pat) > 1 && pat[1] == '*' {
pat = pat[1:]
} }
} else {
pr, prsz = utf8.RuneError, 0
}
// done reading
}
return srsz == 0 && prsz == 0 // If this star is the last character then it must be a match.
if len(pat) == 1 {
return rMatch
}
// Match and trim any non-wildcard suffix characters.
var ok bool
str, pat, ok = matchTrimSuffix(str, pat)
if !ok {
return rNoMatch
}
// 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 rNoMatch
}
wild = true
default:
if ss == 0 {
return rNoMatch
}
if pc == '\\' {
pat = pat[ps:]
pc, ps = utf8.DecodeRuneInString(pat)
if ps == 0 {
return rNoMatch
}
}
if sc != pc {
return rNoMatch
}
}
str = str[ss:]
if !wild {
pat = pat[ps:]
}
}
if len(str) == 0 {
return rMatch
}
return rNoMatch
} }
var maxRuneBytes = func() []byte { // matchTrimSuffix matches and trims any non-wildcard suffix characters.
b := make([]byte, 4) // Returns the trimed string and pattern.
if utf8.EncodeRune(b, '\U0010FFFF') != 4 { //
panic("invalid rune encoding") // This is called because the pattern contains extra data after the wildcard
// star. Here we compare any suffix characters in the pattern to the suffix of
// the target string. Basically a reverse match that stops when a wildcard
// character is reached. This is a little trickier than a forward match because
// we need to evaluate an escaped character in reverse.
//
// Any matched characters will be trimmed from both the target
// string and the pattern.
func matchTrimSuffix(str, pat string) (string, string, bool) {
// It's expected that the pattern has at least two bytes and the first byte
// is a wildcard star '*'
match := true
for len(str) > 0 && len(pat) > 1 {
pc, ps := utf8.DecodeLastRuneInString(pat)
var esc bool
for i := 0; ; i++ {
if pat[len(pat)-ps-i-1] != '\\' {
if i&1 == 1 {
esc = true
ps++
}
break
}
}
if pc == '*' && !esc {
match = true
break
}
sc, ss := utf8.DecodeLastRuneInString(str)
if !((pc == '?' && !esc) || pc == sc) {
match = false
break
}
str = str[:len(str)-ss]
pat = pat[:len(pat)-ps]
} }
return b return str, pat, match
}() }
var maxRuneBytes = [...]byte{244, 143, 191, 191}
// Allowable parses the pattern and determines the minimum and maximum allowable // Allowable parses the pattern and determines the minimum and maximum allowable
// values that the pattern can represent. // values that the pattern can represent.
@ -157,7 +202,7 @@ func Allowable(pattern string) (min, max string) {
} }
if pattern[i] == '?' { if pattern[i] == '?' {
minb = append(minb, 0) minb = append(minb, 0)
maxb = append(maxb, maxRuneBytes...) maxb = append(maxb, maxRuneBytes[:]...)
} else { } else {
minb = append(minb, pattern[i]) minb = append(minb, pattern[i])
maxb = append(maxb, pattern[i]) maxb = append(maxb, pattern[i])

4
vendor/modules.txt vendored
View File

@ -322,10 +322,10 @@ github.com/stretchr/testify/mock
# github.com/subosito/gotenv v1.2.0 # github.com/subosito/gotenv v1.2.0
## explicit ## explicit
github.com/subosito/gotenv github.com/subosito/gotenv
# github.com/tidwall/gjson v1.8.1 # github.com/tidwall/gjson v1.9.3
## explicit; go 1.12 ## explicit; go 1.12
github.com/tidwall/gjson github.com/tidwall/gjson
# github.com/tidwall/match v1.0.3 # github.com/tidwall/match v1.1.1
## explicit; go 1.15 ## explicit; go 1.15
github.com/tidwall/match github.com/tidwall/match
# github.com/tidwall/pretty v1.2.0 # github.com/tidwall/pretty v1.2.0