Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions thorlog/v3/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,19 @@ type Context []ContextObject

// ContextObject describes a relation of an object to another.
type ContextObject struct {
Object ReportableObject `json:"object" textlog:",expand"`
RelationType string `json:"relation_type"` // RelationType is used to specify the type of relation, e.g. "derives from" or "related to"
RelationName string `json:"relation_name"` // RelationName is used to specify the name of the relation, e.g. "parent". It is optional.
Unique bool `json:"unique"` // Unique indicates whether the relation is unique, i.e. there can only be one object with this relation type / name in the context.
Object ReportableObject `json:"object" textlog:",expand"`
// Relations describes how the object relates to the main subject of the finding.
// 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.
// Only the first (and most relevant) relation is used for text log formatting.
Relations []Relation `json:"relations" textlog:",expand" jsonschema:"minItems=1"`
}

type Relation struct {
Type string `json:"relation_type"` // RelationType is used to specify the type of relation, e.g. "derives from" or "related to"
Name string `json:"relation_name"` // RelationName is used to specify the name of the relation, e.g. "parent". It is optional.
Unique bool `json:"unique"` // Unique indicates whether the relation is unique, i.e. there can only be one object with this relation type / name in the context.
}

func (c *ContextObject) UnmarshalJSON(data []byte) error {
Expand All @@ -148,18 +157,27 @@ func (c *ContextObject) UnmarshalJSON(data []byte) error {
const omitInContext = "omitincontext"

func (c Context) MarshalTextLog(t jsonlog.TextlogFormatter) jsonlog.TextlogEntry {
var elementsByRelation [][]ContextObject
type objectsByRelation struct {
Relation Relation
Objects []ContextObject
}
var elementsByRelation []objectsByRelation
for _, element := range c {
var groupExists bool
if len(element.Relations) == 0 {
continue
}
// only use the first relation for textlog conversion
relation := element.Relations[0]
for i := range elementsByRelation {
if elementsByRelation[i][0].RelationName == element.RelationName {
elementsByRelation[i] = append(elementsByRelation[i], element)
if elementsByRelation[i].Relation == relation {
elementsByRelation[i].Objects = append(elementsByRelation[i].Objects, element)
groupExists = true
break
}
}
if !groupExists {
elementsByRelation = append(elementsByRelation, []ContextObject{element})
elementsByRelation = append(elementsByRelation, objectsByRelation{Relation: relation, Objects: []ContextObject{element}})
}
}
oldOmit := t.Omit
Expand All @@ -175,11 +193,11 @@ func (c Context) MarshalTextLog(t jsonlog.TextlogFormatter) jsonlog.TextlogEntry

var result jsonlog.TextlogEntry
for _, group := range elementsByRelation {
for g, element := range group {
for g, element := range group.Objects {
marshaledElement := t.Format(element)
for i := range marshaledElement {
marshaledElement[i].Key = jsonlog.ConcatTextLabels(strings.ToUpper(element.RelationName), marshaledElement[i].Key)
if !element.Unique {
marshaledElement[i].Key = jsonlog.ConcatTextLabels(strings.ToUpper(group.Relation.Name), marshaledElement[i].Key)
if !group.Relation.Unique {
marshaledElement[i].Key = jsonlog.ConcatTextLabels(marshaledElement[i].Key, strconv.Itoa(g+1))
}
}
Expand Down
64 changes: 47 additions & 17 deletions thorlog/v3/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ func TestContext_MarshalTextLog(t *testing.T) {
name: "context with unique related object",
c: &Context{
{
Object: NewFile("path/to/file"),
RelationName: "file",
Unique: true,
Object: NewFile("path/to/file"),
Relations: []Relation{{
Name: "file",
Unique: true,
}},
},
},
want: "FILE: path/to/file",
Expand All @@ -37,14 +39,18 @@ func TestContext_MarshalTextLog(t *testing.T) {
name: "context with related object group",
c: &Context{
{
Object: NewFile("path/to/file"),
RelationName: "file",
Unique: false,
Object: NewFile("path/to/file"),
Relations: []Relation{{
Name: "file",
Unique: false,
}},
},
{
Object: NewFile("path/to/otherfile"),
RelationName: "file",
Unique: false,
Object: NewFile("path/to/otherfile"),
Relations: []Relation{{
Name: "file",
Unique: false,
}},
},
},
want: "FILE_1: path/to/file FILE_2: path/to/otherfile",
Expand All @@ -53,18 +59,40 @@ func TestContext_MarshalTextLog(t *testing.T) {
name: "context with different related objects",
c: &Context{
{
Object: NewFile("path/to/file"),
RelationName: "file",
Unique: false,
Object: NewFile("path/to/file"),
Relations: []Relation{{
Name: "file",
Unique: false,
}},
},
{
Object: NewFile("path/to/otherfile"),
RelationName: "archive",
Unique: true,
Object: NewFile("path/to/otherfile"),
Relations: []Relation{{
Name: "archive",
Unique: true,
}},
},
},
want: "FILE_1: path/to/file ARCHIVE_FILE: path/to/otherfile",
},
{
name: "context with object related in two ways",
c: &Context{
{
Object: NewFile("path/to/file"),
Relations: []Relation{{
Name: "parent",
Type: "derived from",
Unique: true,
}, {
Name: "origin",
Type: "derived from",
Unique: true,
}},
},
},
want: "PARENT_FILE: path/to/file",
},
}
var formatter jsonlog.TextlogFormatter
for _, tt := range tests {
Expand Down Expand Up @@ -104,8 +132,10 @@ func TestFinding_UnmarshalJSON(t *testing.T) {
Subject: NewFile("path/to/file"),
EventContext: Context{
{
Object: NewAtJob(),
RelationType: "related to",
Object: NewAtJob(),
Relations: []Relation{{
Type: "related to",
}},
},
},
Reasons: []Reason{
Expand Down