guards against too many matches.

Signed-off-by: Cyril Tovena <cyril.tovena@gmail.com>
This commit is contained in:
Cyril Tovena 2021-10-05 16:46:24 +02:00
parent 639cd00721
commit d217825af8
No known key found for this signature in database
GPG key ID: FD8F768F9D633FB6
2 changed files with 17 additions and 2 deletions

View file

@ -19,6 +19,8 @@ import (
"strings" "strings"
) )
const maxSetMatches = 256
type FastRegexMatcher struct { type FastRegexMatcher struct {
re *regexp.Regexp re *regexp.Regexp
@ -86,12 +88,12 @@ func findSetMatches(re *syntax.Regexp, base string) []string {
var matches []string var matches []string
var totalSet int var totalSet int
for i := 0; i < len(re.Rune); i = i + 2 { for i := 0; i < len(re.Rune); i = i + 2 {
totalSet += int(re.Rune[i+1] - re.Rune[i]) totalSet += int(re.Rune[i+1]-re.Rune[i]) + 1
} }
// limits the total characters that can be used to create matches. // limits the total characters that can be used to create matches.
// In some case like negation [^0-9] a lot of possibilities exists and that // In some case like negation [^0-9] a lot of possibilities exists and that
// can create thousands of possible matches at which points we're better off using regexp. // can create thousands of possible matches at which points we're better off using regexp.
if totalSet > 100 { if totalSet > maxSetMatches {
return nil return nil
} }
for i := 0; i < len(re.Rune); i = i + 2 { for i := 0; i < len(re.Rune); i = i + 2 {
@ -128,6 +130,9 @@ func findSetMatchesFromConcat(re *syntax.Regexp, base string) []string {
if m == nil { if m == nil {
return nil return nil
} }
if tooManyMatches(newMatches, m...) {
return nil
}
newMatches = append(newMatches, m...) newMatches = append(newMatches, m...)
} }
matches = newMatches matches = newMatches
@ -143,6 +148,9 @@ func findSetMatchesFromAlternate(re *syntax.Regexp, base string) []string {
if found == nil { if found == nil {
return nil return nil
} }
if tooManyMatches(setMatches, found...) {
return nil
}
setMatches = append(setMatches, found...) setMatches = append(setMatches, found...)
} }
return setMatches return setMatches
@ -163,6 +171,11 @@ func isCaseInsensitive(reg *syntax.Regexp) bool {
return (reg.Flags & syntax.FoldCase) != 0 return (reg.Flags & syntax.FoldCase) != 0
} }
// tooManyMatches guards against creating too many set matches
func tooManyMatches(matches []string, new ...string) bool {
return len(matches)+len(new) > maxSetMatches
}
func (m *FastRegexMatcher) MatchString(s string) bool { func (m *FastRegexMatcher) MatchString(s string) bool {
if len(m.setMatches) != 0 { if len(m.setMatches) != 0 {
for _, match := range m.setMatches { for _, match := range m.setMatches {

View file

@ -137,6 +137,8 @@ func TestFindSetMatches(t *testing.T) {
{"(api|rpc)_(v1|prom)_((?i)push|query)", nil}, {"(api|rpc)_(v1|prom)_((?i)push|query)", nil},
// too high charset combination // too high charset combination
{"(api|rpc)_[^0-9]", nil}, {"(api|rpc)_[^0-9]", nil},
// too many combinations
{"[a-z][a-z]", nil},
} { } {
c := c c := c
t.Run(c.pattern, func(t *testing.T) { t.Run(c.pattern, func(t *testing.T) {