Fix containsStringMatcher() when the text contains multiple occurrences of a substring (#431)

Signed-off-by: Marco Pracucci <marco@pracucci.com>
This commit is contained in:
Marco Pracucci 2023-03-01 12:18:30 +01:00 committed by GitHub
parent c77900d58e
commit 2e0ecc013f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 18 deletions

View file

@ -397,27 +397,36 @@ type containsStringMatcher struct {
func (m *containsStringMatcher) Matches(s string) bool { func (m *containsStringMatcher) Matches(s string) bool {
for _, substr := range m.substrings { for _, substr := range m.substrings {
if m.right != nil && m.left != nil { if m.right != nil && m.left != nil {
pos := strings.Index(s, substr) searchStartPos := 0
if pos < 0 {
continue for {
pos := strings.Index(s[searchStartPos:], substr)
if pos < 0 {
break
}
// Since we started searching from searchStartPos, we have to add that offset
// to get the actual position of the substring inside the text.
pos += searchStartPos
// If both the left and right matchers match, then we can stop searching because
// we've found a match.
if m.left.Matches(s[:pos]) && m.right.Matches(s[pos+len(substr):]) {
return true
}
// Continue searching for another occurrence of the substring inside the text.
searchStartPos = pos + 1
} }
if m.left.Matches(s[:pos]) && m.right.Matches(s[pos+len(substr):]) { } else if m.left != nil {
return true // If we have to check for characters on the left then we need to match a suffix.
}
continue
}
// If we have to check for characters on the left then we need to match a suffix.
if m.left != nil {
if strings.HasSuffix(s, substr) && m.left.Matches(s[:len(s)-len(substr)]) { if strings.HasSuffix(s, substr) && m.left.Matches(s[:len(s)-len(substr)]) {
return true return true
} }
continue } else if m.right != nil {
}
if m.right != nil {
if strings.HasPrefix(s, substr) && m.right.Matches(s[len(substr):]) { if strings.HasPrefix(s, substr) && m.right.Matches(s[len(substr):]) {
return true return true
} }
continue
} }
} }
return false return false

View file

@ -19,7 +19,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/grafana/regexp"
"github.com/grafana/regexp/syntax" "github.com/grafana/regexp/syntax"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -60,6 +59,7 @@ var (
"foo", " foo bar", "bar", "buzz\nbar", "bar foo", "bfoo", "\n", "\nfoo", "foo\n", "hello foo world", "hello foo\n world", "", "foo", " foo bar", "bar", "buzz\nbar", "bar foo", "bfoo", "\n", "\nfoo", "foo\n", "hello foo world", "hello foo\n world", "",
"FOO", "Foo", "OO", "Oo", "\nfoo\n", strings.Repeat("f", 20), "prometheus", "prometheus_api_v1", "prometheus_api_v1_foo", "FOO", "Foo", "OO", "Oo", "\nfoo\n", strings.Repeat("f", 20), "prometheus", "prometheus_api_v1", "prometheus_api_v1_foo",
"10.0.1.20", "10.0.2.10", "10.0.3.30", "10.0.4.40", "10.0.1.20", "10.0.2.10", "10.0.3.30", "10.0.4.40",
"foofoo0", "foofoo",
} }
) )
@ -72,9 +72,7 @@ func TestNewFastRegexMatcher(t *testing.T) {
t.Parallel() t.Parallel()
m, err := NewFastRegexMatcher(r) m, err := NewFastRegexMatcher(r)
require.NoError(t, err) require.NoError(t, err)
re, err := regexp.Compile("^(?:" + r + ")$") require.Equal(t, m.re.MatchString(v), m.MatchString(v))
require.NoError(t, err)
require.Equal(t, re.MatchString(v), m.MatchString(v))
}) })
} }
@ -329,3 +327,24 @@ func RandStringRunes(n int) string {
} }
return string(b) return string(b)
} }
func FuzzFastRegexMatcher_WithStaticallyDefinedRegularExpressions(f *testing.F) {
// Create all matchers.
matchers := make([]*FastRegexMatcher, 0, len(regexes))
for _, re := range regexes {
m, err := NewFastRegexMatcher(re)
require.NoError(f, err)
matchers = append(matchers, m)
}
// Add known values to seed corpus.
for _, v := range values {
f.Add(v)
}
f.Fuzz(func(t *testing.T, text string) {
for _, m := range matchers {
require.Equalf(t, m.re.MatchString(text), m.MatchString(text), "regexp: %s text: %s", m.re.String(), text)
}
})
}