From 5febde9ab0c29b5081fbcf078c49841aff411fb7 Mon Sep 17 00:00:00 2001 From: Matthias Bertschy Date: Thu, 19 Feb 2026 15:25:47 +0100 Subject: [PATCH] add NormalizePath to ensure IG paths are always full Signed-off-by: Matthias Bertschy --- pkg/utils/datasource_event.go | 10 +-- pkg/utils/normalize_path_test.go | 72 +++++++++++++++++++ pkg/utils/path.go | 34 +++++++++ pkg/utils/struct_event.go | 10 +-- .../chart/templates/node-agent/daemonset.yaml | 4 ++ 5 files changed, 120 insertions(+), 10 deletions(-) create mode 100644 pkg/utils/normalize_path_test.go create mode 100644 pkg/utils/path.go diff --git a/pkg/utils/datasource_event.go b/pkg/utils/datasource_event.go index 0c31c8b20..6da5497fe 100644 --- a/pkg/utils/datasource_event.go +++ b/pkg/utils/datasource_event.go @@ -381,7 +381,7 @@ func (e *DatasourceEvent) GetExePath() string { switch e.EventType { case DnsEventType, ExecveEventType, ForkEventType, PtraceEventType, RandomXEventType, KmodEventType, UnshareEventType, BpfEventType: exepath, _ := e.getFieldAccessor("exepath").String(e.Data) - return exepath + return NormalizePath(exepath) default: logger.L().Warning("GetExePath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -432,7 +432,7 @@ func (e *DatasourceEvent) GetFullPath() string { if path == "" { path, _ = e.getFieldAccessor("fname").String(e.Data) } - return path + return NormalizePath(path) default: logger.L().Warning("GetFullPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -499,7 +499,7 @@ func (e *DatasourceEvent) GetNewPath() string { switch e.EventType { case HardlinkEventType, SymlinkEventType: newPath, _ := e.getFieldAccessor("newpath").String(e.Data) - return newPath + return NormalizePath(newPath) default: logger.L().Warning("GetNewPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -521,7 +521,7 @@ func (e *DatasourceEvent) GetOldPath() string { switch e.EventType { case HardlinkEventType, SymlinkEventType: oldPath, _ := e.getFieldAccessor("oldpath").String(e.Data) - return oldPath + return NormalizePath(oldPath) default: logger.L().Warning("GetOldPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -559,7 +559,7 @@ func (e *DatasourceEvent) GetPath() string { switch e.EventType { case OpenEventType: path, _ := e.getFieldAccessor("fname").String(e.Data) - return path + return NormalizePath(path) default: logger.L().Warning("GetPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" diff --git a/pkg/utils/normalize_path_test.go b/pkg/utils/normalize_path_test.go new file mode 100644 index 000000000..7ab3a2494 --- /dev/null +++ b/pkg/utils/normalize_path_test.go @@ -0,0 +1,72 @@ +package utils + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNormalizePath(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "empty path", + input: "", + expected: "", + }, + { + name: "dot path", + input: ".", + expected: "/", + }, + { + name: "absolute path", + input: "/etc/passwd", + expected: "/etc/passwd", + }, + { + name: "headless proc path (task)", + input: "/46/task/46/fd", + expected: "/proc/46/task/46/fd", + }, + { + name: "headless proc path (fd)", + input: "/46/fd/3", + expected: "/proc/46/fd/3", + }, + { + name: "already absolute proc path", + input: "/proc/46/fd/3", + expected: "/proc/46/fd/3", + }, + { + name: "relative path (not dot)", + input: "usr/bin/ls", + expected: "/usr/bin/ls", + }, + { + name: "relative path with ./", + input: "./config", + expected: "/config", + }, + { + name: "path with redundant slashes", + input: "/etc//passwd", + expected: "/etc/passwd", + }, + { + name: "path with dot components", + input: "/usr/./bin/../lib", + expected: "/usr/lib", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NormalizePath(tt.input) + assert.Equal(t, tt.expected, got) + }) + } +} diff --git a/pkg/utils/path.go b/pkg/utils/path.go new file mode 100644 index 000000000..0199ce916 --- /dev/null +++ b/pkg/utils/path.go @@ -0,0 +1,34 @@ +package utils + +import ( + "path" + "regexp" + "strings" +) + +var headlessProcRegex = regexp.MustCompile(`^/\d+/(task|fd)/`) + +// NormalizePath normalizes a path by: +// 1. Prepending "/proc" to "headless" proc paths (e.g. /46/task/46/fd -> /proc/46/task/46/fd) +// 2. Ensuring it starts with "/" if it's not empty +// 3. Converting "." to "/" +// 4. Cleaning the path (removing redundant slashes, dot-dots, etc.) +func NormalizePath(p string) string { + if p == "" { + return "" + } + + if p == "." { + return "/" + } + + if headlessProcRegex.MatchString(p) { + p = "/proc" + p + } + + if !strings.HasPrefix(p, "/") { + p = "/" + p + } + + return path.Clean(p) +} diff --git a/pkg/utils/struct_event.go b/pkg/utils/struct_event.go index 674af7037..71ca83af2 100644 --- a/pkg/utils/struct_event.go +++ b/pkg/utils/struct_event.go @@ -252,7 +252,7 @@ func (e *StructEvent) GetEventType() EventType { func (e *StructEvent) GetExePath() string { switch e.EventType { case DnsEventType, ExecveEventType, ForkEventType, PtraceEventType, RandomXEventType, KmodEventType, UnshareEventType, BpfEventType: - return e.ExePath + return NormalizePath(e.ExePath) default: logger.L().Warning("GetExePath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -296,7 +296,7 @@ func (e *StructEvent) GetFlagsRaw() uint32 { func (e *StructEvent) GetFullPath() string { switch e.EventType { case OpenEventType: - return e.FullPath + return NormalizePath(e.FullPath) default: logger.L().Warning("GetFullPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -352,7 +352,7 @@ func (e *StructEvent) GetNamespace() string { func (e *StructEvent) GetNewPath() string { switch e.EventType { case HardlinkEventType, SymlinkEventType: - return e.NewPath + return NormalizePath(e.NewPath) default: logger.L().Warning("GetNewPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -372,7 +372,7 @@ func (e *StructEvent) GetNumAnswers() int { func (e *StructEvent) GetOldPath() string { switch e.EventType { case HardlinkEventType, SymlinkEventType: - return e.OldPath + return NormalizePath(e.OldPath) default: logger.L().Warning("GetOldPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" @@ -408,7 +408,7 @@ func (e *StructEvent) GetPath() string { } switch e.EventType { case OpenEventType: - return e.Path + return NormalizePath(e.Path) default: logger.L().Warning("GetPath not implemented for event type", helpers.String("eventType", string(e.EventType))) return "" diff --git a/tests/chart/templates/node-agent/daemonset.yaml b/tests/chart/templates/node-agent/daemonset.yaml index 581556233..0eebaf6c5 100644 --- a/tests/chart/templates/node-agent/daemonset.yaml +++ b/tests/chart/templates/node-agent/daemonset.yaml @@ -119,6 +119,10 @@ spec: {{- if .value }} value: "{{ .value }}" {{- end }} + {{- if .valueFrom }} + valueFrom: +{{ toYaml .valueFrom | indent 16 }} + {{- end }} {{- end }} securityContext: runAsUser: 0