Repository: stretchr/testify (go)

Issue:
Prevent nil pointer dereference
Contains*-Assertions panic when a `nil` instead of a string, slice, map etc. is injected.
Minimal example here:

```go
var myMap map[string]interface{} // empty
// try an assertion on nested keys
// which effectively leads to a passing of nil
require.Contains(t, myMap["some-key"], "expected-nested-key") // <-- panic
```

Assertions should be robust against nil-injection and gracefully fail instead of panicking.

Pull request description:
Fix #1083 (Nil-pointer dereference)
The fix gracefully fails the assertion when given `nil` as list value instead of panicking unresolved.

Fixes #1083 

Patch:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..bf6944e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+version: 2
+updates:
+- package-ecosystem: gomod
+  directory: /
+  schedule:
+    interval: daily
+- package-ecosystem: github-actions
+  directory: /
+  schedule:
+    interval: daily
diff --git a/assert/assertions.go b/assert/assertions.go
index 30742a0..230118b 100644
--- a/assert/assertions.go
+++ b/assert/assertions.go
@@ -718,10 +718,14 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte
 // return (false, false) if impossible.
 // return (true, false) if element was not found.
 // return (true, true) if element was found.
-func includeElement(list interface{}, element interface{}) (ok, found bool) {
+func containsElement(list interface{}, element interface{}) (ok, found bool) {
 
 	listValue := reflect.ValueOf(list)
-	listKind := reflect.TypeOf(list).Kind()
+	listType := reflect.TypeOf(list)
+	if listType == nil {
+		return false, false
+	}
+	listKind := listType.Kind()
 	defer func() {
 		if e := recover(); e != nil {
 			ok = false
@@ -764,7 +768,7 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
 		h.Helper()
 	}
 
-	ok, found := includeElement(s, contains)
+	ok, found := containsElement(s, contains)
 	if !ok {
 		return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
 	}
@@ -787,7 +791,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
 		h.Helper()
 	}
 
-	ok, found := includeElement(s, contains)
+	ok, found := containsElement(s, contains)
 	if !ok {
 		return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
 	}
@@ -831,7 +835,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
 
 	for i := 0; i < subsetValue.Len(); i++ {
 		element := subsetValue.Index(i).Interface()
-		ok, found := includeElement(list, element)
+		ok, found := containsElement(list, element)
 		if !ok {
 			return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
 		}
@@ -852,7 +856,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
 		h.Helper()
 	}
 	if subset == nil {
-		return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...)
+		return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
 	}
 
 	subsetValue := reflect.ValueOf(subset)
@@ -875,7 +879,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
 
 	for i := 0; i < subsetValue.Len(); i++ {
 		element := subsetValue.Index(i).Interface()
-		ok, found := includeElement(list, element)
+		ok, found := containsElement(list, element)
 		if !ok {
 			return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
 		}
@@ -1161,7 +1165,7 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
 	bf, bok := toFloat(actual)
 
 	if !aok || !bok {
-		return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
+		return Fail(t, "Parameters must be numerical", msgAndArgs...)
 	}
 
 	if math.IsNaN(af) && math.IsNaN(bf) {
@@ -1169,7 +1173,7 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs
 	}
 
 	if math.IsNaN(af) {
-		return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...)
+		return Fail(t, "Expected must not be NaN", msgAndArgs...)
 	}
 
 	if math.IsNaN(bf) {
@@ -1192,7 +1196,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
 	if expected == nil || actual == nil ||
 		reflect.TypeOf(actual).Kind() != reflect.Slice ||
 		reflect.TypeOf(expected).Kind() != reflect.Slice {
-		return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
+		return Fail(t, "Parameters must be slice", msgAndArgs...)
 	}
 
 	actualSlice := reflect.ValueOf(actual)
@@ -1302,7 +1306,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
 	if expected == nil || actual == nil ||
 		reflect.TypeOf(actual).Kind() != reflect.Slice ||
 		reflect.TypeOf(expected).Kind() != reflect.Slice {
-		return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
+		return Fail(t, "Parameters must be slice", msgAndArgs...)
 	}
 
 	actualSlice := reflect.ValueOf(actual)
diff --git a/mock/mock.go b/mock/mock.go
index 5d445c6..853da6c 100644
--- a/mock/mock.go
+++ b/mock/mock.go
@@ -728,7 +728,7 @@ func (f argumentMatcher) Matches(argument interface{}) bool {
 }
 
 func (f argumentMatcher) String() string {
-	return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).Name())
+	return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String())
 }
 
 // MatchedBy can be used to match a mock call based on only certain properties


Test patch:
diff --git a/assert/assertion_compare_test.go b/assert/assertion_compare_test.go
index da5a330..6f7db87 100644
--- a/assert/assertion_compare_test.go
+++ b/assert/assertion_compare_test.go
@@ -150,7 +150,7 @@ func TestGreater(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Greater(out, currCase.less, currCase.greater))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Greater")
 	}
 }
@@ -191,7 +191,7 @@ func TestGreaterOrEqual(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, GreaterOrEqual(out, currCase.less, currCase.greater))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.GreaterOrEqual")
 	}
 }
@@ -232,7 +232,7 @@ func TestLess(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Less(out, currCase.greater, currCase.less))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Less")
 	}
 }
@@ -273,7 +273,7 @@ func TestLessOrEqual(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, LessOrEqual(out, currCase.greater, currCase.less))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.LessOrEqual")
 	}
 }
@@ -312,7 +312,7 @@ func TestPositive(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Positive(out, currCase.e))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Positive")
 	}
 }
@@ -351,7 +351,7 @@ func TestNegative(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, Negative(out, currCase.e))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 		Contains(t, out.helpers, "github.com/stretchr/testify/assert.Negative")
 	}
 }
@@ -386,7 +386,7 @@ func Test_compareTwoValuesNotComparableValues(t *testing.T) {
 	}{
 		{v1: CompareStruct{}, v2: CompareStruct{}},
 		{v1: map[string]int{}, v2: map[string]int{}},
-		{v1: make([]int, 5, 5), v2: make([]int, 5, 5)},
+		{v1: make([]int, 5), v2: make([]int, 5)},
 	} {
 		compareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, "testFailMessage")
 		False(t, compareResult)
diff --git a/assert/assertion_order_test.go b/assert/assertion_order_test.go
index 98cbe0e..7726b82 100644
--- a/assert/assertion_order_test.go
+++ b/assert/assertion_order_test.go
@@ -46,7 +46,7 @@ func TestIsIncreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsIncreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 	}
 }
 
@@ -91,7 +91,7 @@ func TestIsNonIncreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsNonIncreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 	}
 }
 
@@ -136,7 +136,7 @@ func TestIsDecreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsDecreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 	}
 }
 
@@ -181,6 +181,6 @@ func TestIsNonDecreasing(t *testing.T) {
 	} {
 		out := &outputT{buf: bytes.NewBuffer(nil)}
 		False(t, IsNonDecreasing(out, currCase.collection))
-		Contains(t, string(out.buf.Bytes()), currCase.msg)
+		Contains(t, out.buf.String(), currCase.msg)
 	}
 }
diff --git a/assert/assertions_test.go b/assert/assertions_test.go
index 4fafb51..0b5b2c1 100644
--- a/assert/assertions_test.go
+++ b/assert/assertions_test.go
@@ -592,6 +592,7 @@ func TestContainsNotContains(t *testing.T) {
 		{"j", "k"},
 	}
 	simpleMap := map[interface{}]interface{}{"Foo": "Bar"}
+	var zeroMap map[interface{}]interface{}
 
 	cases := []struct {
 		expected interface{}
@@ -606,6 +607,7 @@ func TestContainsNotContains(t *testing.T) {
 		{complexList, &A{"g", "e"}, false},
 		{simpleMap, "Foo", true},
 		{simpleMap, "Bar", false},
+		{zeroMap, "Bar", false},
 	}
 
 	for _, c := range cases {
@@ -652,6 +654,22 @@ func TestContainsFailMessage(t *testing.T) {
 	}
 }
 
+func TestContainsNotContainsOnNilValue(t *testing.T) {
+	mockT := new(mockTestingT)
+
+	Contains(mockT, nil, "key")
+	expectedFail := "<nil> could not be applied builtin len()"
+	actualFail := mockT.errorString()
+	if !strings.Contains(actualFail, expectedFail) {
+		t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail)
+	}
+
+	NotContains(mockT, nil, "key")
+	if !strings.Contains(actualFail, expectedFail) {
+		t.Errorf("Contains failure should include %q but was %q", expectedFail, actualFail)
+	}
+}
+
 func TestSubsetNotSubset(t *testing.T) {
 
 	// MTestCase adds a custom message to the case
@@ -714,53 +732,53 @@ func TestNotSubsetNil(t *testing.T) {
 	}
 }
 
-func Test_includeElement(t *testing.T) {
+func Test_containsElement(t *testing.T) {
 
 	list1 := []string{"Foo", "Bar"}
 	list2 := []int{1, 2}
 	simpleMap := map[interface{}]interface{}{"Foo": "Bar"}
 
-	ok, found := includeElement("Hello World", "World")
+	ok, found := containsElement("Hello World", "World")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list1, "Foo")
+	ok, found = containsElement(list1, "Foo")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list1, "Bar")
+	ok, found = containsElement(list1, "Bar")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list2, 1)
+	ok, found = containsElement(list2, 1)
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list2, 2)
+	ok, found = containsElement(list2, 2)
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(list1, "Foo!")
+	ok, found = containsElement(list1, "Foo!")
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(list2, 3)
+	ok, found = containsElement(list2, 3)
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(list2, "1")
+	ok, found = containsElement(list2, "1")
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(simpleMap, "Foo")
+	ok, found = containsElement(simpleMap, "Foo")
 	True(t, ok)
 	True(t, found)
 
-	ok, found = includeElement(simpleMap, "Bar")
+	ok, found = containsElement(simpleMap, "Bar")
 	True(t, ok)
 	False(t, found)
 
-	ok, found = includeElement(1433, "1")
+	ok, found = containsElement(1433, "1")
 	False(t, ok)
 	False(t, found)
 }
@@ -1557,8 +1575,7 @@ func testAutogeneratedFunction() {
 	t := struct {
 		io.Closer
 	}{}
-	var c io.Closer
-	c = t
+	c := t
 	c.Close()
 }
 
@@ -2206,7 +2223,7 @@ func ExampleValueAssertionFunc() {
 
 	dumbParse := func(input string) interface{} {
 		var x interface{}
-		json.Unmarshal([]byte(input), &x)
+		_ = json.Unmarshal([]byte(input), &x)
 		return x
 	}
 
diff --git a/assert/http_assertions_test.go b/assert/http_assertions_test.go
index 991866a..413c971 100644
--- a/assert/http_assertions_test.go
+++ b/assert/http_assertions_test.go
@@ -122,7 +122,7 @@ func TestHTTPStatusesWrapper(t *testing.T) {
 
 func httpHelloName(w http.ResponseWriter, r *http.Request) {
 	name := r.FormValue("name")
-	w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
+	_, _ = w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
 }
 
 func TestHTTPRequestWithNoParams(t *testing.T) {
diff --git a/mock/mock_test.go b/mock/mock_test.go
index 097addc..91828f8 100644
--- a/mock/mock_test.go
+++ b/mock/mock_test.go
@@ -1482,6 +1482,10 @@ func (s *timer) GetTime(i int) string {
 	return s.Called(i).Get(0).(string)
 }
 
+func (s *timer) GetTimes(times []int) string {
+	return s.Called(times).Get(0).(string)
+}
+
 type tCustomLogger struct {
 	*testing.T
 	logs []string
@@ -1554,6 +1558,23 @@ func TestArgumentMatcherToPrintMismatch(t *testing.T) {
 	m.AssertExpectations(t)
 }
 
+func TestArgumentMatcherToPrintMismatchWithReferenceType(t *testing.T) {
+	defer func() {
+		if r := recover(); r != nil {
+			matchingExp := regexp.MustCompile(
+				`\s+mock: Unexpected Method Call\s+-*\s+GetTimes\(\[\]int\)\s+0: \[\]int\{1\}\s+The closest call I have is:\s+GetTimes\(mock.argumentMatcher\)\s+0: mock.argumentMatcher\{.*?\}\s+Diff:.*\(\[\]int=\[1\]\) not matched by func\(\[\]int\) bool`)
+			assert.Regexp(t, matchingExp, r)
+		}
+	}()
+
+	m := new(timer)
+	m.On("GetTimes", MatchedBy(func(_ []int) bool { return false })).Return("SomeTime").Once()
+
+	res := m.GetTimes([]int{1})
+	require.Equal(t, "SomeTime", res)
+	m.AssertExpectations(t)
+}
+
 func TestClosestCallMismatchedArgumentInformationShowsTheClosest(t *testing.T) {
 	defer func() {
 		if r := recover(); r != nil {