Repository: ozontech/file.d (go)

Issue:
Bug: do_if field op equal matches null value for objects and arrays
**Describe the bug**
Currently do_if field op `equal` allows usage of `null` as a value to check whether the value by the given field is `null` or it does not exist. But it also matches objects and arrays since they are not converted to bytes.

**To Reproduce**
Steps to reproduce the behavior:
1. set action
```yaml
- type: discard
  do_if:
    op: equal
    field: test_field
    values: [null]
```
2. send log `{"test_field":{"key":"val"}}` and `{"test_field":[{"key":"val"}]}`
3. both logs will be discarded.

**Expected behavior**
do_if field op equal should not match object and array with `null`.


Pull request description:
Set early return for arrays and objects in field op node check
# Description

Add early return in do_if fieldOpNode.Check if node is array or object.

Fixes #714 

Patch:
diff --git a/pipeline/doif/README.md b/pipeline/doif/README.md
index 28ac558..388c55b 100755
--- a/pipeline/doif/README.md
+++ b/pipeline/doif/README.md
@@ -28,13 +28,13 @@ the chain of Match func calls are performed across the whole tree.
 
 
 ### Field op node
-DoIf field op node is considered to always be a leaf in the DoIf tree.
-It contains operation to be checked on the field value, the field name to extract data and
-the values to check against.
+DoIf field op node is considered to always be a leaf in the DoIf tree. It checks byte representation of the value by the given field path.
+Array and object values are considered as not matched since encoding them to bytes leads towards large CPU and memory consumption.
 
 Params:
   - `op` - value from field operations list. Required.
-  - `field` - name of the field to apply operation. Required.
+  - `field` - path to field in JSON tree. If empty, root value is checked. Path to nested fields is delimited by dots `"."`, e.g. `"field.subfield"` for `{"field": {"subfield": "val"}}`.
+  If the field name contains dots in it they should be shielded with `"\"`, e.g. `"exception\.type"` for `{"exception.type": "example"}`. Default empty.
   - `values` - list of values to check field. Required non-empty.
   - `case_sensitive` - flag indicating whether checks are performed in case sensitive way. Default `true`.
     Note: case insensitive checks can cause CPU and memory overhead since every field value will be converted to lower letters.
diff --git a/pipeline/doif/field_op.go b/pipeline/doif/field_op.go
index 4b79c3c..589f674 100644
--- a/pipeline/doif/field_op.go
+++ b/pipeline/doif/field_op.go
@@ -162,13 +162,13 @@ const (
 )
 
 /*{ do-if-field-op-node
-DoIf field op node is considered to always be a leaf in the DoIf tree.
-It contains operation to be checked on the field value, the field name to extract data and
-the values to check against.
+DoIf field op node is considered to always be a leaf in the DoIf tree. It checks byte representation of the value by the given field path.
+Array and object values are considered as not matched since encoding them to bytes leads towards large CPU and memory consumption.
 
 Params:
   - `op` - value from field operations list. Required.
-  - `field` - name of the field to apply operation. Required.
+  - `field` - path to field in JSON tree. If empty, root value is checked. Path to nested fields is delimited by dots `"."`, e.g. `"field.subfield"` for `{"field": {"subfield": "val"}}`.
+  If the field name contains dots in it they should be shielded with `"\"`, e.g. `"exception\.type"` for `{"exception.type": "example"}`. Default empty.
   - `values` - list of values to check field. Required non-empty.
   - `case_sensitive` - flag indicating whether checks are performed in case sensitive way. Default `true`.
     Note: case insensitive checks can cause CPU and memory overhead since every field value will be converted to lower letters.
@@ -287,6 +287,9 @@ func (n *fieldOpNode) Type() NodeType {
 func (n *fieldOpNode) Check(eventRoot *insaneJSON.Root) bool {
 	var data []byte
 	node := eventRoot.Dig(n.fieldPath...)
+	if node.IsArray() || node.IsObject() {
+		return false
+	}
 	if !node.IsNull() {
 		data = node.AsBytes()
 	}


Test patch:
diff --git a/pipeline/doif/do_if_test.go b/pipeline/doif/do_if_test.go
index 1204962..31f4339 100644
--- a/pipeline/doif/do_if_test.go
+++ b/pipeline/doif/do_if_test.go
@@ -562,6 +562,8 @@ func TestCheck(t *testing.T) {
 				{eventStr: `{"service":"test-1"}`, want: false},
 				{eventStr: `{"pod":"test-123456789"}`, want: false},
 				{eventStr: ``, want: false},
+				{eventStr: `{"pod":{"key":"test-1"}}`, want: false},
+				{eventStr: `{"pod":[{"key":"test-1"}]}`, want: false},
 			},
 		},
 		{
@@ -777,6 +779,8 @@ func TestCheck(t *testing.T) {
 				{`{"pod":"my-test-2","test-field":null}`, true},
 				{`{"pod":"my-test-3","test-field":""}`, true},
 				{`{"pod":"my-TEST-2","test-field":"non-empty"}`, false},
+				{`{"pod":"my-TEST-2","test-field":{"key":"non-empty"}}`, false},
+				{`{"pod":"my-TEST-2","test-field":[{"key":"non-empty"}]}`, false},
 			},
 		},
 		{