mirror of
https://github.com/prymitive/karma
synced 2026-05-09 03:36:44 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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: ¬EqualMatcher{},
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user