mirror of
https://github.com/hanzoai/metrics.git
synced 2026-10-30 20:03:39 +00:00
Previously regex simplify function made an attempt to parse string representation of simplified regex. And it could produce runtime panic due to std lib specification: ``` // Simplify returns a regexp equivalent to re but without counted repetitions // and with various other simplifications, such as rewriting /(?:a+)+/ to /a+/. // The resulting regexp will execute correctly but its string representation // will not produce the same parse tree, because capturing parentheses // may have been duplicated or removed. ``` This commit ignores simplified regex parsing error and returns back original regex. It results into possible missing simplification of some niche regex patterns. But it's extremely rare cases rarely seen in production. So the tradeoff is acceptable. Fixes victoriaMetrics/victoriaLogs/issues/1112
180 lines
4.9 KiB
Go
180 lines
4.9 KiB
Go
package regexutil
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestNewRegexFailure(t *testing.T) {
|
|
f := func(expr string) {
|
|
t.Helper()
|
|
|
|
r, err := NewRegex(expr)
|
|
if err == nil {
|
|
t.Fatalf("expecting non-nil error when parsing %q; got %q", expr, r)
|
|
}
|
|
}
|
|
|
|
f("[foo")
|
|
f("(foo")
|
|
// Trigger syntax.ErrInvalidRepeatOp
|
|
f("a{0,10000}")
|
|
}
|
|
|
|
func TestRegexMatchString(t *testing.T) {
|
|
f := func(expr, s string, resultExpected bool) {
|
|
t.Helper()
|
|
|
|
r, err := NewRegex(expr)
|
|
if err != nil {
|
|
t.Fatalf("cannot parse %q: %s", expr, err)
|
|
}
|
|
exprResult := r.String()
|
|
if exprResult != expr {
|
|
t.Fatalf("unexpected string representation for %q: %q", expr, exprResult)
|
|
}
|
|
result := r.MatchString(s)
|
|
if result != resultExpected {
|
|
t.Fatalf("unexpected result when matching %q against regex=%q; got %v; want %v", s, expr, result, resultExpected)
|
|
}
|
|
}
|
|
|
|
f("", "", true)
|
|
f("", "foo", true)
|
|
f("foo", "", false)
|
|
f(".*", "", true)
|
|
f(".*", "foo", true)
|
|
f(".+", "", false)
|
|
f(".+", "foo", true)
|
|
f("foo.*", "bar", false)
|
|
f("foo.*", "foo", true)
|
|
f("foo.*", "a foo", true)
|
|
f("foo.*", "a foo a", true)
|
|
f("foo.*", "foobar", true)
|
|
f("foo.*", "a foobar", true)
|
|
f("foo.+", "bar", false)
|
|
f("foo.+", "foo", false)
|
|
f("foo.+", "a foo", false)
|
|
f("foo.+", "foobar", true)
|
|
f("foo.+", "a foobar", true)
|
|
f("foo|bar", "", false)
|
|
f("foo|bar", "a", false)
|
|
f("foo|bar", "foo", true)
|
|
f("foo|bar", "a foo", true)
|
|
f("foo|bar", "foo a", true)
|
|
f("foo|bar", "a foo a", true)
|
|
f("foo|bar", "bar", true)
|
|
f("foo|bar", "foobar", true)
|
|
f("foo(bar|baz)", "a", false)
|
|
f("foo(bar|baz)", "foobar", true)
|
|
f("foo(bar|baz)", "foobaz", true)
|
|
f("foo(bar|baz)", "foobaza", true)
|
|
f("foo(bar|baz)", "a foobaz a", true)
|
|
f("foo(bar|baz)", "foobal", false)
|
|
f("^foo|b(ar)$", "foo", true)
|
|
f("^foo|b(ar)$", "foo a", true)
|
|
f("^foo|b(ar)$", "a foo", false)
|
|
f("^foo|b(ar)$", "bar", true)
|
|
f("^foo|b(ar)$", "a bar", true)
|
|
f("^foo|b(ar)$", "barz", false)
|
|
f("^foo|b(ar)$", "ar", false)
|
|
f(".*foo.*", "foo", true)
|
|
f(".*foo.*", "afoobar", true)
|
|
f(".*foo.*", "abc", false)
|
|
f("foo.*bar.*", "foobar", true)
|
|
f("foo.*bar.*", "foo_bar_", true)
|
|
f("foo.*bar.*", "a foo bar baz", true)
|
|
f("foo.*bar.*", "foobaz", false)
|
|
f("foo.*bar.*", "baz foo", false)
|
|
f(".+foo.+", "foo", false)
|
|
f(".+foo.+", "afoobar", true)
|
|
f(".+foo.+", "afoo", false)
|
|
f(".+foo.+", "abc", false)
|
|
f("foo.+bar.+", "foobar", false)
|
|
f("foo.+bar.+", "foo_bar_", true)
|
|
f("foo.+bar.+", "a foo_bar_", true)
|
|
f("foo.+bar.+", "foobaz", false)
|
|
f("foo.+bar.+", "abc", false)
|
|
f(".+foo.*", "foo", false)
|
|
f(".+foo.*", "afoo", true)
|
|
f(".+foo.*", "afoobar", true)
|
|
f(".*(a|b).*", "a", true)
|
|
f(".*(a|b).*", "ax", true)
|
|
f(".*(a|b).*", "xa", true)
|
|
f(".*(a|b).*", "xay", true)
|
|
f(".*(a|b).*", "xzy", false)
|
|
f("^(?:true)$", "true", true)
|
|
f("^(?:true)$", "false", false)
|
|
|
|
f(".+;|;.+", ";", false)
|
|
f(".+;|;.+", "foo", false)
|
|
f(".+;|;.+", "foo;bar", true)
|
|
f(".+;|;.+", "foo;", true)
|
|
f(".+;|;.+", ";foo", true)
|
|
f(".+foo|bar|baz.+", "foo", false)
|
|
f(".+foo|bar|baz.+", "afoo", true)
|
|
f(".+foo|bar|baz.+", "fooa", false)
|
|
f(".+foo|bar|baz.+", "afooa", true)
|
|
f(".+foo|bar|baz.+", "bar", true)
|
|
f(".+foo|bar|baz.+", "abar", true)
|
|
f(".+foo|bar|baz.+", "abara", true)
|
|
f(".+foo|bar|baz.+", "bara", true)
|
|
f(".+foo|bar|baz.+", "baz", false)
|
|
f(".+foo|bar|baz.+", "baza", true)
|
|
f(".+foo|bar|baz.+", "abaz", false)
|
|
f(".+foo|bar|baz.+", "abaza", true)
|
|
f(".+foo|bar|baz.+", "afoo|bar|baza", true)
|
|
f(".+(foo|bar|baz).+", "bar", false)
|
|
f(".+(foo|bar|baz).+", "bara", false)
|
|
f(".+(foo|bar|baz).+", "abar", false)
|
|
f(".+(foo|bar|baz).+", "abara", true)
|
|
f(".+(foo|bar|baz).+", "afooa", true)
|
|
f(".+(foo|bar|baz).+", "abaza", true)
|
|
|
|
f(".*;|;.*", ";", true)
|
|
f(".*;|;.*", "foo", false)
|
|
f(".*;|;.*", "foo;bar", true)
|
|
f(".*;|;.*", "foo;", true)
|
|
f(".*;|;.*", ";foo", true)
|
|
|
|
f("^bar", "foobarbaz", false)
|
|
f("^foo", "foobarbaz", true)
|
|
f("bar$", "foobarbaz", false)
|
|
f("baz$", "foobarbaz", true)
|
|
f("(bar$|^foo)", "foobarbaz", true)
|
|
f("(bar$^boo)", "foobarbaz", false)
|
|
f("foo(bar|baz)", "a fooxfoobaz a", true)
|
|
f("foo(bar|baz)", "a fooxfooban a", false)
|
|
f("foo(bar|baz)", "a fooxfooban foobar a", true)
|
|
|
|
// Trigger syntax.ErrNestingDepth
|
|
// See https://github.com/VictoriaMetrics/VictoriaLogs/issues/1112
|
|
f("a{0,1000}", "a", true)
|
|
}
|
|
|
|
func TestGetLiterals(t *testing.T) {
|
|
f := func(expr string, literalsExpected []string) {
|
|
t.Helper()
|
|
|
|
r, err := NewRegex(expr)
|
|
if err != nil {
|
|
t.Fatalf("cannot parse %q: %s", expr, err)
|
|
}
|
|
literals := r.GetLiterals()
|
|
if !reflect.DeepEqual(literals, literalsExpected) {
|
|
t.Fatalf("unexpected literals; got %q; want %q", literals, literalsExpected)
|
|
}
|
|
}
|
|
|
|
f("", nil)
|
|
f("foo bar baz", []string{"foo bar baz"})
|
|
f("foo.*bar(a|b)baz.+", []string{"foo", "bar", "baz"})
|
|
f("(foo[ab](?:bar))", []string{"foo", "bar"})
|
|
f("foo|bar", nil)
|
|
f("(?i)foo", nil)
|
|
f("foo((?i)bar)baz", []string{"foo", "baz"})
|
|
f("((foo|bar)baz xxx(?:yzabc))", []string{"baz xxxyzabc"})
|
|
f("((foo|bar)baz xxx(?:yzabc)*)", []string{"baz xxx"})
|
|
f("((foo|bar)baz? xxx(?:yzabc)*)", []string{"ba", " xxx"})
|
|
}
|