package main import ( "bytes" "encoding/json" "os" "path/filepath" "strings" "testing" "time" ) func policy() activationConfig { return activationConfig{ publicInterface: "ens3", minimumObservations: 20, maximumAddresses: 4096, maximumIntervals: 1024, maximumLifetime: 24 * time.Hour, maximumObservationWindow: 10 * time.Minute, } } func validBundle(now time.Time) bundle { return bundle{ SchemaVersion: 1, GeneratedAt: now, ExpiresAt: now.Add(24 * time.Hour), DeniedIPv4: []denyEntry{{ Start: "192.0.2.1", End: "192.0.2.1", Reason: "connection-rate", ObservationStarted: now.Add(-10 * time.Minute), ObservationEnded: now, ObservationCount: 20, ExpiresAt: now.Add(24 * time.Hour), }}, DeniedIntervalCount: 1, DeniedAddressCount: 1, } } func TestWriteAndReadEmptyBundle(t *testing.T) { now := time.Now().UTC().Truncate(time.Second) path := filepath.Join(t.TempDir(), "empty", "current.json.gz") document := bundle{ SchemaVersion: 1, GeneratedAt: now, ExpiresAt: now.Add(24 * time.Hour), DeniedIPv4: []denyEntry{}, } if err := writeBundle(path, document); err != nil { t.Fatal(err) } validated, err := readBundle(now, policy(), path) if err != nil { t.Fatal(err) } if validated.DeniedIntervalCount != 0 || validated.DeniedAddressCount != 0 || validated.DeniedIPv4 == nil { t.Fatal("empty bundle did not retain an explicit empty deny list") } } func TestRemovedIntervalCount(t *testing.T) { now := time.Date(2026, 7, 26, 0, 0, 0, 0, time.UTC) previous, err := validateBundle(now, policy(), validBundle(now)) if err != nil { t.Fatal(err) } currentDocument := validBundle(now) currentDocument.DeniedIPv4 = []denyEntry{} currentDocument.DeniedIntervalCount = 0 currentDocument.DeniedAddressCount = 0 current, err := validateBundle(now, policy(), currentDocument) if err != nil { t.Fatal(err) } if removedIntervalCount(previous, current) != 1 { t.Fatal("omitted range was not recorded as removed") } } func TestWriteJSONFileCreatesParentDirectory(t *testing.T) { path := filepath.Join(t.TempDir(), "audit", "summary.json") if err := writeJSONFile(path, map[string]int{"removed_interval_count": 1}); err != nil { t.Fatal(err) } contents, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if string(contents) != "{\"removed_interval_count\":1}\n" { t.Fatalf("unexpected summary %q", contents) } } func TestWriteActivePolicyStatePreservesRemoteCurrentActivation(t *testing.T) { now := time.Date(2026, 7, 29, 0, 0, 0, 0, time.UTC) directory := t.TempDir() validated, err := validateBundle(now, policy(), validBundle(now)) if err != nil { t.Fatal(err) } if err := writeActivePolicyState(directory, now, "current", &validated); err != nil { t.Fatal(err) } cacheActivation := now.Add(time.Minute) if err := writeActivePolicyState(directory, cacheActivation, "cache", &validated); err != nil { t.Fatal(err) } state, err := readActivePolicyState(filepath.Join(directory, activePolicyStateFile)) if err != nil { t.Fatal(err) } if state.Source != "cache" || !state.ActivatedAt.Equal(cacheActivation) || !state.LastRemoteCurrentAt.Equal(now) { t.Fatalf("unexpected active state %#v", state) } if !state.BundleGeneratedAt.Equal(validated.GeneratedAt) || !state.BundleExpiresAt.Equal(validated.ExpiresAt) { t.Fatalf("unexpected bundle state %#v", state) } } func TestWriteActivePolicyStateRecordsFailOpen(t *testing.T) { now := time.Date(2026, 7, 29, 0, 0, 0, 0, time.UTC) directory := t.TempDir() if err := writeActivePolicyState(directory, now, "fail-open", nil); err != nil { t.Fatal(err) } state, err := readActivePolicyState(filepath.Join(directory, activePolicyStateFile)) if err != nil { t.Fatal(err) } if state.Source != "fail-open" || !state.BundleGeneratedAt.IsZero() || !state.BundleExpiresAt.IsZero() { t.Fatalf("unexpected fail-open state %#v", state) } } func TestFailOpenRecordsFinalStateAfterRulesLoad(t *testing.T) { now := time.Date(2026, 7, 29, 0, 0, 0, 0, time.UTC) directory := t.TempDir() observedPath := filepath.Join(directory, "observed-state.json") nftPath := filepath.Join(directory, "nft") script := "#!/bin/sh\nif [ \"$1\" = \"--file\" ]; then cp \"$STATE_PATH\" \"$OBSERVED_PATH\"; exit \"${NFT_LOAD_EXIT:-0}\"; fi\nexit 0\n" if err := os.WriteFile(nftPath, []byte(script), 0700); err != nil { t.Fatal(err) } t.Setenv("STATE_PATH", filepath.Join(directory, activePolicyStateFile)) t.Setenv("OBSERVED_PATH", observedPath) configuration := policy() configuration.stateDirectory = directory configuration.nft = nftPath if err := failOpen(now, configuration); err != nil { t.Fatal(err) } observed, err := readActivePolicyState(observedPath) if err != nil { t.Fatal(err) } if observed.Source != "transition" { t.Fatalf("rules loaded after source %q was reported", observed.Source) } final, err := readActivePolicyState(filepath.Join(directory, activePolicyStateFile)) if err != nil { t.Fatal(err) } if final.Source != "fail-open" { t.Fatalf("unexpected final source %q", final.Source) } } func TestActivationRecordsFinalStateAfterRulesLoad(t *testing.T) { now := time.Date(2026, 7, 29, 0, 0, 0, 0, time.UTC) directory := t.TempDir() bundlePath := filepath.Join(directory, "current.json.gz") if err := writeBundle(bundlePath, validBundle(now)); err != nil { t.Fatal(err) } observedPath := filepath.Join(directory, "observed-state.json") nftPath := filepath.Join(directory, "nft") script := "#!/bin/sh\nif [ \"$1\" = \"--file\" ]; then cp \"$STATE_PATH\" \"$OBSERVED_PATH\"; fi\nexit 0\n" if err := os.WriteFile(nftPath, []byte(script), 0700); err != nil { t.Fatal(err) } t.Setenv("STATE_PATH", filepath.Join(directory, activePolicyStateFile)) t.Setenv("OBSERVED_PATH", observedPath) configuration := policy() configuration.stateDirectory = directory configuration.nft = nftPath if err := activate(now, configuration, bundlePath, true, "current"); err != nil { t.Fatal(err) } observed, err := readActivePolicyState(observedPath) if err != nil { t.Fatal(err) } if observed.Source != "transition" { t.Fatalf("rules loaded after source %q was reported", observed.Source) } final, err := readActivePolicyState(filepath.Join(directory, activePolicyStateFile)) if err != nil { t.Fatal(err) } if final.Source != "current" { t.Fatalf("unexpected final source %q", final.Source) } } func TestFailOpenLoadFailureRemainsTransitional(t *testing.T) { now := time.Date(2026, 7, 29, 0, 0, 0, 0, time.UTC) directory := t.TempDir() nftPath := filepath.Join(directory, "nft") script := "#!/bin/sh\nif [ \"$1\" = \"--file\" ]; then exit 1; fi\nexit 0\n" if err := os.WriteFile(nftPath, []byte(script), 0700); err != nil { t.Fatal(err) } configuration := policy() configuration.stateDirectory = directory configuration.nft = nftPath if err := failOpen(now, configuration); err == nil { t.Fatal("expected nftables load failure") } state, err := readActivePolicyState(filepath.Join(directory, activePolicyStateFile)) if err != nil { t.Fatal(err) } if state.Source != "transition" { t.Fatalf("failed rules load reported source %q", state.Source) } } func TestValidateAndRender(t *testing.T) { now := time.Date(2026, 7, 26, 0, 0, 0, 0, time.UTC) validated, err := validateBundle(now, policy(), validBundle(now)) if err != nil { t.Fatal(err) } var output bytes.Buffer if err := render(&output, now, validated, "ens3"); err != nil { t.Fatal(err) } for _, expected := range []string{ "table inet oci_l4_relay_smtp_deny", "flags interval, timeout", "192.0.2.1 timeout 86400s", `iifname "ens3" tcp dport 25`, "counter name smtp_denied drop", } { if !strings.Contains(output.String(), expected) { t.Errorf("rendered ruleset does not contain %q", expected) } } for _, excluded := range []string{"dport 465", "dport 587", "dport 993", "dport { 80, 443 }"} { if strings.Contains(output.String(), excluded) { t.Errorf("rendered SMTP deny ruleset contains %q", excluded) } } } func TestEmptyBundleIsValidAndFailOpenIsEmpty(t *testing.T) { now := time.Date(2026, 7, 26, 0, 0, 0, 0, time.UTC) document := validBundle(now) document.DeniedIPv4 = nil document.DeniedIntervalCount = 0 document.DeniedAddressCount = 0 validated, err := validateBundle(now, policy(), document) if err != nil { t.Fatal(err) } var rendered bytes.Buffer if err := render(&rendered, now, validated, "ens3"); err != nil { t.Fatal(err) } var failOpen bytes.Buffer if err := renderFailOpen(&failOpen, "ens3"); err != nil { t.Fatal(err) } for name, value := range map[string]string{"empty bundle": rendered.String(), "fail open": failOpen.String()} { if strings.Contains(value, "elements =") { t.Errorf("%s unexpectedly contains deny elements", name) } if !strings.Contains(value, "ip saddr @denied_v4") { t.Errorf("%s does not retain the empty deny lookup", name) } } } func TestExpiredEntryDecaysWithoutInvalidatingBundle(t *testing.T) { now := time.Date(2026, 7, 26, 0, 0, 0, 0, time.UTC) document := validBundle(now) document.DeniedIPv4[0].ObservationEnded = now.Add(-2 * time.Minute) document.DeniedIPv4[0].ExpiresAt = now.Add(-time.Minute) validated, err := validateBundle(now, policy(), document) if err != nil { t.Fatal(err) } var output bytes.Buffer if err := render(&output, now, validated, "ens3"); err != nil { t.Fatal(err) } if strings.Contains(output.String(), "192.0.2.1") { t.Fatal("expired entry remained in the rendered deny set") } } func TestPolicyBoundaries(t *testing.T) { now := time.Date(2026, 7, 26, 0, 0, 0, 0, time.UTC) maximum := validBundle(now) maximum.DeniedIPv4[0].Start = "192.0.2.0" maximum.DeniedIPv4[0].End = "192.0.17.255" maximum.DeniedAddressCount = 4096 if _, err := validateBundle(now, policy(), maximum); err != nil { t.Fatal(err) } tests := map[string]func(*bundle){ "observation threshold": func(value *bundle) { value.DeniedIPv4[0].ObservationCount = 19 }, "address cap": func(value *bundle) { value.DeniedIPv4[0].Start = "192.0.2.0" value.DeniedIPv4[0].End = "192.0.18.0" value.DeniedAddressCount = 4097 }, "lifetime": func(value *bundle) { value.ExpiresAt = now.Add(24*time.Hour + time.Second) value.DeniedIPv4[0].ExpiresAt = value.ExpiresAt }, "observation window": func(value *bundle) { value.DeniedIPv4[0].ObservationStarted = now.Add(-10*time.Minute - time.Second) }, } for name, mutate := range tests { t.Run(name, func(t *testing.T) { value := validBundle(now) mutate(&value) if _, err := validateBundle(now, policy(), value); err == nil { t.Fatal("expected policy-bound failure") } }) } intervals := validBundle(now) intervals.DeniedIPv4 = make([]denyEntry, 1025) for index := range intervals.DeniedIPv4 { entry := validBundle(now).DeniedIPv4[0] entry.Start = ipv4String(uint32(index * 2)) entry.End = entry.Start intervals.DeniedIPv4[index] = entry } intervals.DeniedIntervalCount = len(intervals.DeniedIPv4) intervals.DeniedAddressCount = uint64(len(intervals.DeniedIPv4)) if _, err := validateBundle(now, policy(), intervals); err == nil { t.Fatal("expected interval-cap failure") } } func TestValidationRejectsNonTransportOrMalformedEvidence(t *testing.T) { now := time.Date(2026, 7, 26, 0, 0, 0, 0, time.UTC) tests := map[string]func(*bundle){ "schema": func(value *bundle) { value.SchemaVersion = 2 }, "expired": func(value *bundle) { value.ExpiresAt = now }, "ipv6": func(value *bundle) { value.DeniedIPv4[0].Start = "2001:db8::1" }, "reason": func(value *bundle) { value.DeniedIPv4[0].Reason = "spam-score" }, "future window": func(value *bundle) { value.DeniedIPv4[0].ObservationEnded = now.Add(6 * time.Minute) }, } for name, mutate := range tests { t.Run(name, func(t *testing.T) { value := validBundle(now) mutate(&value) if _, err := validateBundle(now, policy(), value); err == nil { t.Fatal("expected validation failure") } }) } } func TestReadBundleRejectsContentEvidence(t *testing.T) { now := time.Now().UTC().Truncate(time.Second) contents, err := json.Marshal(validBundle(now)) if err != nil { t.Fatal(err) } contents = bytes.Replace(contents, []byte(`"reason":"connection-rate"`), []byte(`"reason":"connection-rate","spam_score":9`), 1) path := filepath.Join(t.TempDir(), "bundle.json") if err := os.WriteFile(path, contents, 0600); err != nil { t.Fatal(err) } if _, err := readBundle(now, policy(), path); err == nil { t.Fatal("expected content evidence to be rejected") } } func TestRejectsUnsafeInterface(t *testing.T) { if err := renderFailOpen(&bytes.Buffer{}, "ens3\nflush ruleset"); err == nil { t.Fatal("expected unsafe interface rejection") } }