diff --git a/pkg/serverconfigs/filterconfigs/filter_base64_decode.go b/pkg/serverconfigs/filterconfigs/filter_base64_decode.go new file mode 100644 index 0000000..ae0df17 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_base64_decode.go @@ -0,0 +1,21 @@ +package filterconfigs + +import ( + "encoding/base64" +) + +type Base64DecodeFilter struct { +} + +func (this *Base64DecodeFilter) Init() error { + return nil +} + +func (this *Base64DecodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) { + output, err = base64.StdEncoding.DecodeString(ToString(input)) + if err != nil { + return + } + goNext = true + return +} diff --git a/pkg/serverconfigs/filterconfigs/filter_base64_decode_test.go b/pkg/serverconfigs/filterconfigs/filter_base64_decode_test.go new file mode 100644 index 0000000..8328008 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_base64_decode_test.go @@ -0,0 +1,14 @@ +package filterconfigs + +import ( + "encoding/base64" + "testing" +) + +func TestBase64DecodeFilter_Do(t *testing.T) { + filter := &Base64DecodeFilter{} + t.Log(filter.Do("123456", nil)) + + encodedString := base64.StdEncoding.EncodeToString([]byte("hello")) + t.Log(filter.Do(encodedString, nil)) +} diff --git a/pkg/serverconfigs/filterconfigs/filter_base64_encode.go b/pkg/serverconfigs/filterconfigs/filter_base64_encode.go new file mode 100644 index 0000000..764bd0f --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_base64_encode.go @@ -0,0 +1,19 @@ +package filterconfigs + +import ( + "encoding/base64" +) + +type Base64EncodeFilter struct { +} + +func (this *Base64EncodeFilter) Init() error { + return nil +} + +func (this *Base64EncodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) { + data := ToBytes(input) + output = base64.StdEncoding.EncodeToString(data) + goNext = true + return +} diff --git a/pkg/serverconfigs/filterconfigs/filter_base64_encode_test.go b/pkg/serverconfigs/filterconfigs/filter_base64_encode_test.go new file mode 100644 index 0000000..36b28ba --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_base64_encode_test.go @@ -0,0 +1,28 @@ +package filterconfigs + +import ( + "encoding/base64" + "github.com/iwind/TeaGo/assert" + "testing" +) + +func TestBase64EncodeFilter_Do(t *testing.T) { + a := assert.NewAssertion(t) + + filter := &Base64EncodeFilter{} + t.Log(filter.Do("hello", nil)) + t.Log(filter.Do("=", nil)) + + output, goNext, err := filter.Do("123456", nil) + if err != nil { + t.Fatal(err) + } + a.IsTrue(goNext) + + outputString := output.(string) + result, err := base64.StdEncoding.DecodeString(outputString) + if err != nil { + t.Fatal(err) + } + t.Log("origin:", string(result)) +} diff --git a/pkg/serverconfigs/filterconfigs/filter_interface.go b/pkg/serverconfigs/filterconfigs/filter_interface.go new file mode 100644 index 0000000..9ffa347 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_interface.go @@ -0,0 +1,10 @@ +package filterconfigs + +// 过滤接口 +type FilterInterface interface { + // 初始化 + Init() error + + // 执行过滤 + Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) +} diff --git a/pkg/serverconfigs/filterconfigs/filter_length.go b/pkg/serverconfigs/filterconfigs/filter_length.go new file mode 100644 index 0000000..fcfc4d4 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_length.go @@ -0,0 +1,16 @@ +package filterconfigs + +type LengthFilter struct { +} + +// 初始化 +func (this *LengthFilter) Init() error { + return nil +} + +// 执行过滤 +func (this *LengthFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) { + output = len(ToBytes(input)) + goNext = true + return +} diff --git a/pkg/serverconfigs/filterconfigs/filter_length_test.go b/pkg/serverconfigs/filterconfigs/filter_length_test.go new file mode 100644 index 0000000..561fb8f --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_length_test.go @@ -0,0 +1,9 @@ +package filterconfigs + +import "testing" + +func TestLengthFilter_Do(t *testing.T) { + filter := &LengthFilter{} + t.Log(filter.Do("hello", nil)) + t.Log(filter.Do([]byte("hello"), nil)) +} diff --git a/pkg/serverconfigs/filterconfigs/filter_md5.go b/pkg/serverconfigs/filterconfigs/filter_md5.go new file mode 100644 index 0000000..f321dc3 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_md5.go @@ -0,0 +1,23 @@ +package filterconfigs + +import ( + "crypto/md5" + "fmt" +) + +type Md5Filter struct { +} + +func (this *Md5Filter) Init() error { + return nil +} + +func (this *Md5Filter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) { + data := ToBytes(input) + m := md5.New() + m.Write(data) + result := m.Sum(nil) + output = fmt.Sprintf("%x", result) + goNext = true + return +} diff --git a/pkg/serverconfigs/filterconfigs/filter_md5_test.go b/pkg/serverconfigs/filterconfigs/filter_md5_test.go new file mode 100644 index 0000000..60f2304 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_md5_test.go @@ -0,0 +1,11 @@ +package filterconfigs + +import "testing" + +func TestMd5Filter_Do(t *testing.T) { + filter := &Md5Filter{} + t.Log(filter.Do("123456", nil)) + t.Log(filter.Do(nil, nil)) + t.Log(filter.Do("", nil)) + t.Log(filter.Do("hello", nil)) +} diff --git a/pkg/serverconfigs/filterconfigs/filter_urldecode.go b/pkg/serverconfigs/filterconfigs/filter_urldecode.go new file mode 100644 index 0000000..0500e61 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_urldecode.go @@ -0,0 +1,19 @@ +package filterconfigs + +import "net/url" + +type URLDecodeFilter struct { +} + +func (this *URLDecodeFilter) Init() error { + return nil +} + +func (this *URLDecodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) { + output, err = url.QueryUnescape(ToString(input)) + if err != nil { + return + } + goNext = true + return +} diff --git a/pkg/serverconfigs/filterconfigs/filter_urldecode_test.go b/pkg/serverconfigs/filterconfigs/filter_urldecode_test.go new file mode 100644 index 0000000..e0970ea --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_urldecode_test.go @@ -0,0 +1,13 @@ +package filterconfigs + +import ( + "net/url" + "testing" +) + +func TestURLDecodeFilter_Do(t *testing.T) { + filter := &URLDecodeFilter{} + t.Log(filter.Do("hello", nil)) + t.Log(filter.Do(url.QueryEscape("/hello/world/?a=b&c=d"), nil)) + t.Log(filter.Do("/hello/world/?a=b&c=d", nil)) +} diff --git a/pkg/serverconfigs/filterconfigs/filter_urlencode.go b/pkg/serverconfigs/filterconfigs/filter_urlencode.go new file mode 100644 index 0000000..14b7240 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_urlencode.go @@ -0,0 +1,16 @@ +package filterconfigs + +import "net/url" + +type URLEncodeFilter struct { +} + +func (this *URLEncodeFilter) Init() error { + return nil +} + +func (this *URLEncodeFilter) Do(input interface{}, options interface{}) (output interface{}, goNext bool, err error) { + output = url.QueryEscape(ToString(input)) + goNext = true + return +} diff --git a/pkg/serverconfigs/filterconfigs/filter_urlencode_test.go b/pkg/serverconfigs/filterconfigs/filter_urlencode_test.go new file mode 100644 index 0000000..015ef2d --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filter_urlencode_test.go @@ -0,0 +1,9 @@ +package filterconfigs + +import "testing" + +func TestURLEncodeFilter_Do(t *testing.T) { + filter := &URLEncodeFilter{} + t.Log(filter.Do("hello", nil)) + t.Log(filter.Do("/hello/world?a=b&c=中文&d=", nil)) +} diff --git a/pkg/serverconfigs/filterconfigs/filters.go b/pkg/serverconfigs/filterconfigs/filters.go new file mode 100644 index 0000000..be220f1 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/filters.go @@ -0,0 +1,27 @@ +package filterconfigs + +import "github.com/iwind/TeaGo/logs" + +func init() { + for code, filter := range allFilters { + err := filter.Init() + if err != nil { + logs.Println("[FILTER]init '" + code + "' failed: " + err.Error()) + } + } +} + +// 所有的筛选条件 +var allFilters = map[string]FilterInterface{ + "md5": new(Md5Filter), + "urlEncode": new(URLEncodeFilter), + "urlDecode": new(URLDecodeFilter), + "base64Encode": new(Base64EncodeFilter), + "base64Decode": new(Base64DecodeFilter), + "length": new(LengthFilter), +} + +// 查找Filter +func FindFilter(code string) FilterInterface { + return allFilters[code] +} diff --git a/pkg/serverconfigs/filterconfigs/utils.go b/pkg/serverconfigs/filterconfigs/utils.go new file mode 100644 index 0000000..aad4a55 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/utils.go @@ -0,0 +1,25 @@ +package filterconfigs + +import "github.com/iwind/TeaGo/types" + +// 将输入内容转换为字节 +func ToBytes(input interface{}) []byte { + if input == nil { + return []byte{} + } + var data []byte + var ok bool + data, ok = input.([]byte) + if ok { + return data + } + return []byte(types.String(input)) +} + +// 将输入内容转换为字符串 +func ToString(input interface{}) string { + if input == nil { + return "" + } + return types.String(input) +} diff --git a/pkg/serverconfigs/filterconfigs/utils_test.go b/pkg/serverconfigs/filterconfigs/utils_test.go new file mode 100644 index 0000000..9e02263 --- /dev/null +++ b/pkg/serverconfigs/filterconfigs/utils_test.go @@ -0,0 +1,9 @@ +package filterconfigs + +import "testing" + +func TestToBytes(t *testing.T) { + t.Log(ToBytes("hello")) + t.Log(ToBytes(123)) + t.Log(ToBytes([]byte{1, 2, 3})) +} diff --git a/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoint_definition.go b/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoint_definition.go index 450a5f6..837db26 100644 --- a/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoint_definition.go +++ b/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoint_definition.go @@ -18,6 +18,7 @@ type HTTPFirewallCheckpointDefinition struct { Description string `json:"description"` // 描述 Prefix string `json:"prefix"` // 前缀 IsRequest bool `json:"isRequest"` // 是否为请求 + HasParams bool `json:"hasParams"` // 是否有子参数 Params []*KeyValue `json:"params"` // 参数 Options []OptionInterface `json:"options"` // 选项 IsComposed bool `json:"isComposed"` // 是否为组合的checkpoint diff --git a/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoints.go b/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoints.go index 6b8bb62..cac4431 100644 --- a/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoints.go +++ b/pkg/serverconfigs/firewallconfigs/http_firewall_checkpoints.go @@ -80,6 +80,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{ Prefix: "requestForm", Description: "获取POST或者其他方法发送的表单参数,最大请求体限制32M", IsRequest: true, + HasParams: true, }, { Name: "上传文件", @@ -93,12 +94,14 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{ NewKeyValue("表单字段名", "field"), }, IsRequest: true, + HasParams: true, }, { Name: "请求JSON参数", Prefix: "requestJSON", Description: "获取POST或者其他方法发送的JSON,最大请求体限制32M,使用点(.)符号表示多级数据", IsRequest: true, + HasParams: true, }, { Name: "请求方法", @@ -153,6 +156,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{ Prefix: "cookie", Description: "单个cookie值", IsRequest: true, + HasParams: true, }, { Name: "所有URL参数组合", @@ -165,6 +169,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{ Prefix: "arg", Description: "单个URL参数值", IsRequest: true, + HasParams: true, }, { Name: "所有Header信息", @@ -177,11 +182,13 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{ Prefix: "header", Description: "单个Header值", IsRequest: true, + HasParams: true, }, { Name: "CC统计", Prefix: "cc", Description: "统计某段时间段内的请求信息", + HasParams: true, Params: []*KeyValue{ NewKeyValue("请求数", "requests"), }, @@ -267,6 +274,7 @@ var AllCheckpoints = []*HTTPFirewallCheckpointDefinition{ Prefix: "responseHeader", Description: "响应Header值", IsRequest: false, + HasParams: true, }, { Name: "响应内容", diff --git a/pkg/serverconfigs/firewallconfigs/http_firewall_rule.go b/pkg/serverconfigs/firewallconfigs/http_firewall_rule.go index c08ebc1..1314a96 100644 --- a/pkg/serverconfigs/firewallconfigs/http_firewall_rule.go +++ b/pkg/serverconfigs/firewallconfigs/http_firewall_rule.go @@ -11,6 +11,7 @@ type HTTPFirewallRule struct { Id int64 `yaml:"id" json:"id"` IsOn bool `yaml:"isOn" json:"isOn"` Param string `yaml:"param" json:"param"` + ParamFilters []*ParamFilter `yaml:"paramFilters" json:"paramFilters"` Operator string `yaml:"operator" json:"operator"` Value string `yaml:"value" json:"value"` IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"` diff --git a/pkg/serverconfigs/firewallconfigs/http_firewall_rule_operator.go b/pkg/serverconfigs/firewallconfigs/http_firewall_rule_operator.go index 4d4acf4..643f960 100644 --- a/pkg/serverconfigs/firewallconfigs/http_firewall_rule_operator.go +++ b/pkg/serverconfigs/firewallconfigs/http_firewall_rule_operator.go @@ -23,6 +23,9 @@ const ( HTTPFirewallRuleOperatorVersionLt HTTPFirewallRuleOperator = "version lt" HTTPFirewallRuleOperatorVersionRange HTTPFirewallRuleOperator = "version range" + HTTPFirewallRuleOperatorContainsBinary HTTPFirewallRuleOperator = "contains binary" // contains binary + HTTPFirewallRuleOperatorNotContainsBinary HTTPFirewallRuleOperator = "not contains binary" // not contains binary + // ip HTTPFirewallRuleOperatorEqIP HTTPFirewallRuleOperator = "eq ip" HTTPFirewallRuleOperatorGtIP HTTPFirewallRuleOperator = "gt ip" @@ -132,6 +135,18 @@ var AllRuleOperators = []*RuleOperatorDefinition{ Description: "包含某个后缀", CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo, }, + { + Name: "包含二进制数据", + Code: HTTPFirewallRuleOperatorContainsBinary, + Description: "包含一组二进制数据", + CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo, + }, + { + Name: "不包含二进制数据", + Code: HTTPFirewallRuleOperatorNotContainsBinary, + Description: "不包含一组二进制数据", + CaseInsensitive: HTTPFirewallRuleCaseInsensitiveNo, + }, { Name: "包含索引", Code: HTTPFirewallRuleOperatorHasKey, diff --git a/pkg/serverconfigs/firewallconfigs/param_filter.go b/pkg/serverconfigs/firewallconfigs/param_filter.go new file mode 100644 index 0000000..8b13212 --- /dev/null +++ b/pkg/serverconfigs/firewallconfigs/param_filter.go @@ -0,0 +1,10 @@ +package firewallconfigs + +import "github.com/iwind/TeaGo/maps" + +// 对参数的过滤器 +type ParamFilter struct { + Code string `yaml:"code" json:"code"` // 过滤器编号 + Name string `yaml:"name" json:"name"` // 名称 + Options maps.Map `yaml:"options" json:"options"` // 过滤器选项 +}