diff --git a/packetbeat/pb/event.go b/packetbeat/pb/event.go index e20243e56a5..de8bbaa7e3f 100644 --- a/packetbeat/pb/event.go +++ b/packetbeat/pb/event.go @@ -424,6 +424,12 @@ func marshalStruct(m mapstr.M, key string, val reflect.Value) error { } return nil } + + // NumField() will panic if we don't have a struct + if val.Type().Kind() != reflect.Struct { + return fmt.Errorf("value must be a struct or a pointer to a struct, but got %v at key %s", val.Type(), key) + } + for i := 0; i < typ.NumField(); i++ { structField := typ.Field(i) @@ -455,8 +461,10 @@ func marshalStruct(m mapstr.M, key string, val reflect.Value) error { if err := marshalStruct(m, key, fieldValue); err != nil { return err } - // assume a pointer is a struct or other object we can marshal - } else if structField.Type.Kind() == reflect.Ptr || structField.Type.Kind() == reflect.Struct { + // look for a struct or pointer to a struct + // that reflect.Ptr check is needed so Elem() doesn't panic + } else if (structField.Type.Kind() == reflect.Ptr && fieldValue.Elem().Kind() == reflect.Struct) || + structField.Type.Kind() == reflect.Struct { if err := marshalStruct(m, key+"."+tag, fieldValue); err != nil { return err } diff --git a/packetbeat/pb/event_test.go b/packetbeat/pb/event_test.go index 5debd33fb03..b10c2f78d50 100644 --- a/packetbeat/pb/event_test.go +++ b/packetbeat/pb/event_test.go @@ -24,11 +24,68 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/elastic/beats/v7/libbeat/ecs" "github.com/elastic/elastic-agent-libs/mapstr" ) +func TestTimeMarshal(t *testing.T) { + testTime := time.Now() + f := NewFields() + + f.Process = &ecs.Process{ + Start: testTime, + Parent: &ecs.Process{ + Start: testTime, + }, + } + + m := mapstr.M{} + err := f.MarshalMapStr(m) + require.NoError(t, err) + procData := m["process"] + assert.Equal(t, testTime, procData.(mapstr.M)["start"]) + assert.Equal(t, testTime, procData.(mapstr.M)["parent"].(mapstr.M)["start"]) + +} + +func TestPointerHandling(t *testing.T) { + testInt := 10 + testStr := "test" + // test to make to sure we correctly handle pointers that aren't structs + // mostly checking to make sure we don't panic due to pointer/reflect bugs + testStruct := struct { + PointerInt *int `ecs:"one"` + SecondPointerInt *int `ecs:"two"` + TestStruct *ecs.Process `ecs:"struct"` + StrPointer *string `ecs:"string"` + }{ + PointerInt: nil, + SecondPointerInt: &testInt, + StrPointer: &testStr, + TestStruct: &ecs.Process{ + Name: "Test", + }, + } + + out := mapstr.M{} + err := MarshalStruct(out, "test", testStruct) + require.NoError(t, err) + + want := mapstr.M{ + "test": mapstr.M{ + "struct": mapstr.M{ + "name": "Test", + }, + "two": &testInt, + "string": &testStr, + }, + } + + require.Equal(t, want, out) +} + func TestMarshalMapStr(t *testing.T) { f := NewFields() f.Source = &ecs.Source{IP: "127.0.0.1"}