Initial commit (code only without large binaries)
This commit is contained in:
308
EdgeAdmin/internal/utils/exce/excelize.go
Normal file
308
EdgeAdmin/internal/utils/exce/excelize.go
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
@Author: 1usir
|
||||
@Description:
|
||||
@File: excelize
|
||||
@Version: 1.0.0
|
||||
@Date: 2024/2/21 14:10
|
||||
*/
|
||||
|
||||
package exce
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Rule struct {
|
||||
Name string `json:"name"` // 序号 - 规则集名字
|
||||
Type string `json:"type"` // 攻击类型 - 规则分组名称
|
||||
Regular string `json:"regular"` // 正则
|
||||
Regulars []string `json:"regulars"` // 正则集合
|
||||
Level string `json:"level"` // 威胁等级
|
||||
Position []string `json:"position"` // 参数位置 - 参数
|
||||
Description string `json:"description"` // 描述 - 备注
|
||||
CVE string `json:"cve"` // cve 编号
|
||||
Inbound bool `json:"inbound"` // 入站规则
|
||||
Outbound bool `json:"outbound"` // 出站规则
|
||||
IsAnd bool `json:"is_and"` // 多条件
|
||||
}
|
||||
|
||||
func saveErr(sheet string, nf *excelize.File, cols []string, sheetIndexs map[string]int) {
|
||||
index, ok := sheetIndexs[sheet]
|
||||
if !ok {
|
||||
nf.NewSheet(sheet)
|
||||
// 设置单元格的值
|
||||
nf.SetCellValue(sheet, "A1", "序号")
|
||||
nf.SetCellValue(sheet, "B1", "攻击类型")
|
||||
nf.SetCellValue(sheet, "C1", "关键词")
|
||||
nf.SetCellValue(sheet, "D1", "正则")
|
||||
nf.SetCellValue(sheet, "E1", "威胁等级")
|
||||
nf.SetCellValue(sheet, "F1", "攻击语句")
|
||||
nf.SetCellValue(sheet, "G1", "攻击语句解码后")
|
||||
nf.SetCellValue(sheet, "H1", "参数位置")
|
||||
nf.SetCellValue(sheet, "I1", "描述")
|
||||
nf.SetCellValue(sheet, "J1", "CVE编号")
|
||||
nf.SetCellValue(sheet, "K1", "备注")
|
||||
nf.SetCellValue(sheet, "L1", "错误原因")
|
||||
sheetIndexs[sheet] = 2
|
||||
index = 2
|
||||
}
|
||||
for i, col := range cols {
|
||||
nf.SetCellValue(sheet, fmt.Sprintf("%c%d", 'A'+i, index), col)
|
||||
}
|
||||
sheetIndexs[sheet]++
|
||||
}
|
||||
|
||||
func ParseRules(r io.Reader) (*bytes.Buffer, []*Rule, error) {
|
||||
nf := excelize.NewFile()
|
||||
nf.DeleteSheet("Sheet1")
|
||||
f, err := excelize.OpenReader(r)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
res := make([]*Rule, 0)
|
||||
sheets := f.GetSheetList()
|
||||
sheetIndexs := map[string]int{}
|
||||
for _, sheet := range sheets {
|
||||
rows, err := f.GetRows(sheet)
|
||||
if err != nil || len(rows) <= 1 {
|
||||
return nil, nil, err
|
||||
}
|
||||
/*
|
||||
1 2 3 4 5 6 7 8 9 10 11 12
|
||||
序号|攻击类型|关键字|正则|威胁等级|攻击语句|攻击语句解码后|参数位置|描述|CVE编号|备注|错误原因
|
||||
*/
|
||||
|
||||
for _, row := range rows[1:] {
|
||||
cols := make([]string, 12)
|
||||
copy(cols, row)
|
||||
if len(cols) < 8 || cols[0] == "" || cols[1] == "" || cols[3] == "" || cols[7] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
r := &Rule{
|
||||
Name: strings.TrimSpace(cols[0]),
|
||||
Type: strings.TrimSpace(cols[1]),
|
||||
Regular: strings.TrimSpace(cols[3]),
|
||||
Level: strings.TrimSpace(cols[4]),
|
||||
Position: strings.Split(cols[7], "\n"),
|
||||
}
|
||||
if strings.Contains(r.Regular, "\n") {
|
||||
//fmt.Println(fmt.Sprintf("无效规则1:Sheet[%s|%s] %s", sheet, r.Name, r.Regular))
|
||||
//return nil, errors.New(fmt.Sprintf("无效规则:Sheet[%s|%s] %s", sheet, r.Name, r.Regular))
|
||||
// 创建错误新表格
|
||||
cols[11] = "无效正则"
|
||||
saveErr(sheet, nf, cols, sheetIndexs)
|
||||
continue
|
||||
}
|
||||
if len(cols) > 8 {
|
||||
r.Description = cols[8]
|
||||
}
|
||||
if len(cols) > 9 {
|
||||
r.CVE = cols[9]
|
||||
}
|
||||
// 特殊处理
|
||||
if r.Type == "xss注入" {
|
||||
r.Type = "XSS"
|
||||
}
|
||||
// 支持多条件
|
||||
var regulars []string
|
||||
var positions []string
|
||||
|
||||
if strings.Contains(r.Regular, "且") {
|
||||
regulars, positions, err = parseRegulars(r.Regular)
|
||||
if err != nil {
|
||||
//fmt.Println(fmt.Sprintf("多规则解析失败:Sheet[%s|%s] %s %s", sheet, r.Name, r.Regular, err))
|
||||
cols[11] = "多规则解析失败"
|
||||
saveErr(sheet, nf, cols, sheetIndexs)
|
||||
continue
|
||||
}
|
||||
r.IsAnd = true
|
||||
} else {
|
||||
regulars = []string{r.Regular}
|
||||
}
|
||||
for _, regular := range regulars {
|
||||
// 校验正则参数是否合理
|
||||
rule := &firewallconfigs.HTTPFirewallRule{
|
||||
IsOn: true,
|
||||
Operator: "match",
|
||||
Value: regular,
|
||||
IsCaseInsensitive: true,
|
||||
}
|
||||
if err := rule.Init(); err != nil {
|
||||
//fmt.Println(fmt.Sprintf("无效正则规则:Sheet[%s|%s] %s", sheet, r.Name, r.Regular))
|
||||
// 创建错误新表格
|
||||
cols[11] = "正则解析失败"
|
||||
saveErr(sheet, nf, cols, sheetIndexs)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if r.IsAnd {
|
||||
r.Regulars = regulars
|
||||
r.Position = r.setString(positions)
|
||||
} else {
|
||||
// position 格式化去重
|
||||
r.Position = r.setString(r.Position)
|
||||
}
|
||||
res = append(res, r)
|
||||
}
|
||||
}
|
||||
//nf.SaveAs("/Users/1usir/works/waf/open-waf/waf/EdgeAdmin/internal/utils/exce/WAF ALL Error.xlsx")
|
||||
if len(sheetIndexs) > 0 {
|
||||
_ = nf.DeleteSheet("Sheet1")
|
||||
buff, err := nf.WriteToBuffer()
|
||||
return buff, res, err
|
||||
} else {
|
||||
return nil, res, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Rule) formatPosition(p string) string {
|
||||
switch p {
|
||||
case "REQUEST_FILENAME", "REQUEST_FILENAM":
|
||||
this.Inbound = true
|
||||
return "${requestUpload.name}"
|
||||
case "ARGS_NAMES", "ARGS":
|
||||
this.Inbound = true
|
||||
return "${args}"
|
||||
case "REQUEST_BODY", "body":
|
||||
this.Inbound = true
|
||||
return "${requestBody}"
|
||||
case "REQUEST_HEADERS", "head", "header", "headers":
|
||||
this.Inbound = true
|
||||
return "${headers}"
|
||||
case "REQUEST_HEADERS_NAMES":
|
||||
this.Inbound = true
|
||||
return "${headerNames}"
|
||||
case "REQUEST_COOKIES_NAMES", "REQUEST_COOKIES", "cookie":
|
||||
this.Inbound = true
|
||||
return "${cookies}"
|
||||
case "url", "uri", "REQUEST_RAW_URI", "REQUEST_URI":
|
||||
this.Inbound = true
|
||||
return "${requestURI}"
|
||||
case "RESPONSE_BODY":
|
||||
this.Outbound = true
|
||||
return "${responseBody}"
|
||||
case "CONTENT_TYPE":
|
||||
return "${contentType}"
|
||||
case "referer":
|
||||
return "${referer}"
|
||||
case "host":
|
||||
return "${host}"
|
||||
default:
|
||||
if strings.HasPrefix(p, "${") && strings.HasSuffix(p, "}") {
|
||||
return p
|
||||
}
|
||||
//fmt.Println("=========>?", p)
|
||||
//panic(p)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// 元素去重
|
||||
func (this *Rule) setString(slice []string) []string {
|
||||
|
||||
// 解析位置
|
||||
p := []string{}
|
||||
for _, v := range slice {
|
||||
if strings.Contains(v, "ARGS_NAMES_LIST") {
|
||||
p = append(p, "uri")
|
||||
} else if strings.Contains(v, "REQUEST_HEADER_FIELDS") {
|
||||
p = append(p, "header")
|
||||
} else if strings.Contains(v, "COOKIES_NAMES_LIST") {
|
||||
p = append(p, "REQUEST_COOKIES_NAMES")
|
||||
} else if strings.Contains(v, ",") {
|
||||
p = append(p, strings.Split(v, ",")...)
|
||||
} else if strings.Contains(v, ",") {
|
||||
p = append(p, strings.Split(v, ",")...)
|
||||
} else {
|
||||
p = append(p, v)
|
||||
}
|
||||
}
|
||||
slice = p
|
||||
res := make([]string, 0, len(slice))
|
||||
m := map[string]int{}
|
||||
for _, v := range slice {
|
||||
v = this.formatPosition(v)
|
||||
_, ok := m[v]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
if v == "${headers}" || v == "${headerNames}" { // headers 包含headersNames 如果存在headers时 headersName 可以忽略
|
||||
_, ok1 := m["${headers}"]
|
||||
idx2, ok2 := m["${headerNames}"]
|
||||
if ok2 {
|
||||
res[idx2] = "${headers}"
|
||||
delete(m, "${headerNames}")
|
||||
m["{headers}"] = idx2
|
||||
continue
|
||||
}
|
||||
if ok1 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
m[v] = len(res)
|
||||
res = append(res, v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 支持多条件
|
||||
/*
|
||||
uri:\/wp-login\.php且body:.{256,}
|
||||
uri:\/goform\/_aslvl且body:SAPassword=W2402
|
||||
*/
|
||||
func parseRegulars(conditions string) ([]string, []string, error) {
|
||||
|
||||
getFieldFunc := func(s string) (func(string) (string, func(string) string), string) {
|
||||
s = strings.ToLower(s)
|
||||
switch s {
|
||||
case "uri", "body", "host", "header", "headers", "head", "cookie", "referer":
|
||||
return nil, s
|
||||
case "user-agent", "ua":
|
||||
return nil, "${userAgent}"
|
||||
case "authorization":
|
||||
return func(s string) (string, func(string) string) {
|
||||
return "${headers}", func(s string) string {
|
||||
return "authorization:" + s
|
||||
}
|
||||
}, ""
|
||||
default:
|
||||
return nil, ""
|
||||
}
|
||||
}
|
||||
cdts := strings.Split(conditions, "且")
|
||||
var regulars []string
|
||||
var positions []string
|
||||
for _, cdt := range cdts {
|
||||
i := strings.Index(cdt, ":")
|
||||
if i == -1 { // 错误
|
||||
return nil, nil, errors.New("invalid " + cdt)
|
||||
}
|
||||
// 提取position
|
||||
nextFc, field := getFieldFunc(cdt[:i])
|
||||
var position, regular string
|
||||
if nextFc == nil && field == "" { // 无法识别
|
||||
return nil, nil, errors.New("invalid " + cdt)
|
||||
}
|
||||
if nextFc != nil {
|
||||
field, getRegularFc := nextFc(cdt[i+1:])
|
||||
if field == "" || getRegularFc == nil { // 无效正则
|
||||
return nil, nil, errors.New("invalid " + cdt)
|
||||
}
|
||||
position = field
|
||||
regular = getRegularFc(cdt[i+1:])
|
||||
} else {
|
||||
position = field
|
||||
regular = cdt[i+1:]
|
||||
}
|
||||
regulars = append(regulars, regular)
|
||||
positions = append(positions, position)
|
||||
}
|
||||
return regulars, positions, nil
|
||||
}
|
||||
Reference in New Issue
Block a user