123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- package exprhelpers
- import (
- "bufio"
- "fmt"
- "net"
- "net/url"
- "os"
- "path"
- "regexp"
- "strconv"
- "strings"
- "time"
- "github.com/c-robinson/iplib"
- "github.com/crowdsecurity/crowdsec/pkg/cache"
- "github.com/crowdsecurity/crowdsec/pkg/database"
- "github.com/davecgh/go-spew/spew"
- log "github.com/sirupsen/logrus"
- )
- var dataFile map[string][]string
- var dataFileRegex map[string][]*regexp.Regexp
- var dbClient *database.Client
- func Atof(x string) float64 {
- log.Debugf("debug atof %s", x)
- ret, err := strconv.ParseFloat(x, 64)
- if err != nil {
- log.Warningf("Atof : can't convert float '%s' : %v", x, err)
- }
- return ret
- }
- func Upper(s string) string {
- return strings.ToUpper(s)
- }
- func Lower(s string) string {
- return strings.ToLower(s)
- }
- func GetExprEnv(ctx map[string]interface{}) map[string]interface{} {
- var ExprLib = map[string]interface{}{
- "Atof": Atof,
- "JsonExtract": JsonExtract,
- "JsonExtractUnescape": JsonExtractUnescape,
- "JsonExtractLib": JsonExtractLib,
- "JsonExtractSlice": JsonExtractSlice,
- "JsonExtractObject": JsonExtractObject,
- "ToJsonString": ToJson,
- "File": File,
- "RegexpInFile": RegexpInFile,
- "Upper": Upper,
- "Lower": Lower,
- "IpInRange": IpInRange,
- "TimeNow": TimeNow,
- "ParseUri": ParseUri,
- "PathUnescape": PathUnescape,
- "QueryUnescape": QueryUnescape,
- "PathEscape": PathEscape,
- "QueryEscape": QueryEscape,
- "XMLGetAttributeValue": XMLGetAttributeValue,
- "XMLGetNodeValue": XMLGetNodeValue,
- "IpToRange": IpToRange,
- "IsIPV6": IsIPV6,
- "LookupHost": LookupHost,
- "GetDecisionsCount": GetDecisionsCount,
- "GetDecisionsSinceCount": GetDecisionsSinceCount,
- "Sprintf": fmt.Sprintf,
- "ParseUnix": ParseUnix,
- "GetFromStash": cache.GetKey,
- "SetInStash": cache.SetKey,
- }
- for k, v := range ctx {
- ExprLib[k] = v
- }
- return ExprLib
- }
- func Init(databaseClient *database.Client) error {
- dataFile = make(map[string][]string)
- dataFileRegex = make(map[string][]*regexp.Regexp)
- dbClient = databaseClient
- return nil
- }
- func FileInit(fileFolder string, filename string, fileType string) error {
- log.Debugf("init (folder:%s) (file:%s) (type:%s)", fileFolder, filename, fileType)
- filepath := path.Join(fileFolder, filename)
- file, err := os.Open(filepath)
- if err != nil {
- return err
- }
- defer file.Close()
- if fileType == "" {
- log.Debugf("ignored file %s%s because no type specified", fileFolder, filename)
- return nil
- }
- if _, ok := dataFile[filename]; !ok {
- dataFile[filename] = []string{}
- }
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- if strings.HasPrefix(scanner.Text(), "#") { // allow comments
- continue
- }
- if len(scanner.Text()) == 0 { //skip empty lines
- continue
- }
- switch fileType {
- case "regex", "regexp":
- dataFileRegex[filename] = append(dataFileRegex[filename], regexp.MustCompile(scanner.Text()))
- case "string":
- dataFile[filename] = append(dataFile[filename], scanner.Text())
- default:
- return fmt.Errorf("unknown data type '%s' for : '%s'", fileType, filename)
- }
- }
- if err := scanner.Err(); err != nil {
- return err
- }
- return nil
- }
- func QueryEscape(s string) string {
- return url.QueryEscape(s)
- }
- func PathEscape(s string) string {
- return url.PathEscape(s)
- }
- func PathUnescape(s string) string {
- ret, err := url.PathUnescape(s)
- if err != nil {
- log.Debugf("unable to PathUnescape '%s': %+v", s, err)
- return s
- }
- return ret
- }
- func QueryUnescape(s string) string {
- ret, err := url.QueryUnescape(s)
- if err != nil {
- log.Debugf("unable to QueryUnescape '%s': %+v", s, err)
- return s
- }
- return ret
- }
- func File(filename string) []string {
- if _, ok := dataFile[filename]; ok {
- return dataFile[filename]
- }
- log.Errorf("file '%s' (type:string) not found in expr library", filename)
- log.Errorf("expr library : %s", spew.Sdump(dataFile))
- return []string{}
- }
- func RegexpInFile(data string, filename string) bool {
- if _, ok := dataFileRegex[filename]; ok {
- for _, re := range dataFileRegex[filename] {
- if re.Match([]byte(data)) {
- return true
- }
- }
- } else {
- log.Errorf("file '%s' (type:regexp) not found in expr library", filename)
- log.Errorf("expr library : %s", spew.Sdump(dataFileRegex))
- }
- return false
- }
- func IpInRange(ip string, ipRange string) bool {
- var err error
- var ipParsed net.IP
- var ipRangeParsed *net.IPNet
- ipParsed = net.ParseIP(ip)
- if ipParsed == nil {
- log.Debugf("'%s' is not a valid IP", ip)
- return false
- }
- if _, ipRangeParsed, err = net.ParseCIDR(ipRange); err != nil {
- log.Debugf("'%s' is not a valid IP Range", ipRange)
- return false
- }
- if ipRangeParsed.Contains(ipParsed) {
- return true
- }
- return false
- }
- func IsIPV6(ip string) bool {
- ipParsed := net.ParseIP(ip)
- if ipParsed == nil {
- log.Debugf("'%s' is not a valid IP", ip)
- return false
- }
- // If it's a valid IP and can't be converted to IPv4 then it is an IPv6
- return ipParsed.To4() == nil
- }
- func IpToRange(ip string, cidr string) string {
- cidr = strings.TrimPrefix(cidr, "/")
- mask, err := strconv.Atoi(cidr)
- if err != nil {
- log.Errorf("bad cidr '%s': %s", cidr, err)
- return ""
- }
- ipAddr := net.ParseIP(ip)
- if ipAddr == nil {
- log.Errorf("can't parse IP address '%s'", ip)
- return ""
- }
- ipRange := iplib.NewNet(ipAddr, mask)
- if ipRange.IP() == nil {
- log.Errorf("can't get cidr '%s' of '%s'", cidr, ip)
- return ""
- }
- return ipRange.String()
- }
- func TimeNow() string {
- return time.Now().UTC().Format(time.RFC3339)
- }
- func ParseUri(uri string) map[string][]string {
- ret := make(map[string][]string)
- u, err := url.Parse(uri)
- if err != nil {
- log.Errorf("Could not parse URI: %s", err)
- return ret
- }
- parsed, err := url.ParseQuery(u.RawQuery)
- if err != nil {
- log.Errorf("Could not parse query uri : %s", err)
- return ret
- }
- for k, v := range parsed {
- ret[k] = v
- }
- return ret
- }
- func KeyExists(key string, dict map[string]interface{}) bool {
- _, ok := dict[key]
- return ok
- }
- func GetDecisionsCount(value string) int {
- if dbClient == nil {
- log.Error("No database config to call GetDecisionsCount()")
- return 0
- }
- count, err := dbClient.CountDecisionsByValue(value)
- if err != nil {
- log.Errorf("Failed to get decisions count from value '%s'", value)
- return 0
- }
- return count
- }
- func GetDecisionsSinceCount(value string, since string) int {
- if dbClient == nil {
- log.Error("No database config to call GetDecisionsCount()")
- return 0
- }
- sinceDuration, err := time.ParseDuration(since)
- if err != nil {
- log.Errorf("Failed to parse since parameter '%s' : %s", since, err)
- return 0
- }
- sinceTime := time.Now().UTC().Add(-sinceDuration)
- count, err := dbClient.CountDecisionsSinceByValue(value, sinceTime)
- if err != nil {
- log.Errorf("Failed to get decisions count from value '%s'", value)
- return 0
- }
- return count
- }
- func LookupHost(value string) []string {
- addresses, err := net.LookupHost(value)
- if err != nil {
- log.Errorf("Failed to lookup host '%s' : %s", value, err)
- return []string{}
- }
- return addresses
- }
- func ParseUnixTime(value string) (time.Time, error) {
- //Splitting string here as some unix timestamp may have milliseconds and break ParseInt
- i, err := strconv.ParseInt(strings.Split(value, ".")[0], 10, 64)
- if err != nil || i <= 0 {
- return time.Time{}, fmt.Errorf("unable to parse %s as unix timestamp", value)
- }
- return time.Unix(i, 0), nil
- }
- func ParseUnix(value string) string {
- t, err := ParseUnixTime(value)
- if err != nil {
- log.Error(err)
- return ""
- }
- return t.Format(time.RFC3339)
- }
|