mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
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 @caesarxuchao
This commit is contained in:
commit
756a814042
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package jsonpath
|
package jsonpath
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -42,6 +43,8 @@ type Parser struct {
|
|||||||
width int
|
width int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ErrSyntax = errors.New("invalid syntax")
|
||||||
|
|
||||||
// Parse parsed the given text and return a node Parser.
|
// Parse parsed the given text and return a node Parser.
|
||||||
// If an error is encountered, parsing stops and an empty
|
// If an error is encountered, parsing stops and an empty
|
||||||
// Parser is returned with the error
|
// Parser is returned with the error
|
||||||
@ -159,8 +162,8 @@ func (p *Parser) parseInsideAction(cur *ListNode) error {
|
|||||||
p.consumeText()
|
p.consumeText()
|
||||||
case r == '[':
|
case r == '[':
|
||||||
return p.parseArray(cur)
|
return p.parseArray(cur)
|
||||||
case r == '"':
|
case r == '"' || r == '\'':
|
||||||
return p.parseQuote(cur)
|
return p.parseQuote(cur, r)
|
||||||
case r == '.':
|
case r == '.':
|
||||||
return p.parseField(cur)
|
return p.parseField(cur)
|
||||||
case r == '+' || r == '-' || unicode.IsDigit(r):
|
case r == '+' || r == '-' || unicode.IsDigit(r):
|
||||||
@ -334,15 +337,35 @@ Loop:
|
|||||||
func (p *Parser) parseFilter(cur *ListNode) error {
|
func (p *Parser) parseFilter(cur *ListNode) error {
|
||||||
p.pos += len("[?(")
|
p.pos += len("[?(")
|
||||||
p.consumeText()
|
p.consumeText()
|
||||||
|
begin := false
|
||||||
|
end := false
|
||||||
|
var pair rune
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
for {
|
for {
|
||||||
switch p.next() {
|
r := p.next()
|
||||||
|
switch r {
|
||||||
case eof, '\n':
|
case eof, '\n':
|
||||||
return fmt.Errorf("unterminated filter")
|
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 ')':
|
case ')':
|
||||||
|
//in rightParser below quotes only appear zero or once
|
||||||
|
//and must be paired at the beginning and end
|
||||||
|
if begin == end {
|
||||||
break Loop
|
break Loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if p.next() != ']' {
|
if p.next() != ']' {
|
||||||
return fmt.Errorf("unclosed array expect ]")
|
return fmt.Errorf("unclosed array expect ]")
|
||||||
}
|
}
|
||||||
@ -370,19 +393,22 @@ Loop:
|
|||||||
return p.parseInsideAction(cur)
|
return p.parseInsideAction(cur)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseQuote unquotes string inside double quote
|
// parseQuote unquotes string inside double or single quote
|
||||||
func (p *Parser) parseQuote(cur *ListNode) error {
|
func (p *Parser) parseQuote(cur *ListNode, end rune) error {
|
||||||
Loop:
|
Loop:
|
||||||
for {
|
for {
|
||||||
switch p.next() {
|
switch p.next() {
|
||||||
case eof, '\n':
|
case eof, '\n':
|
||||||
return fmt.Errorf("unterminated quoted string")
|
return fmt.Errorf("unterminated quoted string")
|
||||||
case '"':
|
case end:
|
||||||
|
//if it's not escape break the Loop
|
||||||
|
if p.input[p.pos-2] != '\\' {
|
||||||
break Loop
|
break Loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
value := p.consumeText()
|
value := p.consumeText()
|
||||||
s, err := strconv.Unquote(value)
|
s, err := UnquoteExtend(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unquote string %s error %v", value, err)
|
return fmt.Errorf("unquote string %s error %v", value, err)
|
||||||
}
|
}
|
||||||
@ -447,3 +473,51 @@ func isAlphaNumeric(r rune) bool {
|
|||||||
func isBool(s string) bool {
|
func isBool(s string) bool {
|
||||||
return s == "true" || s == "false"
|
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"),
|
newList(), newIdentifier("end"),
|
||||||
}, false},
|
}, false},
|
||||||
{"malformat input", `{\\\}`, []Node{}, true},
|
{"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 {
|
func collectNode(nodes []Node, cur Node) []Node {
|
||||||
|
Loading…
Reference in New Issue
Block a user