90 lines
3.6 KiB
Go
90 lines
3.6 KiB
Go
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package events
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestHostEventCompatibilityAliases(t *testing.T) {
|
|
var event HostEvent
|
|
raw := []byte(`{"type":"accept","pid":"42","remote_ip":"8.8.8.8","remote_port":"51515","bind_ip":"0.0.0.0","bind_port":"4444","target_uid":"0","mode":"0o777","capability":"cap_sys_admin","name":"evil"}`)
|
|
if err := json.Unmarshal(raw, &event); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if event.Event != "accept" || event.PID != 42 || event.PeerIP != "8.8.8.8" ||
|
|
event.PeerPort != 51515 || event.LocalPort != 4444 || event.TargetUID != 0 ||
|
|
event.TargetGID != -1 || event.Mode != 0o777 || event.Capabilities[0] != "CAP_SYS_ADMIN" ||
|
|
event.ModuleName != "evil" {
|
|
t.Fatalf("compatibility decode drifted: %#v", event)
|
|
}
|
|
}
|
|
|
|
func TestDefaultHostRulesMatchAllBuiltins(t *testing.T) {
|
|
engine := DefaultHostRuleEngine()
|
|
tests := []struct {
|
|
event HostEvent
|
|
sid int
|
|
}{
|
|
{HostEvent{Event: "tcp_connect", PID: 1, Comm: "python3", PeerIP: "8.8.8.8", PeerPort: 4444, TargetUID: -1, TargetGID: -1}, 100067},
|
|
{HostEvent{Event: "listen", PID: 2, Comm: "python3", LocalPort: 4444, TargetUID: -1, TargetGID: -1}, 100068},
|
|
{HostEvent{Event: "bind", PID: 3, Comm: "python3", LocalPort: 4444, TargetUID: -1, TargetGID: -1}, 100073},
|
|
{HostEvent{Event: "file_write", PID: 4, Comm: "python3", Path: "/etc/systemd/system/evil.service", TargetUID: -1, TargetGID: -1}, 100069},
|
|
{HostEvent{Event: "chmod", PID: 5, Comm: "chmod", Path: "/etc/cron.d/evil", Mode: 0o777, TargetUID: -1, TargetGID: -1}, 100070},
|
|
{HostEvent{Event: "setuid", PID: 6, Comm: "python3", TargetUID: 0, TargetGID: -1}, 100071},
|
|
{HostEvent{Event: "setgid", PID: 7, Comm: "python3", TargetUID: -1, TargetGID: 0}, 100072},
|
|
{HostEvent{Event: "accept", PID: 8, Comm: "python3", PeerIP: "8.8.8.8", LocalPort: 4444, TargetUID: -1, TargetGID: -1}, 100076},
|
|
{HostEvent{Event: "capset", PID: 9, Comm: "python3", Capabilities: []string{"cap_sys_admin"}, TargetUID: -1, TargetGID: -1}, 100077},
|
|
{HostEvent{Event: "module_load", PID: 10, Comm: "insmod", Path: "/tmp/evil.ko", TargetUID: -1, TargetGID: -1}, 100078},
|
|
}
|
|
for _, test := range tests {
|
|
found := false
|
|
for _, alert := range engine.Match(test.event) {
|
|
if alert.SID == test.sid {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("sid %d did not match %#v", test.sid, test.event)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHostInterpreterCommsReturnsCopy(t *testing.T) {
|
|
first := HostInterpreterComms()
|
|
if len(first) == 0 {
|
|
t.Fatal("interpreter list must not be empty")
|
|
}
|
|
original := first[0]
|
|
first[0] = "mutated"
|
|
if HostInterpreterComms()[0] != original {
|
|
t.Fatal("caller mutated shared interpreter list")
|
|
}
|
|
}
|
|
|
|
func TestHostRulesRejectCommonOrPrivateNetworkShapes(t *testing.T) {
|
|
engine := DefaultHostRuleEngine()
|
|
for _, event := range []HostEvent{
|
|
{Event: "tcp_connect", Comm: "python3", PeerIP: "8.8.8.8", PeerPort: 443},
|
|
{Event: "tcp_connect", Comm: "python3", PeerIP: "10.0.0.2", PeerPort: 4444},
|
|
{Event: "listen", Comm: "python3", LocalPort: 80},
|
|
} {
|
|
if alerts := engine.Match(event); len(alerts) != 0 {
|
|
t.Fatalf("benign network shape matched: %#v", alerts)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHostAlertContract(t *testing.T) {
|
|
event := HostEvent{
|
|
Event: "chmod", PID: 42, PPID: 1, UID: 0, Comm: "chmod",
|
|
Path: "/etc/cron.d/evil", TargetUID: -1, TargetGID: -1, Mode: 0o777,
|
|
}
|
|
alert := DefaultHostRuleEngine().Match(event)[0]
|
|
if alert.Key != "host:100070:chmod:42::0::0:/etc/cron.d/evil:-1:-1:511::" ||
|
|
!strings.Contains(alert.Detail, "mode=0o777 capabilities=- module=-") {
|
|
t.Fatalf("unexpected host alert: %#v", alert)
|
|
}
|
|
}
|