From 434905144f98ae46dd426702b63ebdcade6f246d Mon Sep 17 00:00:00 2001 From: Max Altgelt Date: Fri, 6 Feb 2026 15:31:24 +0100 Subject: [PATCH] feat: rename finding --- thorlog/common/event.go | 2 +- thorlog/jsonschema/generateschema.go | 2 +- thorlog/parser/parser_test.go | 10 ++--- thorlog/v3/event.go | 66 ++++++++++++++-------------- thorlog/v3/event_test.go | 30 ++++++------- thorlog/v3/issue.go | 2 +- thorlog/v3/reason.go | 8 ++-- 7 files changed, 60 insertions(+), 60 deletions(-) diff --git a/thorlog/common/event.go b/thorlog/common/event.go index 0b8be81..c34c1bf 100644 --- a/thorlog/common/event.go +++ b/thorlog/common/event.go @@ -28,7 +28,7 @@ type LogEventMetadata struct { Mod string `json:"module" textlog:"module"` // The ID of the scan where this event was created. ScanID string `json:"scan_id" textlog:"scanid,omitempty"` - // A unique ID for this finding. + // A unique ID for this event. // The ID is transient and the same element may have different IDs across multiple scans. GenID string `json:"event_id,omitempty" textlog:"uid,omitempty"` // The hostname of the machine where this event was generated. diff --git a/thorlog/jsonschema/generateschema.go b/thorlog/jsonschema/generateschema.go index 18c7b10..c35c507 100644 --- a/thorlog/jsonschema/generateschema.go +++ b/thorlog/jsonschema/generateschema.go @@ -97,7 +97,7 @@ func main() { Title: "ThorEvent", OneOf: []*jsonschema.Schema{ { - Ref: "#/$defs/Finding", + Ref: "#/$defs/Assessment", }, { Ref: "#/$defs/Message", diff --git a/thorlog/parser/parser_test.go b/thorlog/parser/parser_test.go index 418df90..b03d71a 100644 --- a/thorlog/parser/parser_test.go +++ b/thorlog/parser/parser_test.go @@ -129,11 +129,11 @@ func TestParseEvent(t *testing.T) { }, }, { - "JsonV3Finding", - `{"type":"THOR finding","meta":{"time":"2024-09-24T14:18:46.190394329+02:00","level":"Alert","module":"Test","scan_id":"abdc","event_id":"abdas","hostname":"aserarsd"},"message":"This is a test finding","subject":{"type":"file","path":"path/to/file"},"score":70,"reasons":[{"type":"reason","summary":"Reason 1","signature":{"score":70,"ref":null,"origin":"internal","kind":""},"matched":null}],"reason_count":0,"context":[{"object":{"type":"at job"},"relation":"","unique":false}],"log_version":"v3"}`, - &thorlog.Finding{ + "JsonV3Assessment", + `{"type":"THOR assessment","meta":{"time":"2024-09-24T14:18:46.190394329+02:00","level":"Alert","module":"Test","scan_id":"abdc","event_id":"abdas","hostname":"aserarsd"},"message":"This is a test assessment","subject":{"type":"file","path":"path/to/file"},"score":70,"reasons":[{"type":"reason","summary":"Reason 1","signature":{"score":70,"ref":null,"origin":"internal","kind":""},"matched":null}],"reason_count":0,"context":[{"object":{"type":"at job"},"relation":"","unique":false}],"log_version":"v3"}`, + &thorlog.Assessment{ ObjectHeader: jsonlog.ObjectHeader{ - Type: "THOR finding", + Type: "THOR assessment", }, Meta: thorlog.LogEventMetadata{ Time: mustTime("2024-09-24T14:18:46.190394329+02:00"), @@ -143,7 +143,7 @@ func TestParseEvent(t *testing.T) { GenID: "abdas", Source: "aserarsd", }, - Text: "This is a test finding", + Text: "This is a test assessment", Subject: &thorlog.File{ ObjectHeader: jsonlog.ObjectHeader{ Type: "file", diff --git a/thorlog/v3/event.go b/thorlog/v3/event.go index e9958bd..56ea699 100644 --- a/thorlog/v3/event.go +++ b/thorlog/v3/event.go @@ -14,21 +14,21 @@ import ( "golang.org/x/exp/slices" ) -// Finding is a summary of a Subject's analysis by THOR. -// This object is usually, but not necessarily suspicious; the +// Assessment is a summary of a Subject's analysis by THOR. +// The assessed object is not necessarily suspicious; the // severity can be seen in the Score, and beyond that the -// Reasons contain further information on why this Subject is +// Reasons contain further information if this Subject is // considered suspicious. -type Finding struct { +type Assessment struct { jsonlog.ObjectHeader Meta LogEventMetadata `json:"meta" textlog:",expand"` - // Text is the message THOR printed for this finding. - // This is usually a summary based on this finding's subject and level. + // Text is the message THOR printed for this assessment. + // This is usually a summary based on this assessment's subject and level. Text string `json:"message" textlog:"message"` - // Subject is the object analysed by THOR. - Subject ReportableObject `json:"subject" textlog:",expand"` + // Subject is the object assessed by THOR. + Subject AssessableObject `json:"subject" textlog:",expand"` // Score is a metric that combines severity and certainty. The score is always in a range of 0 to 100; - // 0 indicates that the analysis found no suspicious indicators, whereas 100 indicates very high + // 0 indicates that the assessment found no suspicious indicators, whereas 100 indicates very high // severity and certainty. Score int64 `json:"score" textlog:"score"` // Reasons describes the indicators that contributed to the score. @@ -45,7 +45,7 @@ type Finding struct { // and a relation name of "parent", indicating that the Subject derives from this object, // which is its parent. EventContext Context `json:"context" textlog:",expand" jsonschema:"nullable"` - // Issues lists any problems that THOR encountered when trying to create a Finding for this analysis. + // Issues lists any problems that THOR encountered when trying to create a JSON struct for this assessment. // This may include e.g. overly long fields that were truncated, fields that could not be rendered to JSON, // or similar problems. Issues []Issue `json:"issues,omitempty" textlog:"-"` @@ -53,38 +53,38 @@ type Finding struct { LogVersion common.Version `json:"log_version"` } -// ReportableObject can be any object type that THOR analyses, e.g. File or Process. -type ReportableObject interface { +// AssessableObject can be any object type that THOR assesses, e.g. File or Process. +type AssessableObject interface { reportable() jsonlog.Object } -func (f *Finding) Message() string { +func (f *Assessment) Message() string { return f.Text } -func (f *Finding) Version() common.Version { +func (f *Assessment) Version() common.Version { return f.LogVersion } -func (f *Finding) Metadata() *LogEventMetadata { +func (f *Assessment) Metadata() *LogEventMetadata { return &f.Meta } -func (f *Finding) UnmarshalJSON(data []byte) error { - type plainFinding Finding - var rawFinding struct { - plainFinding // Embed without unmarshal method to avoid infinite recursion - Subject EmbeddedObject `json:"subject"` // EmbeddedObject is used to allow unmarshalling of the subject as a ReportableObject +func (f *Assessment) UnmarshalJSON(data []byte) error { + type plainAssessment Assessment + var rawAssessment struct { + plainAssessment // Embed without unmarshal method to avoid infinite recursion + Subject EmbeddedObject `json:"subject"` // EmbeddedObject is used to allow unmarshalling of the subject as a AssessableObject } - if err := json.Unmarshal(data, &rawFinding); err != nil { + if err := json.Unmarshal(data, &rawAssessment); err != nil { return err } - subject, ok := rawFinding.Subject.Object.(ReportableObject) + subject, ok := rawAssessment.Subject.Object.(AssessableObject) if !ok { return fmt.Errorf("subject must implement the reportable interface") } - *f = Finding(rawFinding.plainFinding) // Copy the fields from rawFinding to f + *f = Assessment(rawAssessment.plainAssessment) // Copy the fields from rawAssessment to f f.Subject = subject // Resolve all references @@ -115,14 +115,14 @@ func (f *Finding) UnmarshalJSON(data []byte) error { return nil } -var _ common.Event = (*Finding)(nil) +var _ common.Event = (*Assessment)(nil) type Context []ContextObject // ContextObject describes a relation of an object to another. type ContextObject struct { - Object ReportableObject `json:"object" textlog:",expand"` - // Relations describes how the object relates to the main subject of the finding. + Object AssessableObject `json:"object" textlog:",expand"` + // Relations describes how the object relates to the assessed subject. // There may be multiple relations, e.g. if the object is both the parent and the topmost ancestor of the subject. // // Relations should be ordered by relevance, i.e. the most important relation should be first. @@ -145,7 +145,7 @@ func (c *ContextObject) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &rawContextObject); err != nil { return err } - reportableObject, isReportable := rawContextObject.Object.Object.(ReportableObject) + reportableObject, isReportable := rawContextObject.Object.Object.(AssessableObject) if !isReportable { return fmt.Errorf("object of type %q must implement the reportable interface", rawContextObject.Object.Object.EmbeddedHeader().Type) } @@ -207,14 +207,14 @@ func (c Context) MarshalTextLog(t jsonlog.TextlogFormatter) jsonlog.TextlogEntry return result } -const typeFinding = "THOR finding" +const typeAssessment = "THOR assessment" -func init() { AddLogObjectType(typeFinding, &Finding{}) } +func init() { AddLogObjectType(typeAssessment, &Assessment{}) } -func NewFinding(subject ReportableObject, message string) *Finding { - return &Finding{ +func NewAssessment(subject AssessableObject, message string) *Assessment { + return &Assessment{ ObjectHeader: LogObjectHeader{ - Type: typeFinding, + Type: typeAssessment, }, Text: message, Subject: subject, @@ -223,7 +223,7 @@ func NewFinding(subject ReportableObject, message string) *Finding { } // Message describes a THOR message printed during the scan. -// Unlike Finding, this does not describe an analysis' result, +// Unlike Assessment, this does not describe an analysis' result, // but rather something about the scan itself (e.g. how many IOCs were loaded). type Message struct { jsonlog.ObjectHeader diff --git a/thorlog/v3/event_test.go b/thorlog/v3/event_test.go index 3568fee..b41577e 100644 --- a/thorlog/v3/event_test.go +++ b/thorlog/v3/event_test.go @@ -117,10 +117,10 @@ func concatEntry(entry jsonlog.TextlogEntry) string { return builder.String() } -func TestFinding_UnmarshalJSON(t *testing.T) { - for i, finding := range []*Finding{ +func TestAssessment_UnmarshalJSON(t *testing.T) { + for i, assessment := range []*Assessment{ { - ObjectHeader: LogObjectHeader{Type: typeFinding}, + ObjectHeader: LogObjectHeader{Type: typeAssessment}, Meta: LogEventMetadata{ Lvl: common.Alert, Mod: "Test", @@ -128,7 +128,7 @@ func TestFinding_UnmarshalJSON(t *testing.T) { GenID: "abdas", Source: "aserarsd", }, - Text: "This is a test finding", + Text: "This is a test assessment", Subject: NewFile("path/to/file"), EventContext: Context{ { @@ -146,27 +146,27 @@ func TestFinding_UnmarshalJSON(t *testing.T) { }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { - jsonform, err := json.Marshal(finding) + jsonform, err := json.Marshal(assessment) if err != nil { t.Fatal(err) } t.Log(string(jsonform)) - var newFinding Finding - if err := json.Unmarshal(jsonform, &newFinding); err != nil { + var newAssessment Assessment + if err := json.Unmarshal(jsonform, &newAssessment); err != nil { t.Fatal(err) } - newFinding.LogVersion = common.Version(newFinding.LogVersion.Major()) - if !reflect.DeepEqual(finding, &newFinding) { - t.Errorf("UnmarshalJSON() = %+v, want %+v", newFinding, *finding) + newAssessment.LogVersion = common.Version(newAssessment.LogVersion.Major()) + if !reflect.DeepEqual(assessment, &newAssessment) { + t.Errorf("UnmarshalJSON() = %+v, want %+v", newAssessment, *assessment) } }) } } -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","mask":{"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) +func TestAssessment_UnmarshalIssue(t *testing.T) { + assessment := `{"type":"THOR assessment","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","mask":{"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 assessmentObj Assessment + if err := json.Unmarshal([]byte(assessment), &assessmentObj); err != nil { + t.Fatalf("Failed to unmarshal assessment: %v", err) } } diff --git a/thorlog/v3/issue.go b/thorlog/v3/issue.go index f6ca791..b2efa8f 100644 --- a/thorlog/v3/issue.go +++ b/thorlog/v3/issue.go @@ -2,7 +2,7 @@ package thorlog import "github.com/NextronSystems/jsonlog" -// Issue describes a problem that occurred during the analysis of a scan target like a file or process. +// Issue describes a problem that occurred during the assessment of a scan target like a file or process. // Often this will be an issue with displaying the results, // e.g. the results may be truncated due to size limitations. type Issue struct { diff --git a/thorlog/v3/reason.go b/thorlog/v3/reason.go index a0dbd64..742c810 100644 --- a/thorlog/v3/reason.go +++ b/thorlog/v3/reason.go @@ -7,7 +7,7 @@ import ( "github.com/NextronSystems/jsonlog" ) -// Reason describes a match of a single Signature on a ReportableObject. +// Reason describes a match of a single Signature on a AssessableObject. type Reason struct { jsonlog.ObjectHeader @@ -38,11 +38,11 @@ func init() { type Signature struct { // Score is a metric that combines severity and certainty for this signature. // - // It is related to the Finding.Score, which is derived from the scores of all + // It is related to the Assessment.Score, which is derived from the scores of all // signatures that matched; however, signature scores are not limited to the - // 0 to 100 interval of finding scores, but may also be negative to indicate + // 0 to 100 interval of assessment scores, but may also be negative to indicate // a likely false positive (which results in a score reduction on any related - // finding). + // assessment). Score int64 `json:"score" textlog:"subscore"` // Ref contains references (usually as links) for further information about // the threat that is detected by this signature.