diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 51225ea9637..c62d2bd48e6 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -244,6 +244,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Fix `cisco` asa and ftd parsing of messages 106102 and 106103. {pull}20469[20469] - Improve validation checks for Azure configuration {issue}20369[20369] {pull}20389[20389] - Fix event.kind for system/syslog pipeline {issue}20365[20365] {pull}20390[20390] +- Clone value when copy fields in processors to avoid crash. {issue}19206[19206] {pull}20500[20500] *Heartbeat* diff --git a/libbeat/processors/actions/copy_fields.go b/libbeat/processors/actions/copy_fields.go index 44c13f41c8a..43a797e0fd5 100644 --- a/libbeat/processors/actions/copy_fields.go +++ b/libbeat/processors/actions/copy_fields.go @@ -104,7 +104,7 @@ func (f *copyFields) copyField(from string, to string, fields common.MapStr) err return fmt.Errorf("could not fetch value for key: %s, Error: %s", from, err) } - _, err = fields.Put(to, value) + _, err = fields.Put(to, cloneValue(value)) if err != nil { return fmt.Errorf("could not copy value to %s: %v, %+v", to, value, err) } @@ -114,3 +114,24 @@ func (f *copyFields) copyField(from string, to string, fields common.MapStr) err func (f *copyFields) String() string { return "copy_fields=" + fmt.Sprintf("%+v", f.config.Fields) } + +// cloneValue returns a shallow copy of a map. All other types are passed +// through in the return. This should be used when making straight copies of +// maps without doing any type conversions. +func cloneValue(value interface{}) interface{} { + switch v := value.(type) { + case common.MapStr: + return v.Clone() + case map[string]interface{}: + return common.MapStr(v).Clone() + case []interface{}: + len := len(v) + newArr := make([]interface{}, len) + for idx, val := range v { + newArr[idx] = cloneValue(val) + } + return newArr + default: + return value + } +} diff --git a/libbeat/processors/actions/copy_fields_test.go b/libbeat/processors/actions/copy_fields_test.go index 2ccbb1187b8..96b382a596a 100644 --- a/libbeat/processors/actions/copy_fields_test.go +++ b/libbeat/processors/actions/copy_fields_test.go @@ -122,6 +122,29 @@ func TestCopyFields(t *testing.T) { "message": 42, }, }, + "copy map from nested key message.original to top level field message_copied": { + FromTo: fromTo{ + From: "message.original", + To: "message_copied", + }, + Input: common.MapStr{ + "message": common.MapStr{ + "original": common.MapStr{ + "original": "original", + }, + }, + }, + Expected: common.MapStr{ + "message": common.MapStr{ + "original": common.MapStr{ + "original": "original", + }, + }, + "message_copied": common.MapStr{ + "original": "original", + }, + }, + }, } for name, test := range tests {