From d15fda500028c05fde2aa28ea53868eec01140f6 Mon Sep 17 00:00:00 2001 From: Cameron Moore Date: Tue, 29 Mar 2016 20:50:54 -0500 Subject: [PATCH] tpl: Fix panic in pairList.Less While sorting on data sources with missing fields, a panic can occur in pairList.Less if `Interface()` is called on a invalid `reflect.Value`. This commit detects an invalid Value and replacing it with a zero value for the comparison. --- tpl/template_funcs.go | 19 ++++++++++++++++++- tpl/template_funcs_test.go | 15 +++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/tpl/template_funcs.go b/tpl/template_funcs.go index e9cc26869..ad5b8db1e 100644 --- a/tpl/template_funcs.go +++ b/tpl/template_funcs.go @@ -1054,7 +1054,24 @@ type pairList struct { func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] } func (p pairList) Len() int { return len(p.Pairs) } func (p pairList) Less(i, j int) bool { - return lt(p.Pairs[i].SortByValue.Interface(), p.Pairs[j].SortByValue.Interface()) + iv := p.Pairs[i].SortByValue + jv := p.Pairs[j].SortByValue + + if iv.IsValid() { + if jv.IsValid() { + // can only call Interface() on valid reflect Values + return lt(iv.Interface(), jv.Interface()) + } + // if j is invalid, test i against i's zero value + return lt(iv.Interface(), reflect.Zero(iv.Type())) + } + + if jv.IsValid() { + // if i is invalid, test j against j's zero value + return lt(reflect.Zero(jv.Type()), jv.Interface()) + } + + return false } // sorts a pairList and returns a slice of sorted values diff --git a/tpl/template_funcs_test.go b/tpl/template_funcs_test.go index a36629afd..4b49da41b 100644 --- a/tpl/template_funcs_test.go +++ b/tpl/template_funcs_test.go @@ -1535,6 +1535,21 @@ func TestSort(t *testing.T) { "asc", []map[string]mid{{"foo": mid{Tst: TstX{A: "a", B: "b"}}}, {"foo": mid{Tst: TstX{A: "c", B: "d"}}}, {"foo": mid{Tst: TstX{A: "e", B: "f"}}}}, }, + // interface slice with missing elements + { + []interface{}{ + map[interface{}]interface{}{"Title": "Foo", "Weight": 10}, + map[interface{}]interface{}{"Title": "Bar"}, + map[interface{}]interface{}{"Title": "Zap", "Weight": 5}, + }, + "Weight", + "asc", + []interface{}{ + map[interface{}]interface{}{"Title": "Bar"}, + map[interface{}]interface{}{"Title": "Zap", "Weight": 5}, + map[interface{}]interface{}{"Title": "Foo", "Weight": 10}, + }, + }, // test error cases {(*[]TstX)(nil), nil, "asc", false}, {TstX{A: "a", B: "b"}, nil, "asc", false},