// SPDX-License-Identifier: GPL-3.0-or-later package main import ( "context" "fmt" "io" "strings" "syscall" "testing" "time" "codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/agent" "codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/config" "codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/ebpfsource" "codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/events" "codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/model" ) type oneExecReader struct { event events.ExecEvent cancel context.CancelFunc read bool } func (r *oneExecReader) Read() (events.ExecEvent, error) { if !r.read { r.read = true return r.event, nil } r.cancel() return events.ExecEvent{}, io.EOF } type oneSyscallReader struct { event ebpfsource.SecurityEvent cancel context.CancelFunc read bool } func (r *oneSyscallReader) Read() (ebpfsource.SecurityEvent, error) { if !r.read { r.read = true return r.event, nil } r.cancel() return ebpfsource.SecurityEvent{}, io.EOF } func TestMonitorExecSourceUsesSharedAlertPipeline(t *testing.T) { runner := monitorTestAgent() ctx, cancel := context.WithCancel(context.Background()) reader := &oneExecReader{ event: events.ExecEvent{PID: 42, PPID: 7, UID: 1000, ParentComm: "bash", Filename: "/tmp/dropper"}, cancel: cancel, } var records []map[string]any monitorExecSource(ctx, reader, events.DefaultExecRuleEngine(), runner, func(record map[string]any) error { records = append(records, record) return nil }) if len(records) != 1 || records[0]["event_type"] != "alert" { t.Fatalf("unexpected records: %#v", records) } alert := records[0]["alert"].(model.Alert) if alert.SID != 100001 || alert.Key != "exec:100001:42" { t.Fatalf("unexpected alert: %#v", alert) } } func TestMonitorSyscallSourceUsesSharedAlertPipeline(t *testing.T) { runner := monitorTestAgent() ctx, cancel := context.WithCancel(context.Background()) reader := &oneSyscallReader{ event: ebpfsource.SecurityEvent{Syscall: &events.SyscallEvent{ PID: 42, PPID: 7, UID: 1000, Comm: "dropper", Syscall: "mprotect", Args: [6]uint64{0x1000, 0x2000, 0x6}, }}, cancel: cancel, } var records []map[string]any monitorSyscallSource(ctx, reader, events.DefaultSyscallRuleEngine(), events.DefaultHostRuleEngine(), runner, func(record map[string]any) error { records = append(records, record) return nil }) if len(records) != 1 || records[0]["event_type"] != "alert" { t.Fatalf("unexpected records: %#v", records) } alert := records[0]["alert"].(model.Alert) if alert.SID != 100060 || alert.Key != "syscall:100060:42:mprotect:1000:6" { t.Fatalf("unexpected alert: %#v", alert) } } func TestMonitorSyscallSourceRoutesTypedHostEvent(t *testing.T) { runner := monitorTestAgent() ctx, cancel := context.WithCancel(context.Background()) reader := &oneSyscallReader{ event: ebpfsource.SecurityEvent{Host: &events.HostEvent{ Event: "setuid", PID: 42, PPID: 7, UID: 1000, Comm: "python3", TargetUID: 0, TargetGID: -1, }}, cancel: cancel, } var records []map[string]any monitorSyscallSource(ctx, reader, events.DefaultSyscallRuleEngine(), events.DefaultHostRuleEngine(), runner, func(record map[string]any) error { records = append(records, record) return nil }) if len(records) != 1 { t.Fatalf("unexpected records: %#v", records) } alert := records[0]["alert"].(model.Alert) if alert.SID != 100071 || alert.Signature != "host_rule.privilege-transition" { t.Fatalf("unexpected alert: %#v", alert) } } func TestMonitorSyscallSourceRoutesPermissionChange(t *testing.T) { runner := monitorTestAgent() ctx, cancel := context.WithCancel(context.Background()) reader := &oneSyscallReader{ event: ebpfsource.SecurityEvent{Host: &events.HostEvent{ Event: "chmod", PID: 42, PPID: 7, UID: 1000, Comm: "installer", Path: "/etc/systemd/system/backdoor.service", Mode: 0o755, TargetUID: -1, TargetGID: -1, }}, cancel: cancel, } var records []map[string]any monitorSyscallSource(ctx, reader, events.DefaultSyscallRuleEngine(), events.DefaultHostRuleEngine(), runner, func(record map[string]any) error { records = append(records, record) return nil }) if len(records) != 1 { t.Fatalf("unexpected records: %#v", records) } alert := records[0]["alert"].(model.Alert) if alert.SID != 100070 || alert.Signature != "host_rule.persistence-permission-change" { t.Fatalf("unexpected alert: %#v", alert) } } func monitorTestAgent() *agent.Agent { runner := agent.New(config.Default(), nil) runner.Host = func() (string, error) { return "host-a", nil } runner.Now = func() time.Time { return time.Date(2026, 7, 20, 12, 0, 0, 0, time.UTC) } return runner } func TestProbeFailureStatusIsConcise(t *testing.T) { if got := probeFailureStatus(fmt.Errorf("load: %w", syscall.EPERM)); got != "permission denied" { t.Fatalf("permission status=%q", got) } got := probeFailureStatus(fmt.Errorf("%s\nverifier detail", strings.Repeat("x", 200))) if len([]rune(got)) != 160 || !strings.HasSuffix(got, "…") { t.Fatalf("unexpected truncated status: %q", got) } }