diff --git a/go.mod b/go.mod
index 59865ad95..0fd97eff0 100644
--- a/go.mod
+++ b/go.mod
@@ -16,9 +16,9 @@ require (
github.com/confluentinc/bincover v0.2.0
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/crowdsecurity/grokky v0.0.0-20220120093523-d5b3478363fa
+ github.com/crowdsecurity/machineid v1.0.1
github.com/davecgh/go-spew v1.1.1
github.com/denisbrodbeck/machineid v1.0.1
- github.com/crowdsecurity/machineid v1.0.1
github.com/dghubble/sling v1.3.0
github.com/docker/docker v20.10.2+incompatible
github.com/docker/go-connections v0.4.0
@@ -78,6 +78,7 @@ require (
github.com/ahmetalpbalkan/dlog v0.0.0-20170105205344-4fb5f8204f26 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
+ github.com/beevik/etree v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/containerd v1.6.2 // indirect
diff --git a/go.sum b/go.sum
index 96cbc3c76..68e0fb2a8 100644
--- a/go.sum
+++ b/go.sum
@@ -97,6 +97,8 @@ github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:W
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go v1.42.25 h1:BbdvHAi+t9LRiaYUyd53noq9jcaAcfzOhSVbKfr6Avs=
github.com/aws/aws-sdk-go v1.42.25/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs=
+github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
+github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
diff --git a/pkg/exprhelpers/exprlib.go b/pkg/exprhelpers/exprlib.go
index 768ec3834..c16e11f1d 100644
--- a/pkg/exprhelpers/exprlib.go
+++ b/pkg/exprhelpers/exprlib.go
@@ -40,22 +40,24 @@ func Lower(s string) string {
func GetExprEnv(ctx map[string]interface{}) map[string]interface{} {
var ExprLib = map[string]interface{}{
- "Atof": Atof,
- "JsonExtract": JsonExtract,
- "JsonExtractUnescape": JsonExtractUnescape,
- "JsonExtractLib": JsonExtractLib,
- "File": File,
- "RegexpInFile": RegexpInFile,
- "Upper": Upper,
- "Lower": Lower,
- "IpInRange": IpInRange,
- "TimeNow": TimeNow,
- "ParseUri": ParseUri,
- "PathUnescape": PathUnescape,
- "QueryUnescape": QueryUnescape,
- "PathEscape": PathEscape,
- "QueryEscape": QueryEscape,
- "IpToRange": IpToRange,
+ "Atof": Atof,
+ "JsonExtract": JsonExtract,
+ "JsonExtractUnescape": JsonExtractUnescape,
+ "JsonExtractLib": JsonExtractLib,
+ "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,
}
for k, v := range ctx {
ExprLib[k] = v
diff --git a/pkg/exprhelpers/xml.go b/pkg/exprhelpers/xml.go
new file mode 100644
index 000000000..1d0d4073f
--- /dev/null
+++ b/pkg/exprhelpers/xml.go
@@ -0,0 +1,64 @@
+package exprhelpers
+
+import (
+ "github.com/beevik/etree"
+ log "github.com/sirupsen/logrus"
+)
+
+var pathCache = make(map[string]etree.Path)
+
+func XMLGetAttributeValue(xmlString string, path string, attributeName string) string {
+
+ if _, ok := pathCache[path]; !ok {
+ compiledPath, err := etree.CompilePath(path)
+ if err != nil {
+ log.Errorf("Could not compile path %s: %s", path, err)
+ return ""
+ }
+ pathCache[path] = compiledPath
+ }
+
+ compiledPath := pathCache[path]
+ doc := etree.NewDocument()
+ err := doc.ReadFromString(xmlString)
+ if err != nil {
+ log.Tracef("Could not parse XML: %s", err)
+ return ""
+ }
+ elem := doc.FindElementPath(compiledPath)
+ if elem == nil {
+ log.Debugf("Could not find element %s", path)
+ return ""
+ }
+ attr := elem.SelectAttr(attributeName)
+ if attr == nil {
+ log.Debugf("Could not find attribute %s", attributeName)
+ return ""
+ }
+ return attr.Value
+}
+
+func XMLGetNodeValue(xmlString string, path string) string {
+ if _, ok := pathCache[path]; !ok {
+ compiledPath, err := etree.CompilePath(path)
+ if err != nil {
+ log.Errorf("Could not compile path %s: %s", path, err)
+ return ""
+ }
+ pathCache[path] = compiledPath
+ }
+
+ compiledPath := pathCache[path]
+ doc := etree.NewDocument()
+ err := doc.ReadFromString(xmlString)
+ if err != nil {
+ log.Tracef("Could not parse XML: %s", err)
+ return ""
+ }
+ elem := doc.FindElementPath(compiledPath)
+ if elem == nil {
+ log.Debugf("Could not find element %s", path)
+ return ""
+ }
+ return elem.Text()
+}
diff --git a/pkg/exprhelpers/xml_test.go b/pkg/exprhelpers/xml_test.go
new file mode 100644
index 000000000..ca1f54b37
--- /dev/null
+++ b/pkg/exprhelpers/xml_test.go
@@ -0,0 +1,115 @@
+package exprhelpers
+
+import (
+ "log"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestXMLGetAttributeValue(t *testing.T) {
+ if err := Init(); err != nil {
+ log.Fatalf(err.Error())
+ }
+
+ tests := []struct {
+ name string
+ xmlString string
+ path string
+ attribute string
+ expectResult string
+ }{
+ {
+ name: "XMLGetAttributeValue",
+ xmlString: ``,
+ path: "/root/child",
+ attribute: "attr",
+ expectResult: "value",
+ },
+ {
+ name: "Non existing attribute for XMLGetAttributeValue",
+ xmlString: ``,
+ path: "/root/child",
+ attribute: "asdasd",
+ expectResult: "",
+ },
+ {
+ name: "Non existing path for XMLGetAttributeValue",
+ xmlString: ``,
+ path: "/foo/bar",
+ attribute: "asdasd",
+ expectResult: "",
+ },
+ {
+ name: "Invalid XML for XMLGetAttributeValue",
+ xmlString: `<`,
+ path: "/foo/bar",
+ attribute: "asdasd",
+ expectResult: "",
+ },
+ {
+ name: "Invalid path for XMLGetAttributeValue",
+ xmlString: ``,
+ path: "/foo/bar[@",
+ attribute: "asdasd",
+ expectResult: "",
+ },
+ }
+
+ for _, test := range tests {
+ result := XMLGetAttributeValue(test.xmlString, test.path, test.attribute)
+ isOk := assert.Equal(t, test.expectResult, result)
+ if !isOk {
+ t.Fatalf("test '%s' failed", test.name)
+ }
+ log.Printf("test '%s' : OK", test.name)
+ }
+
+}
+func TestXMLGetNodeValue(t *testing.T) {
+ if err := Init(); err != nil {
+ log.Fatalf(err.Error())
+ }
+
+ tests := []struct {
+ name string
+ xmlString string
+ path string
+ expectResult string
+ }{
+ {
+ name: "XMLGetNodeValue",
+ xmlString: `foobar`,
+ path: "/root/child",
+ expectResult: "foobar",
+ },
+ {
+ name: "Non existing path for XMLGetNodeValue",
+ xmlString: `foobar`,
+ path: "/foo/bar",
+ expectResult: "",
+ },
+ {
+ name: "Invalid XML for XMLGetNodeValue",
+ xmlString: `<`,
+ path: "/foo/bar",
+ expectResult: "",
+ },
+ {
+ name: "Invalid path for XMLGetNodeValue",
+ xmlString: `foobar`,
+ path: "/foo/bar[@",
+ expectResult: "",
+ },
+ }
+
+ for _, test := range tests {
+ result := XMLGetNodeValue(test.xmlString, test.path)
+ isOk := assert.Equal(t, test.expectResult, result)
+ if !isOk {
+ t.Fatalf("test '%s' failed", test.name)
+ }
+ log.Printf("test '%s' : OK", test.name)
+ }
+
+}