mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-09 12:00:26 +08:00
WAF操作符增加包含任一单词、包含所有单词、不包含任一单词
This commit is contained in:
@@ -4,7 +4,7 @@ package re
|
|||||||
|
|
||||||
type RuneMap map[rune]*RuneTree
|
type RuneMap map[rune]*RuneTree
|
||||||
|
|
||||||
func (this *RuneMap) Lookup(s string, caseInsensitive bool) bool {
|
func (this RuneMap) Lookup(s string, caseInsensitive bool) bool {
|
||||||
return this.lookup([]rune(s), caseInsensitive, 0)
|
return this.lookup([]rune(s), caseInsensitive, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
110
internal/utils/runes/runes.go
Normal file
110
internal/utils/runes/runes.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package runes
|
||||||
|
|
||||||
|
// ContainsAnyWord 检查字符串是否包含任一单词
|
||||||
|
func ContainsAnyWord(s string, words []string, isCaseInsensitive bool) bool {
|
||||||
|
var allRunes = []rune(s)
|
||||||
|
if len(allRunes) == 0 || len(words) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, word := range words {
|
||||||
|
if ContainsWordRunes(allRunes, []rune(word), isCaseInsensitive) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAllWords 检查字符串是否包含所有单词
|
||||||
|
func ContainsAllWords(s string, words []string, isCaseInsensitive bool) bool {
|
||||||
|
var allRunes = []rune(s)
|
||||||
|
if len(allRunes) == 0 || len(words) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, word := range words {
|
||||||
|
if !ContainsWordRunes(allRunes, []rune(word), isCaseInsensitive) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsWordRunes 检查字符列表是否包含某个单词子字符列表
|
||||||
|
func ContainsWordRunes(allRunes []rune, subRunes []rune, isCaseInsensitive bool) bool {
|
||||||
|
var l = len(subRunes)
|
||||||
|
if l == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var al = len(allRunes)
|
||||||
|
|
||||||
|
for index, r := range allRunes {
|
||||||
|
if EqualRune(r, subRunes[0], isCaseInsensitive) && (index == 0 || !isChar(allRunes[index-1]) /**boundary check **/) {
|
||||||
|
var found = true
|
||||||
|
if l > 1 {
|
||||||
|
for i := 1; i < l; i++ {
|
||||||
|
var subIndex = index + i
|
||||||
|
if subIndex > al-1 || !EqualRune(allRunes[subIndex], subRunes[i], isCaseInsensitive) {
|
||||||
|
found = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check after charset
|
||||||
|
if found && (al <= index+l || !isChar(allRunes[index+l]) /**boundary check **/) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsSubRunes 检查字符列表是否包含某个子子字符列表
|
||||||
|
// 与 ContainsWordRunes 不同,这里不需要检查边界符号
|
||||||
|
func ContainsSubRunes(allRunes []rune, subRunes []rune, isCaseInsensitive bool) bool {
|
||||||
|
var l = len(subRunes)
|
||||||
|
if l == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var al = len(allRunes)
|
||||||
|
|
||||||
|
for index, r := range allRunes {
|
||||||
|
if EqualRune(r, subRunes[0], isCaseInsensitive) {
|
||||||
|
var found = true
|
||||||
|
if l > 1 {
|
||||||
|
for i := 1; i < l; i++ {
|
||||||
|
var subIndex = index + i
|
||||||
|
if subIndex > al-1 || !EqualRune(allRunes[subIndex], subRunes[i], isCaseInsensitive) {
|
||||||
|
found = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check after charset
|
||||||
|
if found {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualRune 判断两个rune是否相同
|
||||||
|
func EqualRune(r1 rune, r2 rune, isCaseInsensitive bool) bool {
|
||||||
|
const d = 'a' - 'A'
|
||||||
|
return r1 == r2 ||
|
||||||
|
(isCaseInsensitive && r1 >= 'a' && r1 <= 'z' && r1-r2 == d) ||
|
||||||
|
(isCaseInsensitive && r1 >= 'A' && r1 <= 'Z' && r1-r2 == -d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isChar(r rune) bool {
|
||||||
|
return r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9'
|
||||||
|
}
|
||||||
97
internal/utils/runes/runes_test.go
Normal file
97
internal/utils/runes/runes_test.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package runes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/runes"
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContainsAllWords(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
a.IsTrue(runes.ContainsAllWords("How are you?", []string{"are", "you"}, false))
|
||||||
|
a.IsFalse(runes.ContainsAllWords("How are you?", []string{"how", "are", "you"}, false))
|
||||||
|
a.IsTrue(runes.ContainsAllWords("How are you?", []string{"how", "are", "you"}, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainsAnyWord(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
a.IsTrue(runes.ContainsAnyWord("How are you?", []string{"are", "you"}, false))
|
||||||
|
a.IsTrue(runes.ContainsAnyWord("How are you?", []string{"are", "you", "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", "ok"}, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainsWordRunes(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
a.IsFalse(runes.ContainsWordRunes([]rune(""), []rune("How"), true))
|
||||||
|
a.IsFalse(runes.ContainsWordRunes([]rune("How are you?"), []rune(""), true))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you?"), []rune("How"), true))
|
||||||
|
a.IsFalse(runes.ContainsWordRunes([]rune("How are you?"), []rune("how"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you?"), []rune("you"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you?"), []rune("are"), false))
|
||||||
|
a.IsFalse(runes.ContainsWordRunes([]rune("How are you?"), []rune("re"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you w?"), []rune("w"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("w How are you?"), []rune("w"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are w you?"), []rune("w"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are how you?"), []rune("how"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you?"), []rune("how"), true))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you?"), []rune("ARE"), true))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you"), []rune("you"), false))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you"), []rune("YOU"), true))
|
||||||
|
a.IsTrue(runes.ContainsWordRunes([]rune("How are you?"), []rune("YOU"), true))
|
||||||
|
a.IsFalse(runes.ContainsWordRunes([]rune("How are you1?"), []rune("YOU"), true))
|
||||||
|
a.IsFalse(runes.ContainsWordRunes([]rune("How are you1?"), []rune("YOU YOU YOU YOU YOU YOU YOU"), true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainsSubRunes(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
a.IsFalse(runes.ContainsSubRunes([]rune(""), []rune("How"), true))
|
||||||
|
a.IsFalse(runes.ContainsSubRunes([]rune("How are you?"), []rune(""), true))
|
||||||
|
a.IsTrue(runes.ContainsSubRunes([]rune("How are you1?"), []rune("YOU"), true))
|
||||||
|
a.IsTrue(runes.ContainsSubRunes([]rune("How are you1?"), []rune("ow"), false))
|
||||||
|
a.IsTrue(runes.ContainsSubRunes([]rune("How are you1?"), []rune("H"), false))
|
||||||
|
a.IsTrue(runes.ContainsSubRunes([]rune("How are you1?"), []rune("How"), false))
|
||||||
|
a.IsTrue(runes.ContainsSubRunes([]rune("How are you doing"), []rune("oi"), false))
|
||||||
|
a.IsTrue(runes.ContainsSubRunes([]rune("How are you doing"), []rune("g"), false))
|
||||||
|
a.IsTrue(runes.ContainsSubRunes([]rune("How are you doing"), []rune("ing"), false))
|
||||||
|
a.IsFalse(runes.ContainsSubRunes([]rune("How are you doing"), []rune("int"), false))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualRune(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
a.IsTrue(runes.EqualRune('a', 'a', false))
|
||||||
|
a.IsTrue(runes.EqualRune('a', 'a', true))
|
||||||
|
a.IsFalse(runes.EqualRune('a', 'A', false))
|
||||||
|
a.IsTrue(runes.EqualRune('a', 'A', true))
|
||||||
|
a.IsFalse(runes.EqualRune('c', 'C', false))
|
||||||
|
a.IsTrue(runes.EqualRune('c', 'C', true))
|
||||||
|
a.IsTrue(runes.EqualRune('C', 'C', true))
|
||||||
|
a.IsTrue(runes.EqualRune('C', 'c', true))
|
||||||
|
a.IsTrue(runes.EqualRune('Z', 'z', true))
|
||||||
|
a.IsTrue(runes.EqualRune('z', 'Z', true))
|
||||||
|
a.IsFalse(runes.EqualRune('z', 'z'+('a'-'A'), true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkContainsWordRunes(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_ = runes.ContainsWordRunes([]rune("How are you"), []rune("YOU"), true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkContainsSubRunes(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_ = runes.ContainsSubRunes([]rune("How are you"), []rune("YOU"), true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/filterconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/filterconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/re"
|
"github.com/TeaOSLab/EdgeNode/internal/re"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/runes"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf/checkpoints"
|
"github.com/TeaOSLab/EdgeNode/internal/waf/checkpoints"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
|
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/waf/utils"
|
||||||
@@ -77,7 +78,7 @@ func (this *Rule) Init() error {
|
|||||||
this.floatValue = types.Float64(this.Value)
|
this.floatValue = types.Float64(this.Value)
|
||||||
case RuleOperatorNeq:
|
case RuleOperatorNeq:
|
||||||
this.floatValue = types.Float64(this.Value)
|
this.floatValue = types.Float64(this.Value)
|
||||||
case RuleOperatorContainsAny, RuleOperatorContainsAll:
|
case RuleOperatorContainsAny, RuleOperatorContainsAll, RuleOperatorContainsAnyWord, RuleOperatorContainsAllWords, RuleOperatorNotContainsAnyWord:
|
||||||
this.stringValues = []string{}
|
this.stringValues = []string{}
|
||||||
if len(this.Value) > 0 {
|
if len(this.Value) > 0 {
|
||||||
var lines = strings.Split(this.Value, "\n")
|
var lines = strings.Split(this.Value, "\n")
|
||||||
@@ -546,6 +547,12 @@ func (this *Rule) Test(value any) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
case RuleOperatorContainsAnyWord:
|
||||||
|
return runes.ContainsAnyWord(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
|
||||||
|
case RuleOperatorContainsAllWords:
|
||||||
|
return runes.ContainsAllWords(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
|
||||||
|
case RuleOperatorNotContainsAnyWord:
|
||||||
|
return !runes.ContainsAnyWord(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive)
|
||||||
case RuleOperatorContainsBinary:
|
case RuleOperatorContainsBinary:
|
||||||
data, _ := base64.StdEncoding.DecodeString(this.stringifyValue(this.Value))
|
data, _ := base64.StdEncoding.DecodeString(this.stringifyValue(this.Value))
|
||||||
if this.IsCaseInsensitive {
|
if this.IsCaseInsensitive {
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ const (
|
|||||||
RuleOperatorSuffix RuleOperator = "suffix"
|
RuleOperatorSuffix RuleOperator = "suffix"
|
||||||
RuleOperatorContainsAny RuleOperator = "contains any"
|
RuleOperatorContainsAny RuleOperator = "contains any"
|
||||||
RuleOperatorContainsAll RuleOperator = "contains all"
|
RuleOperatorContainsAll RuleOperator = "contains all"
|
||||||
|
RuleOperatorContainsAnyWord RuleOperator = "contains any word"
|
||||||
|
RuleOperatorContainsAllWords RuleOperator = "contains all word"
|
||||||
|
RuleOperatorNotContainsAnyWord RuleOperator = "not contains any word"
|
||||||
RuleOperatorInIPList RuleOperator = "in ip list"
|
RuleOperatorInIPList RuleOperator = "in ip list"
|
||||||
RuleOperatorHasKey RuleOperator = "has key" // has key in slice or map
|
RuleOperatorHasKey RuleOperator = "has key" // has key in slice or map
|
||||||
RuleOperatorVersionGt RuleOperator = "version gt"
|
RuleOperatorVersionGt RuleOperator = "version gt"
|
||||||
@@ -32,6 +35,7 @@ const (
|
|||||||
RuleOperatorNotContainsBinary RuleOperator = "not contains binary" // not contains binary
|
RuleOperatorNotContainsBinary RuleOperator = "not contains binary" // not contains binary
|
||||||
|
|
||||||
// ip
|
// ip
|
||||||
|
|
||||||
RuleOperatorEqIP RuleOperator = "eq ip"
|
RuleOperatorEqIP RuleOperator = "eq ip"
|
||||||
RuleOperatorGtIP RuleOperator = "gt ip"
|
RuleOperatorGtIP RuleOperator = "gt ip"
|
||||||
RuleOperatorGteIP RuleOperator = "gte ip"
|
RuleOperatorGteIP RuleOperator = "gte ip"
|
||||||
@@ -42,10 +46,6 @@ const (
|
|||||||
RuleOperatorIPMod10 RuleOperator = "ip mod 10"
|
RuleOperatorIPMod10 RuleOperator = "ip mod 10"
|
||||||
RuleOperatorIPMod100 RuleOperator = "ip mod 100"
|
RuleOperatorIPMod100 RuleOperator = "ip mod 100"
|
||||||
RuleOperatorIPMod RuleOperator = "ip mod"
|
RuleOperatorIPMod RuleOperator = "ip mod"
|
||||||
|
|
||||||
RuleCaseInsensitiveNone = "none"
|
|
||||||
RuleCaseInsensitiveYes = "yes"
|
|
||||||
RuleCaseInsensitiveNo = "no"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuleOperatorDefinition struct {
|
type RuleOperatorDefinition struct {
|
||||||
@@ -54,174 +54,3 @@ type RuleOperatorDefinition struct {
|
|||||||
Description string
|
Description string
|
||||||
CaseInsensitive RuleCaseInsensitive // default caseInsensitive setting
|
CaseInsensitive RuleCaseInsensitive // default caseInsensitive setting
|
||||||
}
|
}
|
||||||
|
|
||||||
var AllRuleOperators = []*RuleOperatorDefinition{
|
|
||||||
{
|
|
||||||
Name: "数值大于",
|
|
||||||
Code: RuleOperatorGt,
|
|
||||||
Description: "使用数值对比大于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNone,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "数值大于等于",
|
|
||||||
Code: RuleOperatorGte,
|
|
||||||
Description: "使用数值对比大于等于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNone,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "数值小于",
|
|
||||||
Code: RuleOperatorLt,
|
|
||||||
Description: "使用数值对比小于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNone,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "数值小于等于",
|
|
||||||
Code: RuleOperatorLte,
|
|
||||||
Description: "使用数值对比小于等于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNone,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "数值等于",
|
|
||||||
Code: RuleOperatorEq,
|
|
||||||
Description: "使用数值对比等于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNone,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "数值不等于",
|
|
||||||
Code: RuleOperatorNeq,
|
|
||||||
Description: "使用数值对比不等于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNone,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "字符串等于",
|
|
||||||
Code: RuleOperatorEqString,
|
|
||||||
Description: "使用字符串对比等于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "字符串不等于",
|
|
||||||
Code: RuleOperatorNeqString,
|
|
||||||
Description: "使用字符串对比不等于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "正则匹配",
|
|
||||||
Code: RuleOperatorMatch,
|
|
||||||
Description: "使用正则表达式匹配,在头部使用(?i)表示不区分大小写,<a href=\"http://teaos.cn/doc/regexp/Regexp.md\" target=\"_blank\">正则表达式语法 »</a>",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveYes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "正则不匹配",
|
|
||||||
Code: RuleOperatorNotMatch,
|
|
||||||
Description: "使用正则表达式不匹配,在头部使用(?i)表示不区分大小写,<a href=\"http://teaos.cn/doc/regexp/Regexp.md\" target=\"_blank\">正则表达式语法 »</a>",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveYes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "包含字符串",
|
|
||||||
Code: RuleOperatorContains,
|
|
||||||
Description: "包含某个字符串",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "不包含字符串",
|
|
||||||
Code: RuleOperatorNotContains,
|
|
||||||
Description: "不包含某个字符串",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "包含前缀",
|
|
||||||
Code: RuleOperatorPrefix,
|
|
||||||
Description: "包含某个前缀",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "包含后缀",
|
|
||||||
Code: RuleOperatorSuffix,
|
|
||||||
Description: "包含某个后缀",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "包含索引",
|
|
||||||
Code: RuleOperatorHasKey,
|
|
||||||
Description: "对于一组数据拥有某个键值或者索引",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "版本号大于",
|
|
||||||
Code: RuleOperatorVersionGt,
|
|
||||||
Description: "对比版本号大于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "版本号小于",
|
|
||||||
Code: RuleOperatorVersionLt,
|
|
||||||
Description: "对比版本号小于",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "版本号范围",
|
|
||||||
Code: RuleOperatorVersionRange,
|
|
||||||
Description: "判断版本号在某个范围内,格式为version1,version2",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP等于",
|
|
||||||
Code: RuleOperatorEqIP,
|
|
||||||
Description: "将参数转换为IP进行对比",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP大于",
|
|
||||||
Code: RuleOperatorGtIP,
|
|
||||||
Description: "将参数转换为IP进行对比",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP大于等于",
|
|
||||||
Code: RuleOperatorGteIP,
|
|
||||||
Description: "将参数转换为IP进行对比",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP小于",
|
|
||||||
Code: RuleOperatorLtIP,
|
|
||||||
Description: "将参数转换为IP进行对比",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP小于等于",
|
|
||||||
Code: RuleOperatorLteIP,
|
|
||||||
Description: "将参数转换为IP进行对比",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP范围",
|
|
||||||
Code: RuleOperatorIPRange,
|
|
||||||
Description: "IP在某个范围之内,范围格式可以是英文逗号分隔的ip1,ip2,或者CIDR格式的ip/bits",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "不在IP范围",
|
|
||||||
Code: RuleOperatorNotIPRange,
|
|
||||||
Description: "IP不在某个范围之内,范围格式可以是英文逗号分隔的ip1,ip2,或者CIDR格式的ip/bits",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP取模10",
|
|
||||||
Code: RuleOperatorIPMod10,
|
|
||||||
Description: "对IP参数值取模,除数为10,对比值为余数",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP取模100",
|
|
||||||
Code: RuleOperatorIPMod100,
|
|
||||||
Description: "对IP参数值取模,除数为100,对比值为余数",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "IP取模",
|
|
||||||
Code: RuleOperatorIPMod,
|
|
||||||
Description: "对IP参数值取模,对比值格式为:除数,余数,比如10,1",
|
|
||||||
CaseInsensitive: RuleCaseInsensitiveNo,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,29 +16,38 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_Template(t *testing.T) {
|
func Test_Template(t *testing.T) {
|
||||||
a := assert.NewAssertion(t)
|
var a = assert.NewAssertion(t)
|
||||||
|
|
||||||
template := Template()
|
var waf = Template()
|
||||||
err := template.Init()
|
|
||||||
|
for _, group := range waf.Inbound {
|
||||||
|
group.IsOn = true
|
||||||
|
|
||||||
|
for _, set := range group.RuleSets {
|
||||||
|
set.IsOn = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := waf.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testTemplate1001(a, t, template)
|
testTemplate1001(a, t, waf)
|
||||||
testTemplate1002(a, t, template)
|
testTemplate1002(a, t, waf)
|
||||||
testTemplate1003(a, t, template)
|
testTemplate1003(a, t, waf)
|
||||||
testTemplate2001(a, t, template)
|
testTemplate2001(a, t, waf)
|
||||||
testTemplate3001(a, t, template)
|
testTemplate3001(a, t, waf)
|
||||||
testTemplate4001(a, t, template)
|
testTemplate4001(a, t, waf)
|
||||||
testTemplate5001(a, t, template)
|
testTemplate5001(a, t, waf)
|
||||||
testTemplate6001(a, t, template)
|
testTemplate6001(a, t, waf)
|
||||||
testTemplate7001(a, t, template)
|
testTemplate7001(a, t, waf)
|
||||||
testTemplate20001(a, t, template)
|
testTemplate20001(a, t, waf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Template2(t *testing.T) {
|
func Test_Template2(t *testing.T) {
|
||||||
reader := bytes.NewReader([]byte(strings.Repeat("HELLO", 1024)))
|
reader := bytes.NewReader([]byte(strings.Repeat("HELLO", 1024)))
|
||||||
req, err := http.NewRequest(http.MethodGet, "https://example.com/index.php?id=123", reader)
|
req, err := http.NewRequest(http.MethodPost, "https://example.com/index.php?id=123", reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -65,15 +74,25 @@ func Test_Template2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkTemplate(b *testing.B) {
|
func BenchmarkTemplate(b *testing.B) {
|
||||||
waf := Template()
|
var waf = Template()
|
||||||
|
|
||||||
|
for _, group := range waf.Inbound {
|
||||||
|
group.IsOn = true
|
||||||
|
|
||||||
|
for _, set := range group.RuleSets {
|
||||||
|
set.IsOn = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := waf.Init()
|
err := waf.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
reader := bytes.NewReader([]byte(strings.Repeat("Hello", 1024)))
|
req, err := http.NewRequest(http.MethodGet, "https://example.com/index.php?id=123", nil)
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://example.com/index.php?id=123", reader)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -312,6 +331,68 @@ func testTemplate7001(a *assert.Assertion, t *testing.T, template *WAF) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTemplateSQLInjection(t *testing.T) {
|
||||||
|
var template = Template()
|
||||||
|
errs := template.Init()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatal(errs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var group = template.FindRuleGroupWithCode("sqlInjection")
|
||||||
|
if group == nil {
|
||||||
|
t.Fatal("group not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//
|
||||||
|
//for _, set := range group.RuleSets {
|
||||||
|
// for _, rule := range set.Rules {
|
||||||
|
// t.Logf("%#v", rule.singleCheckpoint)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, "https://example.com/?id=1234", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, _, result, err := group.MatchRequest(requests.NewTestRequest(req))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if result != nil {
|
||||||
|
t.Log(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTemplateSQLInjection(b *testing.B) {
|
||||||
|
var template = Template()
|
||||||
|
errs := template.Init()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
b.Fatal(errs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var group = template.FindRuleGroupWithCode("sqlInjection")
|
||||||
|
if group == nil {
|
||||||
|
b.Fatal("group not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
req, err := http.NewRequest(http.MethodPost, "https://example.com/?id=1234", nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
_, _, result, err := group.MatchRequest(requests.NewTestRequest(req))
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
_ = result
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testTemplate20001(a *assert.Assertion, t *testing.T, template *WAF) {
|
func testTemplate20001(a *assert.Assertion, t *testing.T, template *WAF) {
|
||||||
// enable bot rule set
|
// enable bot rule set
|
||||||
for _, g := range template.Inbound {
|
for _, g := range template.Inbound {
|
||||||
|
|||||||
Reference in New Issue
Block a user