Files
karma/filters/filter.go
Łukasz Mierzwa 44deae4f11 More strict filter syntax checking
Current filter factory rules are very relaxed, anything that doesn't match filter syntax perfectly is passed down to the fuzzy filter, that happily accepts every input and doesn't complain. This change makes syntax checks more strict, we explicitly look for roughly <anything><any of the chars used as operators><anything> (in reality we match the first <anything> used filter specific regex, but that can still be any regex) and only if evaluated filter expression doesn't match this regex we pass it to the fuzzy filter. Once we have a match we check if operator is supported by the filter and we're more strict there, in case of a===b we currently accept = as the operator and ==b as the value, now operator will be === (as we match for any of the operator chars in use) and fail once that doesn't match.
2017-04-20 13:36:18 -07:00

114 lines
2.6 KiB
Go

package filters
import (
"fmt"
"regexp"
"github.com/cloudflare/unsee/models"
)
// FilterT provides methods for interacting with alert filters
type FilterT interface {
init(name string, matcher *matcherT, rawText string, isValid bool, value string)
Match(alert *models.Alert, matches int) bool
GetRawText() string
GetHits() int
GetIsValid() bool
}
type alertFilter struct {
FilterT
Matched string
Matcher matcherT
RawText string
Value interface{}
IsValid bool
Hits int
}
func (filter *alertFilter) init(name string, matcher *matcherT, rawText string, isValid bool, value string) {
filter.Matched = name
if matcher != nil {
filter.Matcher = *matcher
}
filter.RawText = rawText
filter.IsValid = isValid
filter.Value = value
}
func (filter *alertFilter) GetRawText() string {
return filter.RawText
}
func (filter *alertFilter) GetHits() int {
return filter.Hits
}
func (filter *alertFilter) GetIsValid() bool {
return filter.IsValid
}
type newFilterFactory func() FilterT
// NewFilter creates new filter object from filter expression like "key=value"
// expression will be parsed and best filter implementation and value matcher
// will be selected
func NewFilter(expression string) FilterT {
invalid := alwaysInvalidFilter{}
invalid.init("", nil, expression, false, expression)
for _, fc := range AllFilters {
f := fc.Factory()
reExp := fmt.Sprintf("^(?P<matched>(%s))(?P<operator>(%s))(?P<value>(.*))", fc.Label, matcherRegex)
re := regexp.MustCompile(reExp)
match := re.FindStringSubmatch(expression)
result := make(map[string]string)
for i, name := range re.SubexpNames() {
if name != "" && i > 0 && i <= len(match) {
result[name] = match[i]
}
}
matched, found := result["matched"]
if !found && fc.IsSimple {
matcher, err := newMatcher(regexpOperator)
if err != nil {
f.init("", nil, expression, false, expression)
} else {
f.init("", &matcher, expression, true, expression)
}
return f
}
if !found {
continue
}
if value, ok := result["value"]; !ok || value == "" {
// value group not found in the expression
// example: 'label=''
return &invalid
}
operator, found := result["operator"]
if !found {
// used operator is not supported by the filter
// example: @limit=~0
return &invalid
}
if !stringInSlice(fc.SupportedOperators, operator) {
return &invalid
}
matcher, err := newMatcher(operator)
if err != nil {
f.init(matched, nil, expression, false, "")
} else {
if value, found := result["value"]; found {
f.init(matched, &matcher, expression, true, value)
return f
}
f.init(matched, &matcher, expression, false, "")
}
}
return &invalid
}