fix a client-go bug which could casue kubectl panic (#72952)

* When user try execute command like `kubectl get pod test -o custom-columns=CONTAINER:.spec.containers[-1].name`
It will throw a panic about slice index out of bounds. This patch fix it.

* add test case
pull/564/head
WanLinghao 2019-01-19 08:14:20 +08:00 committed by Kubernetes Prow Robot
parent ef2a5b948b
commit 1e245fad87
5 changed files with 169 additions and 7 deletions

View File

@ -255,10 +255,9 @@ func (j *JSONPath) evalArray(input []reflect.Value, node *ArrayNode) ([]reflect.
params[1].Value = value.Len()
}
if params[1].Value < 0 {
if params[1].Value < 0 || (params[1].Value == 0 && params[1].Derived) {
params[1].Value += value.Len()
}
sliceLength := value.Len()
if params[1].Value != params[0].Value { // if you're requesting zero elements, allow it through.
if params[0].Value >= sliceLength || params[0].Value < 0 {
@ -267,6 +266,11 @@ func (j *JSONPath) evalArray(input []reflect.Value, node *ArrayNode) ([]reflect.
if params[1].Value > sliceLength || params[1].Value < 0 {
return input, fmt.Errorf("array index out of bounds: index %d, length %d", params[1].Value-1, sliceLength)
}
if params[0].Value > params[1].Value {
return input, fmt.Errorf("starting index %d is greater than ending index %d", params[0].Value, params[1].Value)
}
} else {
return result, nil
}
if !params[2].Known {

View File

@ -369,3 +369,153 @@ func TestFilterPartialMatchesSometimesMissingAnnotations(t *testing.T) {
t,
)
}
func TestNegativeIndex(t *testing.T) {
var input = []byte(
`{
"apiVersion": "v1",
"kind": "Pod",
"spec": {
"containers": [
{
"image": "radial/busyboxplus:curl",
"name": "fake0"
},
{
"image": "radial/busyboxplus:curl",
"name": "fake1"
},
{
"image": "radial/busyboxplus:curl",
"name": "fake2"
},
{
"image": "radial/busyboxplus:curl",
"name": "fake3"
}]}}`)
var data interface{}
err := json.Unmarshal(input, &data)
if err != nil {
t.Fatal(err)
}
testJSONPath(
[]jsonpathTest{
{
"test containers[0], it equals containers[0]",
`{.spec.containers[0].name}`,
data,
"fake0",
false,
},
{
"test containers[0:0], it equals the empty set",
`{.spec.containers[0:0].name}`,
data,
"",
false,
},
{
"test containers[0:-1], it equals containers[0:3]",
`{.spec.containers[0:-1].name}`,
data,
"fake0 fake1 fake2",
false,
},
{
"test containers[-1:0], expect error",
`{.spec.containers[-1:0].name}`,
data,
"",
true,
},
{
"test containers[-1], it equals containers[3]",
`{.spec.containers[-1].name}`,
data,
"fake3",
false,
},
{
"test containers[-1:], it equals containers[3:]",
`{.spec.containers[-1:].name}`,
data,
"fake3",
false,
},
{
"test containers[-2], it equals containers[2]",
`{.spec.containers[-2].name}`,
data,
"fake2",
false,
},
{
"test containers[-2:], it equals containers[2:]",
`{.spec.containers[-2:].name}`,
data,
"fake2 fake3",
false,
},
{
"test containers[-3], it equals containers[1]",
`{.spec.containers[-3].name}`,
data,
"fake1",
false,
},
{
"test containers[-4], it equals containers[0]",
`{.spec.containers[-4].name}`,
data,
"fake0",
false,
},
{
"test containers[-4:], it equals containers[0:]",
`{.spec.containers[-4:].name}`,
data,
"fake0 fake1 fake2 fake3",
false,
},
{
"test containers[-5], expect a error cause it out of bounds",
`{.spec.containers[-5].name}`,
data,
"",
true, // expect error
},
{
"test containers[5:5], expect empty set",
`{.spec.containers[5:5].name}`,
data,
"",
false,
},
{
"test containers[-5:-5], expect empty set",
`{.spec.containers[-5:-5].name}`,
data,
"",
false,
},
{
"test containers[3:1], expect a error cause start index is greater than end index",
`{.spec.containers[3:1].name}`,
data,
"",
true,
},
{
"test containers[-1:-2], it equals containers[3:2], expect a error cause start index is greater than end index",
`{.spec.containers[-1:-2].name}`,
data,
"",
true,
},
},
false,
t,
)
}

View File

@ -130,8 +130,9 @@ func (f *IdentifierNode) String() string {
// ParamsEntry holds param information for ArrayNode
type ParamsEntry struct {
Value int
Known bool // whether the value is known when parse it
Value int
Known bool // whether the value is known when parse it
Derived bool
}
// ArrayNode holds start, end, step information for array index selection

View File

@ -325,6 +325,7 @@ Loop:
if i == 1 {
params[i].Known = true
params[i].Value = params[0].Value + 1
params[i].Derived = true
} else {
params[i].Known = false
params[i].Value = 0

View File

@ -35,10 +35,10 @@ var parserTests = []parserTest{
[]Node{newText("hello "), newList(), newField("jsonpath")}, false},
{"quote", `{"{"}`, []Node{newList(), newText("{")}, false},
{"array", `{[1:3]}`, []Node{newList(),
newArray([3]ParamsEntry{{1, true}, {3, true}, {0, false}})}, false},
newArray([3]ParamsEntry{{1, true, false}, {3, true, false}, {0, false, false}})}, false},
{"allarray", `{.book[*].author}`,
[]Node{newList(), newField("book"),
newArray([3]ParamsEntry{{0, false}, {0, false}, {0, false}}), newField("author")}, false},
newArray([3]ParamsEntry{{0, false, false}, {0, false, false}, {0, false, false}}), newField("author")}, false},
{"wildcard", `{.bicycle.*}`,
[]Node{newList(), newField("bicycle"), newWildcard()}, false},
{"filter", `{[?(@.price<3)]}`,
@ -52,7 +52,7 @@ var parserTests = []parserTest{
}, false},
{"union", `{['bicycle.price', 3, 'book.price']}`, []Node{newList(), newUnion([]*ListNode{}),
newList(), newField("bicycle"), newField("price"),
newList(), newArray([3]ParamsEntry{{3, true}, {4, true}, {0, false}}),
newList(), newArray([3]ParamsEntry{{3, true, false}, {4, true, true}, {0, false, false}}),
newList(), newField("book"), newField("price"),
}, false},
{"range", `{range .items}{.name},{end}`, []Node{
@ -77,6 +77,12 @@ var parserTests = []parserTest{
[]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},
{"negative index slice, equals a[len-5] to a[len-1]", `{[-5:]}`, []Node{newList(),
newArray([3]ParamsEntry{{-5, true, false}, {0, false, false}, {0, false, false}})}, false},
{"negative index slice, equals a[len-1]", `{[-1]}`, []Node{newList(),
newArray([3]ParamsEntry{{-1, true, false}, {0, true, true}, {0, false, false}})}, false},
{"negative index slice, equals a[1] to a[len-1]", `{[1:-1]}`, []Node{newList(),
newArray([3]ParamsEntry{{1, true, false}, {-1, true, false}, {0, false, false}})}, false},
}
func collectNode(nodes []Node, cur Node) []Node {