feat(go): advance validation sidecar toward production
This commit is contained in:
parent
6a06eba255
commit
f85c2e831a
92 changed files with 8881 additions and 91 deletions
162
go-agent/cmd/enodia-sentinel-go/main_test.go
Normal file
162
go-agent/cmd/enodia-sentinel-go/main_test.go
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// 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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue