Merge pull request #674 from samwafgo/feat_rule_function

feat:rule function
This commit is contained in:
samwafgo
2026-02-06 16:43:09 +08:00
committed by GitHub
4 changed files with 834 additions and 1 deletions
+269
View File
@@ -0,0 +1,269 @@
package innerbean
import (
"bytes"
"net"
"strings"
)
// RuleFunc 规则函数助手,提供各种通用的规则判断函数
// 使用方式: 在DataContext中注册为 "RF",然后在规则中调用
// 例如: RF.IPInRange(MF.SRC_IP, "192.168.0.0", "192.168.1.254")
type RuleFunc struct{}
// NewRuleFunc 创建规则函数助手实例
func NewRuleFunc() *RuleFunc {
return &RuleFunc{}
}
// ================== IP 相关函数 ==================
// IPInRange 判断IP是否在指定范围内(包含起始和结束IP)
// ip: 要检查的IP地址
// startIP: 起始IP地址
// endIP: 结束IP地址
// 返回: true表示IP在范围内,false表示不在
// 使用示例: RF.IPInRange(MF.SRC_IP, "172.16.0.0", "172.20.255.254")
func (rf *RuleFunc) IPInRange(ip, startIP, endIP string) bool {
parsedIP := net.ParseIP(ip)
parsedStart := net.ParseIP(startIP)
parsedEnd := net.ParseIP(endIP)
if parsedIP == nil || parsedStart == nil || parsedEnd == nil {
return false
}
// 确保都是相同格式(IPv4或IPv6)
parsedIP = parsedIP.To16()
parsedStart = parsedStart.To16()
parsedEnd = parsedEnd.To16()
// IP >= startIP && IP <= endIP
return bytes.Compare(parsedIP, parsedStart) >= 0 && bytes.Compare(parsedIP, parsedEnd) <= 0
}
// IPInCIDR 判断IP是否在指定的CIDR网段内
// ip: 要检查的IP地址
// cidr: CIDR格式的网段,如 "192.168.1.0/24"
// 返回: true表示IP在网段内,false表示不在
// 使用示例: RF.IPInCIDR(MF.SRC_IP, "192.168.1.0/24")
func (rf *RuleFunc) IPInCIDR(ip, cidr string) bool {
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
return false
}
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return false
}
return ipNet.Contains(parsedIP)
}
// IPInRanges 判断IP是否在多个范围中的任意一个(类似SQL的IN操作)
// ip: 要检查的IP地址
// ranges: IP范围列表,格式为 "startIP-endIP" 或 CIDR格式 "192.168.0.0/24"
// 返回: true表示IP在任意一个范围内,false表示都不在
// 使用示例: RF.IPInRanges(MF.SRC_IP, "172.16.0.0-172.20.255.254", "192.168.0.0/24")
func (rf *RuleFunc) IPInRanges(ip string, ranges ...string) bool {
for _, r := range ranges {
// 检查是否是CIDR格式
if strings.Contains(r, "/") {
if rf.IPInCIDR(ip, r) {
return true
}
} else if strings.Contains(r, "-") {
// 范围格式: startIP-endIP
parts := strings.Split(r, "-")
if len(parts) == 2 {
if rf.IPInRange(ip, strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])) {
return true
}
}
} else {
// 单个IP精确匹配
if ip == strings.TrimSpace(r) {
return true
}
}
}
return false
}
// IPEquals 判断两个IP是否相等(支持IPv4和IPv6的标准化比较)
// ip1: 第一个IP地址
// ip2: 第二个IP地址
// 返回: true表示相等,false表示不相等
func (rf *RuleFunc) IPEquals(ip1, ip2 string) bool {
parsedIP1 := net.ParseIP(ip1)
parsedIP2 := net.ParseIP(ip2)
if parsedIP1 == nil || parsedIP2 == nil {
return ip1 == ip2 // 如果无法解析,直接字符串比较
}
return parsedIP1.Equal(parsedIP2)
}
// ================== 字符串相关函数 ==================
// In 判断值是否在给定的列表中(类似SQL的IN操作)
// value: 要检查的值
// list: 可能的值列表
// 返回: true表示值在列表中,false表示不在
// 使用示例: RF.In(MF.METHOD, "GET", "POST", "PUT")
func (rf *RuleFunc) In(value string, list ...string) bool {
for _, item := range list {
if value == item {
return true
}
}
return false
}
// InIgnoreCase 判断值是否在给定的列表中(忽略大小写,类似SQL的IN操作)
// value: 要检查的值
// list: 可能的值列表
// 返回: true表示值在列表中,false表示不在
// 使用示例: RF.InIgnoreCase(MF.METHOD, "get", "post", "put")
func (rf *RuleFunc) InIgnoreCase(value string, list ...string) bool {
valueLower := strings.ToLower(value)
for _, item := range list {
if valueLower == strings.ToLower(item) {
return true
}
}
return false
}
// ContainsAny 判断字符串是否包含给定列表中的任意一个
// value: 要检查的字符串
// list: 要搜索的子串列表
// 返回: true表示包含至少一个,false表示一个都不包含
// 使用示例: RF.ContainsAny(MF.USER_AGENT, "bot", "spider", "crawler")
func (rf *RuleFunc) ContainsAny(value string, list ...string) bool {
for _, item := range list {
if strings.Contains(value, item) {
return true
}
}
return false
}
// ContainsAnyIgnoreCase 判断字符串是否包含给定列表中的任意一个(忽略大小写)
// value: 要检查的字符串
// list: 要搜索的子串列表
// 返回: true表示包含至少一个,false表示一个都不包含
// 使用示例: RF.ContainsAnyIgnoreCase(MF.USER_AGENT, "Bot", "Spider", "Crawler")
func (rf *RuleFunc) ContainsAnyIgnoreCase(value string, list ...string) bool {
valueLower := strings.ToLower(value)
for _, item := range list {
if strings.Contains(valueLower, strings.ToLower(item)) {
return true
}
}
return false
}
// ContainsAll 判断字符串是否包含给定列表中的全部
// value: 要检查的字符串
// list: 要搜索的子串列表
// 返回: true表示包含全部,false表示至少缺少一个
// 使用示例: RF.ContainsAll(MF.URL, "/admin", ".php")
func (rf *RuleFunc) ContainsAll(value string, list ...string) bool {
for _, item := range list {
if !strings.Contains(value, item) {
return false
}
}
return len(list) > 0
}
// StartsWithAny 判断字符串是否以给定列表中的任意一个开头
// value: 要检查的字符串
// list: 可能的前缀列表
// 返回: true表示匹配至少一个前缀,false表示一个都不匹配
// 使用示例: RF.StartsWithAny(MF.URL, "/admin", "/api", "/manage")
func (rf *RuleFunc) StartsWithAny(value string, list ...string) bool {
for _, item := range list {
if strings.HasPrefix(value, item) {
return true
}
}
return false
}
// EndsWithAny 判断字符串是否以给定列表中的任意一个结尾
// value: 要检查的字符串
// list: 可能的后缀列表
// 返回: true表示匹配至少一个后缀,false表示一个都不匹配
// 使用示例: RF.EndsWithAny(MF.URL, ".php", ".asp", ".jsp")
func (rf *RuleFunc) EndsWithAny(value string, list ...string) bool {
for _, item := range list {
if strings.HasSuffix(value, item) {
return true
}
}
return false
}
// ================== 数值比较函数 ==================
// IntInRange 判断整数是否在指定范围内(包含边界)
// value: 要检查的整数
// min: 最小值
// max: 最大值
// 返回: true表示在范围内,false表示不在
// 使用示例: RF.IntInRange(MF.STATUS_CODE, 400, 499)
func (rf *RuleFunc) IntInRange(value, min, max int64) bool {
return value >= min && value <= max
}
// IntIn 判断整数是否在给定的列表中
// value: 要检查的整数
// list: 可能的值列表
// 返回: true表示在列表中,false表示不在
// 使用示例: RF.IntIn(MF.STATUS_CODE, 200, 201, 204)
func (rf *RuleFunc) IntIn(value int64, list ...int64) bool {
for _, item := range list {
if value == item {
return true
}
}
return false
}
// ================== 逻辑辅助函数 ==================
// Not 逻辑非
// value: 布尔值
// 返回: 取反后的值
// 使用示例: RF.Not(RF.IPInRange(MF.SRC_IP, "192.168.0.0", "192.168.1.254"))
func (rf *RuleFunc) Not(value bool) bool {
return !value
}
// IsEmpty 判断字符串是否为空
// value: 要检查的字符串
// 返回: true表示为空,false表示不为空
func (rf *RuleFunc) IsEmpty(value string) bool {
return value == ""
}
// IsNotEmpty 判断字符串是否不为空
// value: 要检查的字符串
// 返回: true表示不为空,false表示为空
func (rf *RuleFunc) IsNotEmpty(value string) bool {
return value != ""
}
// LengthBetween 判断字符串长度是否在指定范围内
// value: 要检查的字符串
// min: 最小长度
// max: 最大长度
// 返回: true表示长度在范围内,false表示不在
func (rf *RuleFunc) LengthBetween(value string, min, max int64) bool {
length := int64(len(value))
return length >= min && length <= max
}
+274
View File
@@ -0,0 +1,274 @@
package innerbean
import (
"testing"
)
func TestRuleFunc_IPInRange(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
ip string
startIP string
endIP string
expected bool
}{
{"IP在范围内-开始", "172.16.0.0", "172.16.0.0", "172.20.255.254", true},
{"IP在范围内-中间", "172.18.0.1", "172.16.0.0", "172.20.255.254", true},
{"IP在范围内-结束", "172.20.255.254", "172.16.0.0", "172.20.255.254", true},
{"IP在范围外-小于", "172.15.255.255", "172.16.0.0", "172.20.255.254", false},
{"IP在范围外-大于", "172.21.0.0", "172.16.0.0", "172.20.255.254", false},
{"192.168网段-在范围内", "192.168.0.100", "192.168.0.0", "192.168.1.254", true},
{"192.168网段-不在范围内", "192.168.2.0", "192.168.0.0", "192.168.1.254", false},
{"无效IP", "invalid", "172.16.0.0", "172.20.255.254", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.IPInRange(tt.ip, tt.startIP, tt.endIP)
if result != tt.expected {
t.Errorf("IPInRange(%s, %s, %s) = %v, want %v",
tt.ip, tt.startIP, tt.endIP, result, tt.expected)
}
})
}
}
func TestRuleFunc_IPInCIDR(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
ip string
cidr string
expected bool
}{
{"IP在CIDR内", "192.168.1.100", "192.168.1.0/24", true},
{"IP不在CIDR内", "192.168.2.100", "192.168.1.0/24", false},
{"大网段-在范围内", "10.0.50.1", "10.0.0.0/8", true},
{"大网段-不在范围内", "11.0.0.1", "10.0.0.0/8", false},
{"无效IP", "invalid", "192.168.1.0/24", false},
{"无效CIDR", "192.168.1.1", "invalid", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.IPInCIDR(tt.ip, tt.cidr)
if result != tt.expected {
t.Errorf("IPInCIDR(%s, %s) = %v, want %v",
tt.ip, tt.cidr, result, tt.expected)
}
})
}
}
func TestRuleFunc_IPInRanges(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
ip string
ranges []string
expected bool
}{
{
"IP在第一个范围内",
"172.18.0.1",
[]string{"172.16.0.0-172.20.255.254", "192.168.0.0-192.168.1.254"},
true,
},
{
"IP在第二个范围内",
"192.168.0.100",
[]string{"172.16.0.0-172.20.255.254", "192.168.0.0-192.168.1.254"},
true,
},
{
"IP不在任何范围内",
"10.0.0.1",
[]string{"172.16.0.0-172.20.255.254", "192.168.0.0-192.168.1.254"},
false,
},
{
"混合格式-CIDR和范围",
"192.168.1.50",
[]string{"10.0.0.0/8", "192.168.0.0-192.168.1.254"},
true,
},
{
"混合格式-在CIDR内",
"10.0.0.50",
[]string{"10.0.0.0/8", "192.168.0.0-192.168.1.254"},
true,
},
{
"单个IP匹配",
"127.0.0.1",
[]string{"127.0.0.1", "192.168.0.1"},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.IPInRanges(tt.ip, tt.ranges...)
if result != tt.expected {
t.Errorf("IPInRanges(%s, %v) = %v, want %v",
tt.ip, tt.ranges, result, tt.expected)
}
})
}
}
func TestRuleFunc_In(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
value string
list []string
expected bool
}{
{"值在列表中", "GET", []string{"GET", "POST", "PUT"}, true},
{"值不在列表中", "DELETE", []string{"GET", "POST", "PUT"}, false},
{"空列表", "GET", []string{}, false},
{"精确匹配", "get", []string{"GET", "POST"}, false}, // 大小写敏感
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.In(tt.value, tt.list...)
if result != tt.expected {
t.Errorf("In(%s, %v) = %v, want %v",
tt.value, tt.list, result, tt.expected)
}
})
}
}
func TestRuleFunc_InIgnoreCase(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
value string
list []string
expected bool
}{
{"忽略大小写匹配", "get", []string{"GET", "POST", "PUT"}, true},
{"忽略大小写匹配2", "Get", []string{"get", "post"}, true},
{"不匹配", "delete", []string{"GET", "POST"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.InIgnoreCase(tt.value, tt.list...)
if result != tt.expected {
t.Errorf("InIgnoreCase(%s, %v) = %v, want %v",
tt.value, tt.list, result, tt.expected)
}
})
}
}
func TestRuleFunc_ContainsAny(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
value string
list []string
expected bool
}{
{"包含其中一个", "Mozilla/5.0 Googlebot", []string{"bot", "spider", "crawler"}, true},
{"包含多个", "Googlebot spider", []string{"bot", "spider"}, true},
{"不包含任何", "Mozilla/5.0 Chrome", []string{"bot", "spider"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.ContainsAny(tt.value, tt.list...)
if result != tt.expected {
t.Errorf("ContainsAny(%s, %v) = %v, want %v",
tt.value, tt.list, result, tt.expected)
}
})
}
}
func TestRuleFunc_IntInRange(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
value int64
min int64
max int64
expected bool
}{
{"在范围内", 404, 400, 499, true},
{"最小边界", 400, 400, 499, true},
{"最大边界", 499, 400, 499, true},
{"小于范围", 399, 400, 499, false},
{"大于范围", 500, 400, 499, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.IntInRange(tt.value, tt.min, tt.max)
if result != tt.expected {
t.Errorf("IntInRange(%d, %d, %d) = %v, want %v",
tt.value, tt.min, tt.max, result, tt.expected)
}
})
}
}
func TestRuleFunc_StartsWithAny(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
value string
list []string
expected bool
}{
{"以其中一个开头", "/admin/users", []string{"/admin", "/api", "/manage"}, true},
{"不以任何一个开头", "/user/profile", []string{"/admin", "/api"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.StartsWithAny(tt.value, tt.list...)
if result != tt.expected {
t.Errorf("StartsWithAny(%s, %v) = %v, want %v",
tt.value, tt.list, result, tt.expected)
}
})
}
}
func TestRuleFunc_EndsWithAny(t *testing.T) {
rf := NewRuleFunc()
tests := []struct {
name string
value string
list []string
expected bool
}{
{"以其中一个结尾", "/admin/login.php", []string{".php", ".asp", ".jsp"}, true},
{"不以任何一个结尾", "/admin/login.html", []string{".php", ".asp"}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := rf.EndsWithAny(tt.value, tt.list...)
if result != tt.expected {
t.Errorf("EndsWithAny(%s, %v) = %v, want %v",
tt.value, tt.list, result, tt.expected)
}
})
}
}
+8 -1
View File
@@ -6,11 +6,12 @@ import (
"SamWaf/model"
"errors"
"fmt"
"regexp"
"github.com/hyperjumptech/grule-rule-engine/ast"
"github.com/hyperjumptech/grule-rule-engine/builder"
"github.com/hyperjumptech/grule-rule-engine/engine"
"github.com/hyperjumptech/grule-rule-engine/pkg"
"regexp"
)
// 规则帮助类
@@ -80,6 +81,7 @@ func (rulehelper *RuleHelper) LoadRules(ruleconfig []model.Rules) (string, error
func (rulehelper *RuleHelper) Exec(key string, ruleinfo *innerbean.WAF_REQUEST_FULL) error {
dataCtx := ast.NewDataContext()
dataCtx.Add(key, ruleinfo)
dataCtx.Add("RF", innerbean.NewRuleFunc()) // 注册规则函数助手
err := rulehelper.engine.Execute(dataCtx, rulehelper.KnowledgeBase)
if err != nil {
zlog.Error("Exec", err)
@@ -97,6 +99,7 @@ func (rulehelper *RuleHelper) Match(key string, ruleinfo *innerbean.WebLog) ([]*
}()
dataCtx := ast.NewDataContext()
dataCtx.Add(key, ruleinfo)
dataCtx.Add("RF", innerbean.NewRuleFunc()) // 注册规则函数助手
if rulehelper.KnowledgeBase == nil {
return nil, errors.New("没有规则数据")
}
@@ -111,6 +114,10 @@ func (rulehelper *RuleHelper) CheckRuleAvailable(ruleText string) error {
if err != nil {
return err
}
err = dataCtx.Add("RF", innerbean.NewRuleFunc()) // 注册规则函数助手
if err != nil {
return err
}
knowledgeLibrary := ast.NewKnowledgeLibrary()
ruleBuilder := builder.NewRuleBuilder(knowledgeLibrary)
+283
View File
@@ -0,0 +1,283 @@
package utils
import (
"SamWaf/innerbean"
"SamWaf/model"
"testing"
)
// TestUserRule_IPInRanges 测试用户原始规则的改写版本 - IPInRanges 方式
func TestUserRule_IPInRanges(t *testing.T) {
ruleHelper := &RuleHelper{}
ruleHelper.InitRuleEngine()
// 用户改写后的规则 - 使用 IPInRanges(推荐方式)
drls := `
rule R835f9bf09867473dbe873027241db107 "允许特定内网网段访问" salience 10 {
when
RF.IPInRanges(MF.SRC_IP, "172.16.0.0-172.20.255.254", "192.168.0.0-192.168.1.254") == true
then
Retract("R835f9bf09867473dbe873027241db107");
}`
var ruleconfigs []model.Rules
rule := model.Rules{
HostCode: "",
RuleCode: "R835f9bf09867473dbe873027241db107",
RuleName: "允许特定内网网段访问",
RuleContent: drls,
RuleContentJSON: "",
RuleVersionName: "1.0",
RuleVersion: 1,
IsPublicRule: 0,
IsManualRule: 1,
RuleStatus: 1,
}
ruleconfigs = append(ruleconfigs, rule)
_, err := ruleHelper.LoadRules(ruleconfigs)
if err != nil {
t.Errorf("加载规则失败: %v", err)
return
}
// 测试用例
testCases := []struct {
name string
ip string
expected bool // true 表示应该匹配规则(在范围内),false 表示不应该匹配
}{
{"172网段-起始IP", "172.16.0.0", true},
{"172网段-中间IP", "172.18.100.50", true},
{"172网段-结束IP", "172.20.255.254", true},
{"172网段-超出范围", "172.21.0.0", false},
{"192.168网段-起始IP", "192.168.0.0", true},
{"192.168网段-中间IP", "192.168.0.100", true},
{"192.168网段-结束IP", "192.168.1.254", true},
{"192.168网段-超出范围", "192.168.2.0", false},
{"外网IP", "8.8.8.8", false},
{"本地IP", "127.0.0.1", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
webLog := &innerbean.WebLog{
SRC_IP: tc.ip,
}
ruleMatches, err := ruleHelper.Match("MF", webLog)
if err != nil {
t.Errorf("规则匹配错误: %v", err)
return
}
matched := len(ruleMatches) > 0
if matched != tc.expected {
t.Errorf("IP %s: 期望匹配=%v, 实际匹配=%v", tc.ip, tc.expected, matched)
} else {
t.Logf("✓ IP %s: 匹配结果正确 (匹配=%v)", tc.ip, matched)
}
})
}
}
// TestUserRule_IPInRange 测试用户原始规则的改写版本 - IPInRange 方式
func TestUserRule_IPInRange(t *testing.T) {
ruleHelper := &RuleHelper{}
ruleHelper.InitRuleEngine()
// 用户改写后的规则 - 使用多个 IPInRange
drls := `
rule R835f9bf09867473dbe873027241db107 "允许特定内网网段访问" salience 10 {
when
RF.IPInRange(MF.SRC_IP, "172.16.0.0", "172.20.255.254") == true ||
RF.IPInRange(MF.SRC_IP, "192.168.0.0", "192.168.1.254") == true
then
Retract("R835f9bf09867473dbe873027241db107");
}`
var ruleconfigs []model.Rules
rule := model.Rules{
HostCode: "",
RuleCode: "R835f9bf09867473dbe873027241db107",
RuleName: "允许特定内网网段访问",
RuleContent: drls,
RuleContentJSON: "",
RuleVersionName: "1.0",
RuleVersion: 1,
IsPublicRule: 0,
IsManualRule: 1,
RuleStatus: 1,
}
ruleconfigs = append(ruleconfigs, rule)
_, err := ruleHelper.LoadRules(ruleconfigs)
if err != nil {
t.Errorf("加载规则失败: %v", err)
return
}
// 测试 172 网段内的 IP
webLog := &innerbean.WebLog{
SRC_IP: "172.18.0.1",
}
ruleMatches, err := ruleHelper.Match("MF", webLog)
if err != nil {
t.Errorf("规则匹配错误: %v", err)
return
}
if len(ruleMatches) == 0 {
t.Errorf("172.18.0.1 应该匹配规则")
} else {
t.Logf("✓ 规则匹配成功: %s", ruleMatches[0].RuleDescription)
}
// 测试外网 IP
webLog2 := &innerbean.WebLog{
SRC_IP: "8.8.8.8",
}
ruleMatches2, err := ruleHelper.Match("MF", webLog2)
if err != nil {
t.Errorf("规则匹配错误: %v", err)
return
}
if len(ruleMatches2) > 0 {
t.Errorf("8.8.8.8 不应该匹配规则")
} else {
t.Logf("✓ 外网IP正确未匹配")
}
}
// TestUserRule_CIDR 测试用户规则 - CIDR 格式
func TestUserRule_CIDR(t *testing.T) {
ruleHelper := &RuleHelper{}
ruleHelper.InitRuleEngine()
// 使用 CIDR 格式
drls := `
rule R835f9bf09867473dbe873027241db108 "允许CIDR网段访问" salience 10 {
when
RF.IPInRanges(MF.SRC_IP, "192.168.1.0/24", "10.0.0.0/8") == true
then
Retract("R835f9bf09867473dbe873027241db108");
}`
var ruleconfigs []model.Rules
rule := model.Rules{
HostCode: "",
RuleCode: "R835f9bf09867473dbe873027241db108",
RuleName: "允许CIDR网段访问",
RuleContent: drls,
RuleContentJSON: "",
RuleVersionName: "1.0",
RuleVersion: 1,
IsPublicRule: 0,
IsManualRule: 1,
RuleStatus: 1,
}
ruleconfigs = append(ruleconfigs, rule)
_, err := ruleHelper.LoadRules(ruleconfigs)
if err != nil {
t.Errorf("加载规则失败: %v", err)
return
}
// 测试用例
testCases := []struct {
name string
ip string
expected bool
}{
{"192.168.1网段", "192.168.1.100", true},
{"10网段", "10.0.0.1", true},
{"10网段大范围", "10.255.255.255", true},
{"不在范围内", "192.168.2.1", false},
{"不在范围内2", "11.0.0.1", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
webLog := &innerbean.WebLog{
SRC_IP: tc.ip,
}
ruleMatches, err := ruleHelper.Match("MF", webLog)
if err != nil {
t.Errorf("规则匹配错误: %v", err)
return
}
matched := len(ruleMatches) > 0
if matched != tc.expected {
t.Errorf("IP %s: 期望匹配=%v, 实际匹配=%v", tc.ip, tc.expected, matched)
} else {
t.Logf("✓ IP %s: 匹配结果正确 (匹配=%v)", tc.ip, matched)
}
})
}
}
// TestUserRule_ReversedLogic 测试用户规则 - 反转逻辑(禁止内网访问)
func TestUserRule_ReversedLogic(t *testing.T) {
ruleHelper := &RuleHelper{}
ruleHelper.InitRuleEngine()
// 如果用户想要禁止这些内网IP访问
drls := `
rule R835f9bf09867473dbe873027241db107 "禁止特定内网网段访问" salience 10 {
when
RF.IPInRanges(MF.SRC_IP, "172.16.0.0-172.20.255.254", "192.168.0.0-192.168.1.254") == false
then
Retract("R835f9bf09867473dbe873027241db107");
}`
var ruleconfigs []model.Rules
rule := model.Rules{
HostCode: "",
RuleCode: "R835f9bf09867473dbe873027241db107",
RuleName: "禁止特定内网网段访问",
RuleContent: drls,
RuleContentJSON: "",
RuleVersionName: "1.0",
RuleVersion: 1,
IsPublicRule: 0,
IsManualRule: 1,
RuleStatus: 1,
}
ruleconfigs = append(ruleconfigs, rule)
_, err := ruleHelper.LoadRules(ruleconfigs)
if err != nil {
t.Errorf("加载规则失败: %v", err)
return
}
// 内网IP不应该匹配(因为我们是禁止内网)
webLog1 := &innerbean.WebLog{
SRC_IP: "172.18.0.1",
}
ruleMatches1, _ := ruleHelper.Match("MF", webLog1)
if len(ruleMatches1) > 0 {
t.Errorf("内网IP 172.18.0.1 不应该匹配规则(应该被排除)")
} else {
t.Logf("✓ 内网IP正确未匹配(被禁止)")
}
// 外网IP应该匹配
webLog2 := &innerbean.WebLog{
SRC_IP: "8.8.8.8",
}
ruleMatches2, _ := ruleHelper.Match("MF", webLog2)
if len(ruleMatches2) == 0 {
t.Errorf("外网IP 8.8.8.8 应该匹配规则")
} else {
t.Logf("✓ 外网IP正确匹配")
}
}