From a3108d4a5f6a2d214063a5fd68291c3ee15f44d0 Mon Sep 17 00:00:00 2001 From: harshit-splunk Date: Fri, 27 Jan 2023 15:52:57 +0530 Subject: [PATCH 1/3] Drop events when nested indexed fields are present --- .../handle_nested_map_splunkhec_receiver.yaml | 16 +++++ receiver/splunkhecreceiver/receiver.go | 24 ++++++- receiver/splunkhecreceiver/receiver_test.go | 63 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100755 .chloggen/handle_nested_map_splunkhec_receiver.yaml diff --git a/.chloggen/handle_nested_map_splunkhec_receiver.yaml b/.chloggen/handle_nested_map_splunkhec_receiver.yaml new file mode 100755 index 000000000000..085d092bdb2a --- /dev/null +++ b/.chloggen/handle_nested_map_splunkhec_receiver.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: receiver/splunkhec + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Return 400 status code when nested indextime fields are present + +# One or more tracking issues related to the change +issues: [17308] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/receiver/splunkhecreceiver/receiver.go b/receiver/splunkhecreceiver/receiver.go index b9303bd67616..87569b9ac8f8 100644 --- a/receiver/splunkhecreceiver/receiver.go +++ b/receiver/splunkhecreceiver/receiver.go @@ -51,7 +51,7 @@ const ( responseErrInternalServerError = "Internal Server Error" responseErrUnsupportedMetricEvent = "Unsupported metric event" responseErrUnsupportedLogEvent = "Unsupported log event" - + responseErrHandlingIndexedFields = `{"text":"Error in handling indexed fields","code":15,"invalid-event-number":%d}` // Centralizing some HTTP and related string constants. gzipEncoding = "gzip" httpContentEncodingHeader = "Content-Encoding" @@ -341,6 +341,13 @@ func (r *splunkReceiver) handleReq(resp http.ResponseWriter, req *http.Request) r.failRequest(ctx, resp, http.StatusBadRequest, errUnmarshalBodyRespBody, len(events), err) return } + + for _, v := range msg.Fields { + if !isFlatJsonField(v) { + r.failRequest(ctx, resp, http.StatusBadRequest, []byte(fmt.Sprintf(responseErrHandlingIndexedFields, len(events))), len(events), nil) + return + } + } if msg.IsMetric() { if r.metricsConsumer == nil { r.failRequest(ctx, resp, http.StatusBadRequest, errUnsupportedMetricEvent, len(events), err) @@ -458,3 +465,18 @@ func initJSONResponse(s string) []byte { } return respBody } + +func isFlatJsonField(value interface{}) bool { + switch value.(type) { + case map[string]interface{}: + return false + case []interface{}: + for _, v := range value.([]interface{}) { + switch v.(type) { + case map[string]interface{}, []interface{}: + return false + } + } + } + return true +} diff --git a/receiver/splunkhecreceiver/receiver_test.go b/receiver/splunkhecreceiver/receiver_test.go index 24ae10eff7f1..e2d0e41686eb 100644 --- a/receiver/splunkhecreceiver/receiver_test.go +++ b/receiver/splunkhecreceiver/receiver_test.go @@ -28,6 +28,7 @@ import ( "testing" "time" + jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" @@ -1004,6 +1005,68 @@ func Test_splunkhecreceiver_handleHealthPath(t *testing.T) { assert.Equal(t, 200, resp.StatusCode) } +func Test_splunkhecreceiver_handle_nested_fields(t *testing.T) { + tests := []struct { + name string + field interface{} + success bool + }{ + { + name: "map", + field: map[string]interface{}{}, + success: false, + }, + { + name: "flat_array", + field: []interface{}{1, 2, 3}, + success: true, + }, + { + name: "nested_array", + field: []interface{}{1, []interface{}{1, 2}}, + success: false, + }, + { + name: "int", + field: int(0), + success: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := createDefaultConfig().(*Config) + sink := new(consumertest.LogsSink) + rcv, err := newLogsReceiver(receivertest.NewNopCreateSettings(), *config, sink) + assert.NoError(t, err) + + r := rcv.(*splunkReceiver) + assert.NoError(t, r.Start(context.Background(), componenttest.NewNopHost())) + defer func() { + assert.NoError(t, r.Shutdown(context.Background())) + }() + currentTime := float64(time.Now().UnixNano()) / 1e6 + event := buildSplunkHecMsg(currentTime, 3) + event.Fields["nested_map"] = tt.field + msgBytes, err := jsoniter.Marshal(event) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/services/collector", bytes.NewReader(msgBytes)) + + w := httptest.NewRecorder() + r.handleReq(w, req) + + if tt.success { + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, 1, sink.LogRecordCount()) + } else { + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Equal(t, fmt.Sprintf(responseErrHandlingIndexedFields, 0), w.Body.String()) + } + + }) + } +} + func BenchmarkHandleReq(b *testing.B) { config := createDefaultConfig().(*Config) config.Endpoint = "localhost:0" From 9cf4dbd8a484e7b3a85f484b44c1bc648bd7ef75 Mon Sep 17 00:00:00 2001 From: harshit-splunk Date: Fri, 27 Jan 2023 17:20:03 +0530 Subject: [PATCH 2/3] fix lint --- receiver/splunkhecreceiver/receiver.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/receiver/splunkhecreceiver/receiver.go b/receiver/splunkhecreceiver/receiver.go index 87569b9ac8f8..95a1b36a6a9a 100644 --- a/receiver/splunkhecreceiver/receiver.go +++ b/receiver/splunkhecreceiver/receiver.go @@ -343,7 +343,7 @@ func (r *splunkReceiver) handleReq(resp http.ResponseWriter, req *http.Request) } for _, v := range msg.Fields { - if !isFlatJsonField(v) { + if !isFlatJSONField(v) { r.failRequest(ctx, resp, http.StatusBadRequest, []byte(fmt.Sprintf(responseErrHandlingIndexedFields, len(events))), len(events), nil) return } @@ -466,12 +466,12 @@ func initJSONResponse(s string) []byte { return respBody } -func isFlatJsonField(value interface{}) bool { - switch value.(type) { +func isFlatJSONField(field interface{}) bool { + switch value := field.(type) { case map[string]interface{}: return false case []interface{}: - for _, v := range value.([]interface{}) { + for _, v := range value { switch v.(type) { case map[string]interface{}, []interface{}: return false From c34d2e4db753bb9c8f653446a0ec500104e09760 Mon Sep 17 00:00:00 2001 From: harshit-splunk Date: Mon, 30 Jan 2023 11:27:57 +0530 Subject: [PATCH 3/3] add testcase for array of map --- receiver/splunkhecreceiver/receiver_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/receiver/splunkhecreceiver/receiver_test.go b/receiver/splunkhecreceiver/receiver_test.go index e2d0e41686eb..5043dc9155c5 100644 --- a/receiver/splunkhecreceiver/receiver_test.go +++ b/receiver/splunkhecreceiver/receiver_test.go @@ -1026,6 +1026,15 @@ func Test_splunkhecreceiver_handle_nested_fields(t *testing.T) { field: []interface{}{1, []interface{}{1, 2}}, success: false, }, + { + name: "array_of_map", + field: []interface{}{ + map[string]interface{}{ + "key": "value", + }, + }, + success: false, + }, { name: "int", field: int(0),