Skip to content

Commit

Permalink
[8.15](backport #40751) libbeat/processors/replace: fix zero string r…
Browse files Browse the repository at this point in the history
…eplacement validation (#40917)

* libbeat/processors/replace: fix zero string replacement validation (#40751)

The change in #40047 breaks the use of the replace processor to do
s/<pattern>// replacement. So make the validation on absence, not zero state.

(cherry picked from commit a45a3ac)

* add missing pr number

---------

Co-authored-by: Dan Kortschak <[email protected]>
  • Loading branch information
mergify[bot] and efd6 committed Sep 20, 2024
1 parent e96f442 commit 1e1c7f9
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff]
- Updated Websocket input title to align with existing inputs {pull}39006[39006]
- Fix publication of group data from the Okta entity analytics provider. {pull}40681[40681]
- Ensure netflow custom field configuration is applied. {issue}40735[40735] {pull}40730[40730]
- Fix replace processor handling of zero string replacement validation. {pull}40751[40751]

*Heartbeat*

Expand Down
11 changes: 9 additions & 2 deletions libbeat/processors/actions/replace.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ type replaceStringConfig struct {
type replaceConfig struct {
Field string `config:"field" validate:"required"`
Pattern *regexp.Regexp `config:"pattern" validate:"required"`
Replacement string `config:"replacement" validate:"required"`
Replacement *string `config:"replacement"`
}

func (c replaceConfig) Validate() error {
if c.Replacement == nil {
return errors.New("missing replacement")
}
return nil
}

func init() {
Expand Down Expand Up @@ -82,7 +89,7 @@ func (f *replaceString) Run(event *beat.Event) (*beat.Event, error) {
}

for _, field := range f.config.Fields {
err := f.replaceField(field.Field, field.Pattern, field.Replacement, event)
err := f.replaceField(field.Field, field.Pattern, *field.Replacement, event)
if err != nil {
errMsg := fmt.Errorf("Failed to replace fields in processor: %w", err)
f.log.Debugw(errMsg.Error(), logp.TypeKey, logp.EventType)
Expand Down
48 changes: 37 additions & 11 deletions libbeat/processors/actions/replace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestBadConfig(t *testing.T) {
},
{
name: "no-regex",
cfg: replaceStringConfig{Fields: []replaceConfig{{Field: "message", Replacement: "new_message"}}},
cfg: replaceStringConfig{Fields: []replaceConfig{{Field: "message", Replacement: ptr("new_message")}}},
shouldError: true,
},
{
Expand All @@ -56,15 +56,20 @@ func TestBadConfig(t *testing.T) {
{
name: "valid-then-invalid",
cfg: replaceStringConfig{Fields: []replaceConfig{
{Field: "message", Pattern: regexp.MustCompile(`message`), Replacement: "new_message"},
{Field: "message", Pattern: regexp.MustCompile(`message`), Replacement: ptr("new_message")},
{Field: "message", Pattern: regexp.MustCompile(`message`)},
},
},
shouldError: true,
},
{
name: "no-error",
cfg: replaceStringConfig{Fields: []replaceConfig{{Field: "message", Replacement: "new_message", Pattern: regexp.MustCompile(`message`)}}},
cfg: replaceStringConfig{Fields: []replaceConfig{{Field: "message", Replacement: ptr("new_message"), Pattern: regexp.MustCompile(`message`)}}},
shouldError: false,
},
{
name: "no-error zero string",
cfg: replaceStringConfig{Fields: []replaceConfig{{Field: "message", Replacement: ptr(""), Pattern: regexp.MustCompile(`message`)}}},
shouldError: false,
},
}
Expand Down Expand Up @@ -102,7 +107,7 @@ func TestReplaceRun(t *testing.T) {
{
Field: "f",
Pattern: regexp.MustCompile(`a`),
Replacement: "b",
Replacement: ptr("b"),
},
},
Input: mapstr.M{
Expand All @@ -115,13 +120,32 @@ func TestReplaceRun(t *testing.T) {
IgnoreMissing: false,
FailOnError: true,
},
{
description: "replace with zero",
Fields: []replaceConfig{
{
Field: "f",
Pattern: regexp.MustCompile(`a`),
Replacement: ptr(""),
},
},
Input: mapstr.M{
"f": "abc",
},
Output: mapstr.M{
"f": "bc",
},
error: false,
IgnoreMissing: false,
FailOnError: true,
},
{
description: "Add one more hierarchy to event",
Fields: []replaceConfig{
{
Field: "f.b",
Pattern: regexp.MustCompile(`a`),
Replacement: "b",
Replacement: ptr("b"),
},
},
Input: mapstr.M{
Expand All @@ -144,12 +168,12 @@ func TestReplaceRun(t *testing.T) {
{
Field: "f",
Pattern: regexp.MustCompile(`a.*c`),
Replacement: "cab",
Replacement: ptr("cab"),
},
{
Field: "g",
Pattern: regexp.MustCompile(`ef`),
Replacement: "oor",
Replacement: ptr("oor"),
},
},
Input: mapstr.M{
Expand All @@ -170,12 +194,12 @@ func TestReplaceRun(t *testing.T) {
{
Field: "f",
Pattern: regexp.MustCompile(`abc`),
Replacement: "xyz",
Replacement: ptr("xyz"),
},
{
Field: "g",
Pattern: regexp.MustCompile(`def`),
Replacement: "",
Replacement: nil,
},
},
Input: mapstr.M{
Expand Down Expand Up @@ -216,11 +240,13 @@ func TestReplaceRun(t *testing.T) {
assert.Error(t, err)
}

assert.True(t, reflect.DeepEqual(newEvent.Fields, test.Output))
assert.Equal(t, newEvent.Fields, test.Output)
})
}
}

func ptr[T any](v T) *T { return &v }

func TestReplaceField(t *testing.T) {
var tests = []struct {
Field string
Expand Down Expand Up @@ -322,7 +348,7 @@ func TestReplaceField(t *testing.T) {
{
Field: "@metadata.f",
Pattern: regexp.MustCompile(`a`),
Replacement: "b",
Replacement: ptr("b"),
},
},
},
Expand Down

0 comments on commit 1e1c7f9

Please sign in to comment.