flatmap: be resilient to lying "foo.#" key

We use the .# key primarily as a hint that we have a list, but its value
describes how many items the writer thinks were in the list.

Since this information is redundant with the _actual_ data, it's
potentially useful as a form of corrupted data detection but this function
isn't equipped to actually report on such a problem (no error return
value, and not in a good place for UI feedback anyway), so instead we'll
largely ignore this value and just go by the number of items we
encounter.

The result of this is that when the counts mismatch we will go by the
number of items actually holding the prefix, rather than panicking as
we would've before.

This fixes the crashes in #15300, #15135 and #15334, though it does not
address any root-cause for the count to be incorrect in the first place,
so there may be something to fix here somewhere else.
This commit is contained in:
Martin Atkins 2017-06-23 13:52:07 -07:00
parent 61b96f0860
commit 606e7c991f
2 changed files with 40 additions and 1 deletions

View File

@ -60,6 +60,11 @@ func expandArray(m map[string]string, prefix string) []interface{} {
return []interface{}{}
}
// NOTE: "num" is not necessarily accurate, e.g. if a user tampers
// with state, so the following code should not crash when given a
// number of items more or less than what's given in num. The
// num key is mainly just a hint that this is a list or set.
// The Schema "Set" type stores its values in an array format, but
// using numeric hash values instead of ordinal keys. Take the set
// of keys regardless of value, and expand them in numeric order.
@ -101,7 +106,7 @@ func expandArray(m map[string]string, prefix string) []interface{} {
}
sort.Ints(keysList)
result := make([]interface{}, num)
result := make([]interface{}, len(keysList))
for i, key := range keysList {
keyString := strconv.Itoa(key)
if computed[keyString] {

View File

@ -35,6 +35,40 @@ func TestExpand(t *testing.T) {
},
},
{
Map: map[string]string{
// # mismatches actual number of keys; actual number should
// "win" here, since the # is just a hint that this is a list.
"foo.#": "1",
"foo.0": "one",
"foo.1": "two",
"foo.2": "three",
},
Key: "foo",
Output: []interface{}{
"one",
"two",
"three",
},
},
{
Map: map[string]string{
// # mismatches actual number of keys; actual number should
// "win" here, since the # is just a hint that this is a list.
"foo.#": "5",
"foo.0": "one",
"foo.1": "two",
"foo.2": "three",
},
Key: "foo",
Output: []interface{}{
"one",
"two",
"three",
},
},
{
Map: map[string]string{
"foo.#": "1",