More strict filter validation

@silenced=true should now be marked as invalid filter, but it isn't, this commit refactors it so that instead of looping over all filters and falling back to fuzzy (which is very relaxed checking and can't find typos in filter names like @statuz=active) we tokenize the expression into '<filter name> <operator> <value>' and only fallback to fuzzy if there's only a static text (no filter name and operator). This is more manual but provides stronger checks so now @silenced=true is correctly marked as invalid
This commit is contained in:
Łukasz Mierzwa
2017-05-05 22:27:21 +01:00
parent 4ade516f57
commit 7bf0b9d5d0
3 changed files with 69 additions and 40 deletions

View File

@@ -57,43 +57,50 @@ func NewFilter(expression string) FilterT {
invalid := alwaysInvalidFilter{}
invalid.init("", nil, expression, false, expression)
reExp := fmt.Sprintf("^(?P<matched>(%s))(?P<operator>(%s))(?P<value>(.*))", filterRegex, 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, _ := result["matched"]
operator, _ := result["operator"]
value, _ := result["value"]
if matched == "" && operator == "" && value == "" {
// no "filter=" part, just the value, use fuzzy filter
f := newFuzzyFilter()
matcher, err := newMatcher(regexpOperator)
if err != nil {
f.init("", nil, expression, false, expression)
} else {
f.init("", &matcher, expression, true, expression)
}
return f
}
if value == "" {
// there's no value, so it's always invalid
return &invalid
}
if operator == "" {
// no operator, no valid filter here
return &invalid
}
// we have "filter=" part, lookup filter that matches
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 {
labelRe := regexp.MustCompile("^(?:" + fc.Label + ")$")
if !labelRe.MatchString(matched) {
// filter name doesn't match, keep searching
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
}
@@ -101,13 +108,13 @@ func NewFilter(expression string) FilterT {
if err != nil {
f.init(matched, nil, expression, false, "")
} else {
if value, found := result["value"]; found {
if value != "" {
f.init(matched, &matcher, expression, true, value)
return f
}
f.init(matched, &matcher, expression, false, "")
}
}
return &invalid
}

View File

@@ -406,6 +406,30 @@ var tests = []filterTest{
Expression: "^abb[****].*****",
IsValid: false,
},
filterTest{
Expression: "@silenced=true",
IsValid: false,
},
filterTest{
Expression: "@silenced!=false",
IsValid: false,
},
filterTest{
Expression: "@silenced=~false",
IsValid: false,
},
filterTest{
Expression: "@inhibited=true",
IsValid: false,
},
filterTest{
Expression: "@inhibited!=false",
IsValid: false,
},
filterTest{
Expression: "@inhibited=~false",
IsValid: false,
},
}
func TestFilters(t *testing.T) {

View File

@@ -15,6 +15,9 @@ const (
// a===b should yield an error
var matcherRegex = "[=!<>~]+"
// same as matcherRegex but for the filter name part
var filterRegex = "^(@)?[a-zA-Z_][a-zA-Z0-9_]*"
var matcherConfig = map[string]matcherT{
equalOperator: &equalMatcher{},
notEqualOperator: &notEqualMatcher{},
@@ -25,7 +28,6 @@ var matcherConfig = map[string]matcherT{
}
type filterConfig struct {
IsSimple bool
Label string
SupportedOperators []string
Factory newFilterFactory
@@ -71,8 +73,4 @@ var AllFilters = []filterConfig{
Factory: newLabelFilter,
Autocomplete: labelAutocomplete,
},
filterConfig{
IsSimple: true,
Factory: newFuzzyFilter,
},
}