mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-03 23:20:25 +08:00
优化WAF单词匹配性能
This commit is contained in:
@@ -2,6 +2,40 @@
|
|||||||
|
|
||||||
package runes
|
package runes
|
||||||
|
|
||||||
|
// ContainsAnyWordRunes 直接使用rune检查字符串是否包含任一单词
|
||||||
|
func ContainsAnyWordRunes(s string, words [][]rune, isCaseInsensitive bool) bool {
|
||||||
|
var allRunes = []rune(s)
|
||||||
|
if len(allRunes) == 0 || len(words) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastRune rune // last searching rune in s
|
||||||
|
var lastIndex = -2 // -2: not started, -1: not found, >=0: rune index
|
||||||
|
for _, wordRunes := range words {
|
||||||
|
if len(wordRunes) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastIndex > -2 && lastRune == wordRunes[0] {
|
||||||
|
if lastIndex >= 0 {
|
||||||
|
result, _ := ContainsWordRunes(allRunes[lastIndex:], wordRunes, isCaseInsensitive)
|
||||||
|
if result {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
result, firstIndex := ContainsWordRunes(allRunes, wordRunes, isCaseInsensitive)
|
||||||
|
lastIndex = firstIndex
|
||||||
|
if result {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastRune = wordRunes[0]
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// ContainsAnyWord 检查字符串是否包含任一单词
|
// ContainsAnyWord 检查字符串是否包含任一单词
|
||||||
func ContainsAnyWord(s string, words []string, isCaseInsensitive bool) bool {
|
func ContainsAnyWord(s string, words []string, isCaseInsensitive bool) bool {
|
||||||
var allRunes = []rune(s)
|
var allRunes = []rune(s)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ func TestContainsAnyWord(t *testing.T) {
|
|||||||
a.IsFalse(runes.ContainsAnyWord("How are you?", []string{"how", "ok"}, false))
|
a.IsFalse(runes.ContainsAnyWord("How are you?", []string{"how", "ok"}, false))
|
||||||
a.IsTrue(runes.ContainsAnyWord("How are you?", []string{"how"}, true))
|
a.IsTrue(runes.ContainsAnyWord("How are you?", []string{"how"}, true))
|
||||||
a.IsTrue(runes.ContainsAnyWord("How are you?", []string{"how", "ok"}, true))
|
a.IsTrue(runes.ContainsAnyWord("How are you?", []string{"how", "ok"}, true))
|
||||||
|
a.IsTrue(runes.ContainsAnyWord("How-are you?", []string{"how", "ok"}, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainsAnyWord_Sort(t *testing.T) {
|
func TestContainsAnyWord_Sort(t *testing.T) {
|
||||||
@@ -100,6 +101,12 @@ func BenchmarkContainsAnyWord(b *testing.B) {
|
|||||||
|
|
||||||
var words = strings.Split("python\npycurl\nhttp-client\nhttpclient\napachebench\nnethttp\nhttp_request\njava\nperl\nruby\nscrapy\nphp\nrust", "\n")
|
var words = strings.Split("python\npycurl\nhttp-client\nhttpclient\napachebench\nnethttp\nhttp_request\njava\nperl\nruby\nscrapy\nphp\nrust", "\n")
|
||||||
sort.Strings(words)
|
sort.Strings(words)
|
||||||
|
|
||||||
|
var wordRunes = [][]rune{}
|
||||||
|
for _, word := range words {
|
||||||
|
wordRunes = append(wordRunes, []rune(word))
|
||||||
|
}
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
@@ -109,6 +116,27 @@ func BenchmarkContainsAnyWord(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkContainsAnyWordRunes(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
|
||||||
|
var words = strings.Split("python\npycurl\nhttp-client\nhttpclient\napachebench\nnethttp\nhttp_request\njava\nperl\nruby\nscrapy\nphp\nrust", "\n")
|
||||||
|
sort.Strings(words)
|
||||||
|
|
||||||
|
var wordRunes = [][]rune{}
|
||||||
|
for _, word := range words {
|
||||||
|
wordRunes = append(wordRunes, []rune(word))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_ = runes.ContainsAnyWordRunes("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_0_0) AppleWebKit/500.00 (KHTML, like Gecko) Chrome/100.0.0.0", wordRunes, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func BenchmarkContainsAnyWord_Regexp(b *testing.B) {
|
func BenchmarkContainsAnyWord_Regexp(b *testing.B) {
|
||||||
runtime.GOMAXPROCS(4)
|
runtime.GOMAXPROCS(4)
|
||||||
var reg = regexp.MustCompile("(?i)" + strings.ReplaceAll("python\npycurl\nhttp-client\nhttpclient\napachebench\nnethttp\nhttp_request\njava\nperl\nruby\nscrapy\nphp\nrust", "\n", "|"))
|
var reg = regexp.MustCompile("(?i)" + strings.ReplaceAll("python\npycurl\nhttp-client\nhttpclient\napachebench\nnethttp\nhttp_request\njava\nperl\nruby\nscrapy\nphp\nrust", "\n", "|"))
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type Rule struct {
|
|||||||
|
|
||||||
ipRangeListValue *values.IPRangeList
|
ipRangeListValue *values.IPRangeList
|
||||||
stringValues []string
|
stringValues []string
|
||||||
|
stringValueRunes [][]rune
|
||||||
ipList *values.StringList
|
ipList *values.StringList
|
||||||
|
|
||||||
floatValue float64
|
floatValue float64
|
||||||
@@ -97,6 +98,11 @@ func (this *Rule) Init() error {
|
|||||||
if this.Operator == RuleOperatorContainsAnyWord || this.Operator == RuleOperatorContainsAllWords || this.Operator == RuleOperatorNotContainsAnyWord {
|
if this.Operator == RuleOperatorContainsAnyWord || this.Operator == RuleOperatorContainsAllWords || this.Operator == RuleOperatorNotContainsAnyWord {
|
||||||
sort.Strings(this.stringValues)
|
sort.Strings(this.stringValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.stringValueRunes = [][]rune{}
|
||||||
|
for _, line := range this.stringValues {
|
||||||
|
this.stringValueRunes = append(this.stringValueRunes, []rune(line))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case RuleOperatorMatch:
|
case RuleOperatorMatch:
|
||||||
var v = this.Value
|
var v = this.Value
|
||||||
@@ -567,11 +573,11 @@ func (this *Rule) Test(value any) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
case RuleOperatorContainsAnyWord:
|
case RuleOperatorContainsAnyWord:
|
||||||
return runes.ContainsAnyWord(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
|
return runes.ContainsAnyWordRunes(this.stringifyValue(value), this.stringValueRunes, this.IsCaseInsensitive)
|
||||||
case RuleOperatorContainsAllWords:
|
case RuleOperatorContainsAllWords:
|
||||||
return runes.ContainsAllWords(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
|
return runes.ContainsAllWords(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
|
||||||
case RuleOperatorNotContainsAnyWord:
|
case RuleOperatorNotContainsAnyWord:
|
||||||
return !runes.ContainsAnyWord(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
|
return !runes.ContainsAnyWordRunes(this.stringifyValue(value), this.stringValueRunes, this.IsCaseInsensitive)
|
||||||
case RuleOperatorContainsSQLInjection:
|
case RuleOperatorContainsSQLInjection:
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
Reference in New Issue
Block a user