141 lines
5.4 KiB
Go
141 lines
5.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMonitoringRulesValidation(t *testing.T) {
|
|
rules, err := validateMonitoringRules([]MonitoringRule{{IP: "2001:db8:0:0::1", Domain: "EXAMPLE.org", PathPrefix: "/health"}})
|
|
if err != nil || rules[0].IP != "2001:db8::1" || rules[0].Domain != "example.org" {
|
|
t.Fatalf("normalization: %+v %v", rules, err)
|
|
}
|
|
for _, r := range []MonitoringRule{{IP: "192.168.0.0/24"}, {IP: "8.8.8.8", Domain: "https://example.org"}, {IP: "8.8.8.8", PathPrefix: "health"}} {
|
|
if _, err := validateMonitoringRules([]MonitoringRule{r}); err == nil {
|
|
t.Fatalf("bad rule accepted: %+v", r)
|
|
}
|
|
}
|
|
if _, err := validateMonitoringRules(make([]MonitoringRule, 33)); err == nil {
|
|
t.Fatal("unbounded rules")
|
|
}
|
|
}
|
|
func TestMonitoringFiltersKeepHistoryAndSecurityEvents(t *testing.T) {
|
|
s, a := trafficFixture(t)
|
|
rules := []MonitoringRule{{IP: "192.168.0.10"}, {IP: "8.8.8.8", Domain: "example.org", PathPrefix: "/health"}}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID, MonitoringRules: rules})
|
|
now := time.Now().Unix()
|
|
batch := TrafficBatch{Events: []TrafficEvent{
|
|
{ID: "probe", Time: now, Kind: "request", IP: "192.168.0.10", Domain: "other.org", Path: "/"},
|
|
{ID: "health", Time: now, Kind: "request", IP: "8.8.8.8", Domain: "EXAMPLE.org", Path: "/health/live"},
|
|
{ID: "visitor", Time: now, Kind: "request", IP: "8.8.8.8", Domain: "example.org", Path: "/login"},
|
|
{ID: "other-domain", Time: now, Kind: "request", IP: "8.8.8.8", Domain: "other.org", Path: "/health"},
|
|
{ID: "alert", Time: now, Kind: "alert", IP: "192.168.0.10"},
|
|
{ID: "ban", Time: now, Kind: "decision", IP: "192.168.0.10"},
|
|
}}
|
|
if err := s.SaveTraffic(a.ID, batch); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
all, err := s.TrafficView("", "", false)
|
|
if err != nil || all.Requests != 4 {
|
|
t.Fatalf("all: %+v %v", all, err)
|
|
}
|
|
tagged := 0
|
|
for _, e := range all.Events {
|
|
if e.Monitoring {
|
|
tagged++
|
|
}
|
|
}
|
|
if tagged != 2 {
|
|
t.Fatalf("tags %d", tagged)
|
|
}
|
|
filtered, err := s.TrafficView("", "", false, true)
|
|
if err != nil || filtered.Requests != 2 || filtered.Alerts != 1 || filtered.Decisions != 1 || len(filtered.Events) != 4 {
|
|
t.Fatalf("filter: %+v %v", filtered, err)
|
|
}
|
|
if len(filtered.Series) != 1 || filtered.Series[0].Count != 2 {
|
|
t.Fatalf("graph leaks probes: %+v", filtered.Series)
|
|
}
|
|
var count int
|
|
s.db.QueryRow("SELECT COUNT(*) FROM traffic_events").Scan(&count)
|
|
if count != 6 {
|
|
t.Fatal("history deleted")
|
|
}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID, MonitoringRules: []MonitoringRule{}})
|
|
filtered, err = s.TrafficView("", "", false, true)
|
|
if err != nil || filtered.Requests != 4 {
|
|
t.Fatal("cleared rules not applied retroactively")
|
|
}
|
|
}
|
|
func TestMonitoringPrefixIsLiteralAndDefaultCanBeCleared(t *testing.T) {
|
|
s, a := trafficFixture(t)
|
|
if len(s.TrafficSettings().MonitoringRules) != 1 {
|
|
t.Fatal("missing suggested rule")
|
|
}
|
|
rules := []MonitoringRule{{IP: "8.8.8.8", PathPrefix: "/health_%"}}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID, MonitoringRules: rules})
|
|
s.SaveTraffic(a.ID, TrafficBatch{Events: []TrafficEvent{{ID: "x", Time: time.Now().Unix(), Kind: "request", IP: "8.8.8.8", Path: "/health_ok"}}})
|
|
v, err := s.TrafficView("", "", false, true)
|
|
if err != nil || v.Requests != 1 {
|
|
t.Fatal("prefix used as wildcard")
|
|
}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID})
|
|
if len(s.TrafficSettings().MonitoringRules) != 0 {
|
|
t.Fatal("cannot clear default")
|
|
}
|
|
}
|
|
func TestNetworkMapAndMonitoringSourceOnly(t *testing.T) {
|
|
s, a := trafficFixture(t)
|
|
rules := []MonitoringRule{{IP: "192.168.0.10"}, {IP: "8.8.8.8", Domain: "example.org"}}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, NetworkEnabled: true, AgentID: a.ID, MonitoringRules: rules})
|
|
incoming, _ := parseNetworkFlow(networkNATLine)
|
|
probe := incoming
|
|
probe.Source = "192.168.0.10"
|
|
outgoing := incoming
|
|
outgoing.Source = "192.168.0.12"
|
|
outgoing.Destination = "1.1.1.1"
|
|
outgoing.Protocol = "udp"
|
|
local := incoming
|
|
local.Source = "192.168.0.15"
|
|
local.Destination = "192.168.0.10"
|
|
local.Protocol = "icmpv6"
|
|
s.SaveNetwork(a.ID, NetworkSnapshot{At: time.Now().Unix(), Flows: []NetworkFlow{incoming, probe, outgoing, local}})
|
|
snapshot, err := s.ReadNetworkSnapshot(a.ID, rules, true)
|
|
if err != nil || len(snapshot.Flows) != 3 || snapshot.MonitoringHidden != 1 {
|
|
t.Fatalf("source-only: %+v %v", snapshot, err)
|
|
}
|
|
mapped := makeNetworkMap(snapshot, false)
|
|
if mapped.TCP != 1 || mapped.UDP != 1 || mapped.ICMP != 1 || mapped.Unmapped != 1 || len(mapped.Points) != 2 {
|
|
t.Fatalf("map: %+v", mapped)
|
|
}
|
|
for _, p := range mapped.Points {
|
|
if p.Protocol == "udp" && p.Direction != "out" {
|
|
t.Fatal("outbound direction lost")
|
|
}
|
|
if p.Protocol == "tcp" && p.Direction != "in" {
|
|
t.Fatal("inbound direction lost")
|
|
}
|
|
}
|
|
mapped = makeNetworkMap(snapshot, true)
|
|
if mapped.ICMP != 0 {
|
|
t.Fatal("LAN flow leaked")
|
|
}
|
|
view, err := s.TrafficView("example.org", "alert", false, true)
|
|
if err != nil || view.Network == nil || view.Network.TCP != 1 {
|
|
t.Fatal("HTTP filters incorrectly hide network layer")
|
|
}
|
|
encoded, _ := json.Marshal(view)
|
|
if len(encoded) == 0 {
|
|
t.Fatal("map not serializable")
|
|
}
|
|
s.SaveTrafficSettings(TrafficSettings{Enabled: true, AgentID: a.ID})
|
|
view, _ = s.TrafficView("", "", false)
|
|
if view.Network != nil {
|
|
t.Fatal("disabled network leaked")
|
|
}
|
|
v6 := NetworkFlow{Source: "2001:db8:0:0::1"}
|
|
if !isMonitoringFlow(v6, []MonitoringRule{{IP: "2001:db8::1"}}) {
|
|
t.Fatal("IPv6 representation mismatch")
|
|
}
|
|
}
|