-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from Matrix86/json_filter
new: added basic json filter
- Loading branch information
Showing
6 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package filters | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/Matrix86/driplane/data" | ||
|
||
"github.com/antchfx/jsonquery" | ||
"github.com/evilsocket/islazy/log" | ||
"github.com/evilsocket/islazy/str" | ||
) | ||
|
||
// JSON is a filter to parse the JSON format | ||
type JSON struct { | ||
Base | ||
|
||
selector string | ||
target string | ||
|
||
params map[string]string | ||
} | ||
|
||
// NewJSONFilter is the registered method to instantiate a JSONFilter | ||
func NewJSONFilter(p map[string]string) (Filter, error) { | ||
f := &JSON{ | ||
params: p, | ||
target: "main", | ||
selector: "", | ||
} | ||
f.cbFilter = f.DoFilter | ||
|
||
if v, ok := f.params["selector"]; ok { | ||
f.selector = v | ||
} | ||
|
||
if f.selector == "" { | ||
return nil, errors.New("no selector specified for JSON filter") | ||
} | ||
if v, ok := f.params["target"]; ok { | ||
f.target = v | ||
} | ||
|
||
return f, nil | ||
} | ||
|
||
// DoFilter is the mandatory method used to "filter" the input data.Message | ||
func (f *JSON) DoFilter(msg *data.Message) (bool, error) { | ||
//var err error | ||
var text string | ||
|
||
if v, ok := msg.GetTarget(f.target).(string); ok { | ||
text = str.Trim(v) | ||
} else if v, ok := msg.GetTarget(f.target).([]byte); ok { | ||
text = string(v) | ||
} else { | ||
// ERROR this filter can't be used with different types | ||
return false, fmt.Errorf("received data is not a string") | ||
} | ||
|
||
if len(text) > 0 { | ||
var jsonData string | ||
|
||
if text[0] == '{' { | ||
// json text | ||
jsonData = str.Trim(text) | ||
} else { | ||
log.Error("'%v' is not a json document", text) | ||
return false, nil | ||
} | ||
|
||
if doc, err := jsonquery.Parse(strings.NewReader(jsonData)); err == nil { | ||
atLeastOne := false | ||
for _, node := range jsonquery.Find(doc, f.selector) { | ||
atLeastOne = true | ||
clone := msg.Clone() | ||
clone.SetMessage(node.Value()) | ||
f.Propagate(clone) | ||
} | ||
|
||
return atLeastOne, nil | ||
|
||
} else { | ||
log.Debug("'%v' could not be parsed as JSON: %v", text, err) | ||
return false, nil | ||
} | ||
|
||
} | ||
|
||
return false, nil | ||
} | ||
|
||
// OnEvent is called when an event occurs | ||
func (f *JSON) OnEvent(event *data.Event) {} | ||
|
||
// Set the name of the filter | ||
func init() { | ||
register("json", NewJSONFilter) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
package filters | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
"github.com/Matrix86/driplane/data" | ||
|
||
"github.com/asaskevich/EventBus" | ||
) | ||
|
||
const jsonTest = `{"top": { "inside": "value"}}` | ||
|
||
func TestNewJSONFilter(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{"none": "none", "selector": "#selector"}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
if e.target != "main" { | ||
t.Errorf("target should be 'main' if not specified") | ||
} | ||
if e.selector != "#selector" { | ||
t.Errorf("selector should be '#selector'") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestNewJSONFilterParams(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "#selector", | ||
"target": "othertarget", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
if e.target != "othertarget" { | ||
t.Errorf("target should be 'othertarget'") | ||
} | ||
if e.selector != "#selector" { | ||
t.Errorf("selector should be '#selector'") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestNewJSONWithoutFilterParams(t *testing.T) { | ||
_, err := NewJSONFilter(map[string]string{}) | ||
if err == nil { | ||
t.Errorf("The test should return an error") | ||
} | ||
} | ||
|
||
func TestJSON_DoFilter(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "top/inside", | ||
"target": "main", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
fb := NewFakeBus() | ||
filter.setBus(EventBus.Bus(fb)) | ||
|
||
msg := jsonTest | ||
m := data.NewMessage(msg) | ||
_, err := e.DoFilter(m) | ||
if err != nil { | ||
t.Errorf("DoFilter returned an error '%s'", err) | ||
} | ||
if m.GetMessage() != msg { | ||
t.Errorf("the message has been altered by the filter") | ||
} | ||
if len(fb.Collected) == 0 || fb.Collected[0].GetMessage() != "value" { | ||
t.Errorf("tags have not been extracted correctly") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestJSON_DoFilterOnByte(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "top/inside", | ||
"target": "main", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
fb := NewFakeBus() | ||
filter.setBus(EventBus.Bus(fb)) | ||
|
||
msg := []byte(jsonTest) | ||
m := data.NewMessage(msg) | ||
_, err := e.DoFilter(m) | ||
if err != nil { | ||
t.Errorf("DoFilter returned an error '%s'", err) | ||
} | ||
if !bytes.Equal(m.GetMessage().([]byte), msg) { | ||
t.Errorf("the message has been altered by the filter") | ||
} | ||
if len(fb.Collected) == 0 || fb.Collected[0].GetMessage() != "value" { | ||
t.Errorf("tags have not been extracted correctly") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestJSON_DoFilterOnWrongType(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "top/inside", | ||
"target": "main", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
fb := NewFakeBus() | ||
filter.setBus(EventBus.Bus(fb)) | ||
|
||
msg := 2 | ||
m := data.NewMessage(msg) | ||
x, err := e.DoFilter(m) | ||
if err == nil { | ||
t.Errorf("DoFilter should return an error") | ||
} | ||
if x { | ||
t.Errorf("DoFilter should return false") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestJSON_DoFilterIfNotJSON(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "top/inside", | ||
"target": "main", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
fb := NewFakeBus() | ||
filter.setBus(EventBus.Bus(fb)) | ||
|
||
msg := "not a JSON" | ||
m := data.NewMessage(msg) | ||
x, err := e.DoFilter(m) | ||
if err != nil { | ||
t.Errorf("DoFilter should return nil") | ||
} | ||
if x { | ||
t.Errorf("DoFilter should return false") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestJSON_DoFilterOnWrongSelector(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "top/ops", | ||
"target": "main", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
fb := NewFakeBus() | ||
filter.setBus(EventBus.Bus(fb)) | ||
|
||
msg := jsonTest | ||
m := data.NewMessage(msg) | ||
x, err := e.DoFilter(m) | ||
if err != nil { | ||
t.Errorf("DoFilter should return nil") | ||
} | ||
if x { | ||
t.Errorf("DoFilter should return false") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestJSON_DoFilterOnEmptyJSON(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "top/ops", | ||
"target": "main", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
fb := NewFakeBus() | ||
filter.setBus(EventBus.Bus(fb)) | ||
|
||
msg := "" | ||
m := data.NewMessage(msg) | ||
x, err := e.DoFilter(m) | ||
if err != nil { | ||
t.Errorf("DoFilter should return nil") | ||
} | ||
if x { | ||
t.Errorf("DoFilter should return false") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} | ||
|
||
func TestJSON_DoFilterOnBadJSON(t *testing.T) { | ||
filter, err := NewJSONFilter(map[string]string{ | ||
"selector": "top/ops", | ||
"target": "main", | ||
}) | ||
if err != nil { | ||
t.Errorf("constructor returned '%s'", err) | ||
} | ||
if e, ok := filter.(*JSON); ok { | ||
fb := NewFakeBus() | ||
filter.setBus(EventBus.Bus(fb)) | ||
|
||
msg := jsonTest[:len(jsonTest)-1] | ||
m := data.NewMessage(msg) | ||
x, err := e.DoFilter(m) | ||
if err != nil { | ||
t.Errorf("DoFilter should return nil") | ||
} | ||
if x { | ||
t.Errorf("DoFilter should return false") | ||
} | ||
} else { | ||
t.Errorf("cannot cast to proper Filter...") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.