From 64369b5c2b2eabc4653eb3b4188fbab0d5844773 Mon Sep 17 00:00:00 2001 From: blotus Date: Fri, 29 Apr 2022 13:52:23 +0200 Subject: [PATCH] add expr XML helpers (#1493) --- go.mod | 3 +- go.sum | 2 + pkg/exprhelpers/exprlib.go | 34 ++++++----- pkg/exprhelpers/xml.go | 64 ++++++++++++++++++++ pkg/exprhelpers/xml_test.go | 115 ++++++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 17 deletions(-) create mode 100644 pkg/exprhelpers/xml.go create mode 100644 pkg/exprhelpers/xml_test.go 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) + } + +}