// SPDX-License-Identifier: GPL-3.0-or-later package baseline import ( "encoding/json" "os" "path/filepath" "testing" "time" "codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/config" "codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/model" ) func TestManagerGraceAndDurableFirstSeenLifecycle(t *testing.T) { directory := t.TempDir() watched := filepath.Join(directory, "cron-entry") if err := os.WriteFile(watched, []byte("job"), 0o600); err != nil { t.Fatal(err) } start := time.Date(2026, 7, 20, 12, 0, 0, 0, time.UTC) now := start cfg := config.Default() cfg.LogDir = directory cfg.BaselineGrace = 10 * time.Second cfg.WatchPersistence = []string{watched} manager := New(cfg, func() []string { return []string{"/usr/bin/sudo"} }) manager.Now = func() time.Time { return now } p := 42 capture := func() (model.State, error) { return model.State{Sockets: []model.Socket{{ State: "LISTEN", Local: "[::]:22", Comm: "sshd", PID: &p, }}}, nil } if err := manager.Initialize(capture); err != nil { t.Fatal(err) } now = start.Add(5 * time.Second) state := model.State{} if err := manager.Prepare(&state, now); err != nil { t.Fatal(err) } if state.ListenerBaseline != nil || state.PersistSince != nil { t.Fatalf("baseline detectors armed during grace: %#v", state) } if err := manager.Commit(state, now); err != nil { t.Fatal(err) } now = start.Add(11 * time.Second) state = model.State{} if err := manager.Prepare(&state, now); err != nil { t.Fatal(err) } if len(state.ListenerBaseline) != 1 || state.ListenerBaseline[0] != "22/sshd" { t.Fatalf("unexpected listener baseline: %#v", state.ListenerBaseline) } if state.PersistSince == nil || *state.PersistSince != float64(start.Add(5*time.Second).Unix()) { t.Fatalf("unexpected persistence boundary: %#v", state.PersistSince) } state.FirstSeenPublicDestinations["bash"] = []string{"8.8.8.8:443"} state.FirstSeenListenerPorts["nc"] = []string{"31337"} if err := manager.Commit(state, now); err != nil { t.Fatal(err) } raw, err := os.ReadFile(filepath.Join(directory, "first-seen.json")) if err != nil { t.Fatal(err) } var stored firstSeenStore if err := json.Unmarshal(raw, &stored); err != nil { t.Fatal(err) } if !stored.Initialized || stored.PublicDestinations["bash"][0] != "8.8.8.8:443" { t.Fatalf("unexpected first-seen store: %#v", stored) } for _, name := range []string{"listener-baseline.json", "suid-baseline.json"} { if _, err := os.Stat(filepath.Join(directory, name)); err != nil { t.Fatalf("missing %s: %v", name, err) } } } func TestLoadFirstSeenFailsOpenOnCorruptStore(t *testing.T) { path := filepath.Join(t.TempDir(), "first-seen.json") if err := os.WriteFile(path, []byte("{"), 0o600); err != nil { t.Fatal(err) } store := loadFirstSeen(path) if store.Initialized || store.Schema != firstSeenSchema || store.PublicDestinations == nil || store.ListenerPorts == nil { t.Fatalf("unexpected fallback: %#v", store) } }