mirror of https://github.com/k3s-io/k3s
Merge pull request #43674 from shiywang/fixjsonpath
Automatic merge from submit-queue (batch tested with PRs 48538, 43674) fix JSONPath parser will not filter strings containing parentheses Fixes https://github.com/kubernetes/client-go/issues/158 @mtaufen @daizuozhuo @caesarxuchaopull/6/head
commit
756a814042
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package jsonpath
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
@ -42,6 +43,8 @@ type Parser struct {
|
|||
width int
|
||||
}
|
||||
|
||||
var ErrSyntax = errors.New("invalid syntax")
|
||||
|
||||
// Parse parsed the given text and return a node Parser.
|
||||
// If an error is encountered, parsing stops and an empty
|
||||
// Parser is returned with the error
|
||||
|
@ -159,8 +162,8 @@ func (p *Parser) parseInsideAction(cur *ListNode) error {
|
|||
p.consumeText()
|
||||
case r == '[':
|
||||
return p.parseArray(cur)
|
||||
case r == '"':
|
||||
return p.parseQuote(cur)
|
||||
case r == '"' || r == '\'':
|
||||
return p.parseQuote(cur, r)
|
||||
case r == '.':
|
||||
return p.parseField(cur)
|
||||
case r == '+' || r == '-' || unicode.IsDigit(r):
|
||||
|
@ -334,13 +337,33 @@ Loop:
|
|||
func (p *Parser) parseFilter(cur *ListNode) error {
|
||||
p.pos += len("[?(")
|
||||
p.consumeText()
|
||||
begin := false
|
||||
end := false
|
||||
var pair rune
|
||||
|
||||
Loop:
|
||||
for {
|
||||
switch p.next() {
|
||||
r := p.next()
|
||||
switch r {
|
||||
case eof, '\n':
|
||||
return fmt.Errorf("unterminated filter")
|
||||
case '"', '\'':
|
||||
if begin == false {
|
||||
//save the paired rune
|
||||
begin = true
|
||||
pair = r
|
||||
continue
|
||||
}
|
||||
//only add when met paired rune
|
||||
if p.input[p.pos-2] != '\\' && r == pair {
|
||||
end = true
|
||||
}
|
||||
case ')':
|
||||
break Loop
|
||||
//in rightParser below quotes only appear zero or once
|
||||
//and must be paired at the beginning and end
|
||||
if begin == end {
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}
|
||||
if p.next() != ']' {
|
||||
|
@ -370,19 +393,22 @@ Loop:
|
|||
return p.parseInsideAction(cur)
|
||||
}
|
||||
|
||||
// parseQuote unquotes string inside double quote
|
||||
func (p *Parser) parseQuote(cur *ListNode) error {
|
||||
// parseQuote unquotes string inside double or single quote
|
||||
func (p *Parser) parseQuote(cur *ListNode, end rune) error {
|
||||
Loop:
|
||||
for {
|
||||
switch p.next() {
|
||||
case eof, '\n':
|
||||
return fmt.Errorf("unterminated quoted string")
|
||||
case '"':
|
||||
break Loop
|
||||
case end:
|
||||
//if it's not escape break the Loop
|
||||
if p.input[p.pos-2] != '\\' {
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}
|
||||
value := p.consumeText()
|
||||
s, err := strconv.Unquote(value)
|
||||
s, err := UnquoteExtend(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unquote string %s error %v", value, err)
|
||||
}
|
||||
|
@ -447,3 +473,51 @@ func isAlphaNumeric(r rune) bool {
|
|||
func isBool(s string) bool {
|
||||
return s == "true" || s == "false"
|
||||
}
|
||||
|
||||
//UnquoteExtend is almost same as strconv.Unquote(), but it support parse single quotes as a string
|
||||
func UnquoteExtend(s string) (string, error) {
|
||||
n := len(s)
|
||||
if n < 2 {
|
||||
return "", ErrSyntax
|
||||
}
|
||||
quote := s[0]
|
||||
if quote != s[n-1] {
|
||||
return "", ErrSyntax
|
||||
}
|
||||
s = s[1 : n-1]
|
||||
|
||||
if quote != '"' && quote != '\'' {
|
||||
return "", ErrSyntax
|
||||
}
|
||||
|
||||
// Is it trivial? Avoid allocation.
|
||||
if !contains(s, '\\') && !contains(s, quote) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var runeTmp [utf8.UTFMax]byte
|
||||
buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations.
|
||||
for len(s) > 0 {
|
||||
c, multibyte, ss, err := strconv.UnquoteChar(s, quote)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s = ss
|
||||
if c < utf8.RuneSelf || !multibyte {
|
||||
buf = append(buf, byte(c))
|
||||
} else {
|
||||
n := utf8.EncodeRune(runeTmp[:], c)
|
||||
buf = append(buf, runeTmp[:n]...)
|
||||
}
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
func contains(s string, c byte) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -61,6 +61,22 @@ var parserTests = []parserTest{
|
|||
newList(), newIdentifier("end"),
|
||||
}, false},
|
||||
{"malformat input", `{\\\}`, []Node{}, true},
|
||||
{"paired parentheses in quotes", `{[?(@.status.nodeInfo.osImage == "()")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("()")}, false},
|
||||
{"paired parentheses in double quotes and with double quotes escape", `{[?(@.status.nodeInfo.osImage == "(\"\")")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("(\"\")")}, false},
|
||||
{"unregular parentheses in double quotes", `{[?(@.test == "())(")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("test"), newList(), newText("())(")}, false},
|
||||
{"plain text in single quotes", `{[?(@.status.nodeInfo.osImage == 'Linux')]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("Linux")}, false},
|
||||
{"test filter suffix", `{[?(@.status.nodeInfo.osImage == "{[()]}")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("{[()]}")}, false},
|
||||
{"double inside single", `{[?(@.status.nodeInfo.osImage == "''")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("''")}, false},
|
||||
{"single inside double", `{[?(@.status.nodeInfo.osImage == '""')]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("\"\"")}, false},
|
||||
{"single containing escaped single", `{[?(@.status.nodeInfo.osImage == '\\\'')]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("\\'")}, false},
|
||||
}
|
||||
|
||||
func collectNode(nodes []Node, cur Node) []Node {
|
||||
|
|
Loading…
Reference in New Issue