diff --git a/jsonpointer/resolve.go b/jsonpointer/resolve.go index 1114319..1a392a9 100644 --- a/jsonpointer/resolve.go +++ b/jsonpointer/resolve.go @@ -59,6 +59,16 @@ func findStructFieldByLabel(base reflect.Value, label string) (reflect.Value, bo if !field.IsExported() { continue } + if field.Anonymous { + // If the field is anonymous, we need to search recursively + // in the embedded struct. + embeddedValue := base.Field(i) + value, found := findByLabel(embeddedValue, label) + if found { + return value, true + } + continue + } if strings.SplitN(field.Tag.Get("json"), ",", 2)[0] == label { return base.Field(i), true } diff --git a/jsonpointer/resolve_test.go b/jsonpointer/resolve_test.go index 4c4b41f..8a598d6 100644 --- a/jsonpointer/resolve_test.go +++ b/jsonpointer/resolve_test.go @@ -6,6 +6,11 @@ type BaseStruct struct { Field1 string `json:"field1"` SliceField []string `json:"slice_field"` SubStruct *BaseStruct `json:"sub_struct"` + AnonymousSubstruct +} + +type AnonymousSubstruct struct { + Field2 string `json:"field2"` } func TestResolve(t *testing.T) { @@ -15,6 +20,9 @@ func TestResolve(t *testing.T) { SubStruct: &BaseStruct{ Field1: "sub_field1", }, + AnonymousSubstruct: AnonymousSubstruct{ + Field2: "field2", + }, } for _, tt := range []struct { desc string @@ -36,6 +44,11 @@ func TestResolve(t *testing.T) { pointer: "/sub_struct/field1", want: &base.SubStruct.Field1, }, + { + desc: "anonymous sub struct field", + pointer: "/field2", + want: &base.Field2, + }, } { t.Run(tt.desc, func(t *testing.T) { pointer, err := Parse(tt.pointer) diff --git a/thorlog/v3/event_test.go b/thorlog/v3/event_test.go index 6536819..658d81b 100644 --- a/thorlog/v3/event_test.go +++ b/thorlog/v3/event_test.go @@ -132,3 +132,11 @@ func TestFinding_UnmarshalJSON(t *testing.T) { }) } } + +func TestFinding_UnmarshalIssue(t *testing.T) { + finding := `{"type":"THOR finding","meta":{"time":"2025-07-01T12:05:12.993789131+02:00","level":"Info","module":"ProcessCheck","scan_id":"S-pSxgCmyvvfs","event_id":"","hostname":"dummy"},"message":"process found","subject":{"type":"process","pid":502168,"name":"chromium","command":"/usr/lib/chromium/chromium","owner":"owner","image":{"type":"file","path":"/usr/lib/chromium/chromium","exists":"yes","extension":"","magic_header":"ELF","hashes":{"md5":"fc04ee20f064adc18e370c22512e268e","sha1":"2c8b7d05d25e04db9c169ce85e8e8f84321ef0c8","sha256":"0cf1727aa8dc3995d5aa103001f656b8ee8a1b3ffbc6d8664c5ad95cf225771f"},"first_bytes":{"hex":"7f454c4602010100000000000000000003003e00","ascii":"ELF\u003e"},"file_times":{"modified":"2025-06-25T19:45:43+02:00","accessed":"2025-07-01T08:46:56.750309598+02:00","changed":"2025-06-26T08:39:59.980605063+02:00"},"size":252546120,"permissions":{"type":"unix permissions","owner":"root","group":"root","permissions":{"user":{"readable":true,"writable":true,"executable":true},"group":{"readable":true,"writable":false,"executable":true},"world":{"readable":true,"writable":false,"executable":true}}}},"parent_info":{"pid":9011,"exe":"/usr/lib/chromium/chromium","command":"/usr/lib/chromium/chromium"},"tree":["/usr/lib/chromium/chromium","/usr/lib/chromium/chromium"],"created":"2025-07-01T12:00:05+02:00","session":"","listen_ports":null,"connections":[]},"score":0,"reasons":null,"reason_count":0,"context":null,"issues":[{"affected":"/subject/sections","category":"truncated","description":"Removed some sections from process memory (originally 638)"}],"log_version":"v3.0.0"}` + var findingObj Finding + if err := json.Unmarshal([]byte(finding), &findingObj); err != nil { + t.Fatalf("Failed to unmarshal finding: %v", err) + } +}