Skip to content

Commit

Permalink
Allow fingerprint processor to read @timestamp (#28705) (#28767)
Browse files Browse the repository at this point in the history
Use `beat.Event.GetValue` so that @timestamp and @metadata are accessible to
be hashed.

Also clarify the documentation to explain how the value being hashed is constructed.

Fixes #28683

(cherry picked from commit 6390852)

Co-authored-by: Andrew Kroh <[email protected]>
  • Loading branch information
mergify[bot] and andrewkroh authored Nov 2, 2021
1 parent 7404a24 commit 835a241
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Allow `clone3` syscall in seccomp filters. {pull}28117[28117]
- Remove unnecessary escaping step in dashboard loading, so they can be displayed in Kibana. {pull}28395[28395]
- Fix handling of float data types within processors. {issue}28279[28279] {pull}28280[28280]
- Fix `fingerprint` processor to give it access to the `@timestamp` field. {issue}28683[28683]

*Auditbeat*

Expand Down
6 changes: 5 additions & 1 deletion libbeat/processors/fingerprint/docs/fingerprint.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
The `fingerprint` processor generates a fingerprint of an event based on a
specified subset of its fields.

The value that is hashed is constructed as a concatenation of the field name and
field value separated by `|`. For example `|field1|value1|field2|value2|`.

[source,yaml]
-----------------------------------------------------
processors:
Expand All @@ -17,7 +20,8 @@ processors:

The following settings are supported:

`fields`:: List of fields to use as the source for the fingerprint.
`fields`:: List of fields to use as the source for the fingerprint. The list
will be alphabetically sorted by the processor.
`ignore_missing`:: (Optional) Whether to ignore missing fields. Default is `false`.
`target_field`:: (Optional) Field in which the generated fingerprint should be stored. Default is `fingerprint`.
`method`:: (Optional) Algorithm to use for computing the fingerprint. Must be one of: `md5`, `sha1`, `sha256`, `sha384`, `sha512`, `xxhash`. Default is `sha256`.
Expand Down
2 changes: 1 addition & 1 deletion libbeat/processors/fingerprint/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func makeErrConfigUnpack(cause error) errConfigUnpack {
return errConfigUnpack{cause}
}
func (e errConfigUnpack) Error() string {
return fmt.Sprintf("failed to unpack %v processor configuration: %v", processorName, e.cause)
return fmt.Sprintf("failed to unpack %v processor configuration: %v", procName, e.cause)
}

func makeErrComputeFingerprint(cause error) errComputeFingerprint {
Expand Down
34 changes: 18 additions & 16 deletions libbeat/processors/fingerprint/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
package fingerprint

import (
"encoding/json"
"fmt"
"io"
"strings"
"time"

"github.com/elastic/beats/v7/libbeat/beat"
Expand All @@ -28,13 +30,15 @@ import (
jsprocessor "github.com/elastic/beats/v7/libbeat/processors/script/javascript/module/processor"
)

const (
procName = "fingerprint"
)

func init() {
processors.RegisterPlugin("fingerprint", New)
jsprocessor.RegisterPlugin("Fingerprint", New)
processors.RegisterPlugin(procName, New)
jsprocessor.RegisterPlugin(strings.Title(procName), New)
}

const processorName = "fingerprint"

type fingerprint struct {
config Config
fields []string
Expand Down Expand Up @@ -62,49 +66,47 @@ func New(cfg *common.Config) (processors.Processor, error) {
return p, nil
}

// Run enriches the given event with fingerprint information
// Run enriches the given event with a fingerprint.
func (p *fingerprint) Run(event *beat.Event) (*beat.Event, error) {
hashFn := p.hash()

err := p.writeFields(hashFn, event.Fields)
if err != nil {
if err := p.writeFields(hashFn, event); err != nil {
return nil, makeErrComputeFingerprint(err)
}

hash := hashFn.Sum(nil)
encodedHash := p.config.Encoding(hash)
encodedHash := p.config.Encoding(hashFn.Sum(nil))

if _, err = event.PutValue(p.config.TargetField, encodedHash); err != nil {
if _, err := event.PutValue(p.config.TargetField, encodedHash); err != nil {
return nil, makeErrComputeFingerprint(err)
}

return event, nil
}

func (p *fingerprint) String() string {
return fmt.Sprintf("%v=[method=[%v]]", processorName, p.config.Method)
json, _ := json.Marshal(p.config)
return procName + "=" + string(json)
}

func (p *fingerprint) writeFields(to io.Writer, eventFields common.MapStr) error {
func (p *fingerprint) writeFields(to io.Writer, event *beat.Event) error {
for _, k := range p.fields {
v, err := eventFields.GetValue(k)
v, err := event.GetValue(k)
if err != nil {
if p.config.IgnoreMissing {
continue
}
return makeErrMissingField(k, err)
}

i := v
switch vv := v.(type) {
case map[string]interface{}, []interface{}, common.MapStr:
return makeErrNonScalarField(k)
case time.Time:
// Ensure we consistently hash times in UTC.
i = vv.UTC()
v = vv.UTC()
}

fmt.Fprintf(to, "|%v|%v", k, i)
fmt.Fprintf(to, "|%v|%v", k, v)
}

io.WriteString(to, "|")
Expand Down
11 changes: 10 additions & 1 deletion libbeat/processors/fingerprint/fingerprint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ func TestWithConfig(t *testing.T) {
},
want: "14a0364b79acbe4c78dd5e77db2c93ae8c750518b32581927d50b3eef407184e",
},
"with @timestamp": {
config: common.MapStr{
"fields": []string{"@timestamp", "message"},
},
input: common.MapStr{
"message": `test message "hello world"`,
},
want: "081da76e049554943843b83948ac83ab7aa79fd2849331813e02042586021c26",
},
}

for name, test := range cases {
Expand All @@ -63,7 +72,7 @@ func TestWithConfig(t *testing.T) {
require.NoError(t, err)

testEvent := &beat.Event{
Timestamp: time.Now(),
Timestamp: time.Unix(1635443183, 0),
Fields: test.input.Clone(),
}
newEvent, err := p.Run(testEvent)
Expand Down

0 comments on commit 835a241

Please sign in to comment.