From 3eba5e3eca681a7e3eaeb20a2ff7bd6830bf1a7d Mon Sep 17 00:00:00 2001 From: ian woolf Date: Wed, 19 Aug 2020 21:29:02 +0800 Subject: [PATCH] Clone value when copy fields in processors to avoid crash (#20500) Closes #19206 (cherry picked from commit 0940e25d0e45a2b57139219a317b7e248e0a5fd9) --- CHANGELOG.next.asciidoc | 1 + libbeat/processors/actions/copy_fields.go | 23 ++++++++++++++++++- .../processors/actions/copy_fields_test.go | 23 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index eb88a802d4d..376d2b6718b 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -169,6 +169,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d - Drop aws.vpcflow.pkt_srcaddr and aws.vpcflow.pkt_dstaddr when equal to "-". {pull}22721[22721] {issue}22716[22716] - Fix bug in aws-s3 input where the end of gzipped log files might have been discarded. {pull}26260[26260] - o365: Avoid mapping exception for `Parameters` and `ExtendedProperties` fields of string type. {pull}26164[26164] +- 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 5dacf79dddd..8652ea87e78 100644 --- a/libbeat/processors/actions/copy_fields_test.go +++ b/libbeat/processors/actions/copy_fields_test.go @@ -121,6 +121,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 {