package lib import ( "bytes" "crypto/md5" "encoding/base64" "fmt" "github.com/google/cel-go/cel" "github.com/google/cel-go/checker/decls" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/interpreter/functions" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "math/rand" "net/http" "net/url" "regexp" "strings" "time" ) func NewEnv(c *CustomLib) (*cel.Env, error) { return cel.NewEnv(cel.Lib(c)) } func Evaluate(env *cel.Env, expression string, params map[string]interface{}) (ref.Val, error) { ast, iss := env.Compile(expression) if iss.Err() != nil { //fmt.Println("compile: ", iss.Err()) return nil, iss.Err() } prg, err := env.Program(ast) if err != nil { //fmt.Println("Program creation error: %v", err) return nil, err } out, _, err := prg.Eval(params) if err != nil { //fmt.Println("Evaluation error: %v", err) return nil, err } return out, nil } func UrlTypeToString(u *UrlType) string { var buf strings.Builder if u.Scheme != "" { buf.WriteString(u.Scheme) buf.WriteByte(':') } if u.Scheme != "" || u.Host != "" { if u.Host != "" || u.Path != "" { buf.WriteString("//") } if h := u.Host; h != "" { buf.WriteString(u.Host) } } path := u.Path if path != "" && path[0] != '/' && u.Host != "" { buf.WriteByte('/') } if buf.Len() == 0 { if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 { buf.WriteString("./") } } buf.WriteString(path) if u.Query != "" { buf.WriteByte('?') buf.WriteString(u.Query) } if u.Fragment != "" { buf.WriteByte('#') buf.WriteString(u.Fragment) } return buf.String() } type CustomLib struct { envOptions []cel.EnvOption programOptions []cel.ProgramOption } func NewEnvOption() CustomLib { c := CustomLib{} c.envOptions = []cel.EnvOption{ cel.Container("lib"), cel.Types( &UrlType{}, &Request{}, &Response{}, &Reverse{}, ), cel.Declarations( decls.NewIdent("request", decls.NewObjectType("lib.Request"), nil), decls.NewIdent("response", decls.NewObjectType("lib.Response"), nil), //decls.NewIdent("reverse", decls.NewObjectType("lib.Reverse"), nil), ), cel.Declarations( // functions decls.NewFunction("bcontains", decls.NewInstanceOverload("bytes_bcontains_bytes", []*exprpb.Type{decls.Bytes, decls.Bytes}, decls.Bool)), decls.NewFunction("bmatches", decls.NewInstanceOverload("string_bmatches_bytes", []*exprpb.Type{decls.String, decls.Bytes}, decls.Bool)), decls.NewFunction("md5", decls.NewOverload("md5_string", []*exprpb.Type{decls.String}, decls.String)), decls.NewFunction("randomInt", decls.NewOverload("randomInt_int_int", []*exprpb.Type{decls.Int, decls.Int}, decls.Int)), decls.NewFunction("randomLowercase", decls.NewOverload("randomLowercase_int", []*exprpb.Type{decls.Int}, decls.String)), decls.NewFunction("base64", decls.NewOverload("base64_string", []*exprpb.Type{decls.String}, decls.String)), decls.NewFunction("base64", decls.NewOverload("base64_bytes", []*exprpb.Type{decls.Bytes}, decls.String)), decls.NewFunction("base64Decode", decls.NewOverload("base64Decode_string", []*exprpb.Type{decls.String}, decls.String)), decls.NewFunction("base64Decode", decls.NewOverload("base64Decode_bytes", []*exprpb.Type{decls.Bytes}, decls.String)), decls.NewFunction("urlencode", decls.NewOverload("urlencode_string", []*exprpb.Type{decls.String}, decls.String)), decls.NewFunction("urlencode", decls.NewOverload("urlencode_bytes", []*exprpb.Type{decls.Bytes}, decls.String)), decls.NewFunction("urldecode", decls.NewOverload("urldecode_string", []*exprpb.Type{decls.String}, decls.String)), decls.NewFunction("urldecode", decls.NewOverload("urldecode_bytes", []*exprpb.Type{decls.Bytes}, decls.String)), decls.NewFunction("substr", decls.NewOverload("substr_string_int_int", []*exprpb.Type{decls.String, decls.Int, decls.Int}, decls.String)), decls.NewFunction("wait", decls.NewInstanceOverload("reverse_wait_int", []*exprpb.Type{decls.Any, decls.Int}, decls.Bool)), decls.NewFunction("icontains", decls.NewInstanceOverload("icontains_string", []*exprpb.Type{decls.String, decls.String}, decls.Bool)), ), } c.programOptions = []cel.ProgramOption{ cel.Functions( &functions.Overload{ Operator: "bytes_bcontains_bytes", Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { v1, ok := lhs.(types.Bytes) if !ok { return types.ValOrErr(lhs, "unexpected type '%v' passed to bcontains", lhs.Type()) } v2, ok := rhs.(types.Bytes) if !ok { return types.ValOrErr(rhs, "unexpected type '%v' passed to bcontains", rhs.Type()) } return types.Bool(bytes.Contains(v1, v2)) }, }, &functions.Overload{ Operator: "string_bmatch_bytes", Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { v1, ok := lhs.(types.String) if !ok { return types.ValOrErr(lhs, "unexpected type '%v' passed to bmatch", lhs.Type()) } v2, ok := rhs.(types.Bytes) if !ok { return types.ValOrErr(rhs, "unexpected type '%v' passed to bmatch", rhs.Type()) } ok, err := regexp.Match(string(v1), v2) if err != nil { return types.NewErr("%v", err) } return types.Bool(ok) }, }, &functions.Overload{ Operator: "md5_string", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.String) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to md5_string", value.Type()) } return types.String(fmt.Sprintf("%x", md5.Sum([]byte(v)))) }, }, &functions.Overload{ Operator: "randomInt_int_int", Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { from, ok := lhs.(types.Int) if !ok { return types.ValOrErr(lhs, "unexpected type '%v' passed to randomInt", lhs.Type()) } to, ok := rhs.(types.Int) if !ok { return types.ValOrErr(rhs, "unexpected type '%v' passed to randomInt", rhs.Type()) } min, max := int(from), int(to) return types.Int(rand.Intn(max-min) + min) }, }, &functions.Overload{ Operator: "randomLowercase_int", Unary: func(value ref.Val) ref.Val { n, ok := value.(types.Int) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to randomLowercase", value.Type()) } return types.String(randomLowercase(int(n))) }, }, &functions.Overload{ Operator: "base64_string", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.String) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to base64_string", value.Type()) } return types.String(base64.StdEncoding.EncodeToString([]byte(v))) }, }, &functions.Overload{ Operator: "base64_bytes", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.Bytes) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to base64_bytes", value.Type()) } return types.String(base64.StdEncoding.EncodeToString(v)) }, }, &functions.Overload{ Operator: "base64Decode_string", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.String) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to base64Decode_string", value.Type()) } decodeBytes, err := base64.StdEncoding.DecodeString(string(v)) if err != nil { return types.NewErr("%v", err) } return types.String(decodeBytes) }, }, &functions.Overload{ Operator: "base64Decode_bytes", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.Bytes) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to base64Decode_bytes", value.Type()) } decodeBytes, err := base64.StdEncoding.DecodeString(string(v)) if err != nil { return types.NewErr("%v", err) } return types.String(decodeBytes) }, }, &functions.Overload{ Operator: "urlencode_string", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.String) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to urlencode_string", value.Type()) } return types.String(url.QueryEscape(string(v))) }, }, &functions.Overload{ Operator: "urlencode_bytes", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.Bytes) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to urlencode_bytes", value.Type()) } return types.String(url.QueryEscape(string(v))) }, }, &functions.Overload{ Operator: "urldecode_string", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.String) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to urldecode_string", value.Type()) } decodeString, err := url.QueryUnescape(string(v)) if err != nil { return types.NewErr("%v", err) } return types.String(decodeString) }, }, &functions.Overload{ Operator: "urldecode_bytes", Unary: func(value ref.Val) ref.Val { v, ok := value.(types.Bytes) if !ok { return types.ValOrErr(value, "unexpected type '%v' passed to urldecode_bytes", value.Type()) } decodeString, err := url.QueryUnescape(string(v)) if err != nil { return types.NewErr("%v", err) } return types.String(decodeString) }, }, &functions.Overload{ Operator: "substr_string_int_int", Function: func(values ...ref.Val) ref.Val { if len(values) == 3 { str, ok := values[0].(types.String) if !ok { return types.NewErr("invalid string to 'substr'") } start, ok := values[1].(types.Int) if !ok { return types.NewErr("invalid start to 'substr'") } length, ok := values[2].(types.Int) if !ok { return types.NewErr("invalid length to 'substr'") } runes := []rune(str) if start < 0 || length < 0 || int(start+length) > len(runes) { return types.NewErr("invalid start or length to 'substr'") } return types.String(runes[start : start+length]) } else { return types.NewErr("too many arguments to 'substr'") } }, }, &functions.Overload{ Operator: "reverse_wait_int", Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { reverse, ok := lhs.Value().(*Reverse) if !ok { return types.ValOrErr(lhs, "unexpected type '%v' passed to 'wait'", lhs.Type()) } timeout, ok := rhs.Value().(int64) if !ok { return types.ValOrErr(rhs, "unexpected type '%v' passed to 'wait'", rhs.Type()) } return types.Bool(reverseCheck(reverse, timeout)) }, }, &functions.Overload{ Operator: "icontains_string", Binary: func(lhs ref.Val, rhs ref.Val) ref.Val { v1, ok := lhs.(types.String) if !ok { return types.ValOrErr(lhs, "unexpected type '%v' passed to bcontains", lhs.Type()) } v2, ok := rhs.(types.String) if !ok { return types.ValOrErr(rhs, "unexpected type '%v' passed to bcontains", rhs.Type()) } // 不区分大小写包含 return types.Bool(strings.Contains(strings.ToLower(string(v1)), strings.ToLower(string(v2)))) }, }, ), } return c } // 声明环境中的变量类型和函数 func (c *CustomLib) CompileOptions() []cel.EnvOption { return c.envOptions } func (c *CustomLib) ProgramOptions() []cel.ProgramOption { return c.programOptions } func (c *CustomLib) UpdateCompileOptions(args map[string]string) { for k, v := range args { // 在执行之前是不知道变量的类型的,所以统一声明为字符型 // 所以randomInt虽然返回的是int型,在运算中却被当作字符型进行计算,需要重载string_*_string var d *exprpb.Decl if strings.HasPrefix(v, "randomInt") { d = decls.NewIdent(k, decls.Int, nil) } else if strings.HasPrefix(v, "newReverse") { d = decls.NewIdent(k, decls.NewObjectType("lib.Reverse"), nil) } else { d = decls.NewIdent(k, decls.String, nil) } c.envOptions = append(c.envOptions, cel.Declarations(d)) } } func randomLowercase(n int) string { lowercase := "abcdefghijklmnopqrstuvwxyz" randSource := rand.New(rand.NewSource(time.Now().Unix())) return RandomStr(randSource, lowercase, n) } func reverseCheck(r *Reverse, timeout int64) bool { if ceyeApi == "" || r.Domain == "" { return false } time.Sleep(time.Second * time.Duration(timeout)) sub := strings.Split(r.Domain, ".")[0] urlStr := fmt.Sprintf("http://api.ceye.io/v1/records?token=%s&type=dns&filter=%s", ceyeApi, sub) fmt.Println(urlStr) req, _ := http.NewRequest("GET", urlStr, nil) resp, err := DoRequest(req, false) if err != nil { return false } if !bytes.Contains(resp.Body, []byte(`"data": []`)) && bytes.Contains(resp.Body, []byte(`"message": "OK"`)) { // api返回结果不为空 return true } return false } func RandomStr(randSource *rand.Rand, letterBytes string, n int) string { const ( letterIdxBits = 6 // 6 bits to represent a letter index letterIdxMask = 1<= 0; { if remain == 0 { cache, remain = randSource.Int63(), letterIdxMax } if idx := int(cache & letterIdxMask); idx < len(letterBytes) { randBytes[i] = letterBytes[idx] i-- } cache >>= letterIdxBits remain-- } return string(randBytes) }