ce7cb702218693d08ea6d83464d2457c9250027c

This commit is contained in:
ms2k 2026-11-12 20:46:28 +09:00
commit 34c5f8963c

View file

@ -0,0 +1,155 @@
package main
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func TestWriteJSONFileCreatesParentDirectory(t *testing.T) {
path := filepath.Join(t.TempDir(), "workflow", "validation-summary.json")
if err := writeJSONFile(path, map[string]bool{"valid": true}); err != nil {
t.Fatal(err)
}
contents, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if string(contents) != "{\"valid\":true}\n" {
t.Fatalf("unexpected contents %q", contents)
}
}
func validBundle(now time.Time) bundle {
return bundle{
SchemaVersion: 1,
GeneratedAt: now.Add(-time.Minute),
ExpiresAt: now.Add(24 * time.Hour),
AdmittedIPv4: []addressRange{{Start: "192.0.2.0", End: "192.0.2.255"}, {Start: "198.51.100.1", End: "198.51.100.1"}},
ExceptionIPv4: []addressRange{{Start: "203.0.113.0", End: "203.0.113.15"}},
AdmittedIntervalCount: 2,
AdmittedAddressCount: 257,
ExceptionIntervalCount: 1,
ExceptionAddressCount: 16,
}
}
func TestValidateAndRender(t *testing.T) {
now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC)
validated, err := validateBundle(now, validBundle(now))
if err != nil {
t.Fatal(err)
}
var output bytes.Buffer
if err := render(&output, validated, "ens3"); err != nil {
t.Fatal(err)
}
for _, expected := range []string{
"delete table inet oci_l4_relay_web_allow",
"192.0.2.0-192.0.2.255",
"198.51.100.1",
"203.0.113.0-203.0.113.15",
"iifname \"ens3\" tcp dport { 80, 443, 465, 993 }",
"counter name web_allow_denied drop",
} {
if !strings.Contains(output.String(), expected) {
t.Errorf("rendered output does not contain %q", expected)
}
}
if strings.Contains(output.String(), "587") {
t.Fatal("rendered output admits cluster-local submission")
}
}
func TestValidationFailures(t *testing.T) {
now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC)
tests := map[string]func(*bundle){
"schema": func(value *bundle) { value.SchemaVersion = 2 },
"future": func(value *bundle) { value.GeneratedAt = now.Add(10 * time.Minute) },
"expiry": func(value *bundle) { value.ExpiresAt = now.Add(30 * time.Minute) },
"lifetime": func(value *bundle) { value.ExpiresAt = now.Add(8 * 24 * time.Hour) },
"overlap": func(value *bundle) { value.AdmittedIPv4[1] = addressRange{Start: "192.0.2.128", End: "192.0.2.200"} },
"reversed": func(value *bundle) { value.AdmittedIPv4[0] = addressRange{Start: "192.0.2.10", End: "192.0.2.1"} },
"ipv6": func(value *bundle) { value.AdmittedIPv4[0] = addressRange{Start: "2001:db8::1", End: "2001:db8::2"} },
"noncanonical": func(value *bundle) { value.AdmittedIPv4[0].Start = "192.000.002.000" },
"address count": func(value *bundle) { value.AdmittedAddressCount++ },
}
for name, mutate := range tests {
t.Run(name, func(t *testing.T) {
value := validBundle(now)
mutate(&value)
if _, err := validateBundle(now, value); err == nil {
t.Fatal("expected validation failure")
}
})
}
}
func TestRejectsUnsafeInterface(t *testing.T) {
now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC)
validated, err := validateBundle(now, validBundle(now))
if err != nil {
t.Fatal(err)
}
if err := render(&bytes.Buffer{}, validated, "ens3\nflush ruleset"); err == nil {
t.Fatal("expected unsafe interface rejection")
}
}
func TestRenderFailClosed(t *testing.T) {
var output bytes.Buffer
if err := renderFailClosed(&output, "ens3"); err != nil {
t.Fatal(err)
}
if !strings.Contains(output.String(), "iifname \"ens3\" tcp dport { 80, 443, 465, 993 } counter name web_allow_denied drop") {
t.Fatal("fail-closed ruleset does not drop every positive-policy surface")
}
}
func TestPolicyBounds(t *testing.T) {
now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC)
validated, err := validateBundle(now, validBundle(now))
if err != nil {
t.Fatal(err)
}
if err := validatePolicy(updateConfig{minimumAddresses: 257, maximumAddresses: 257, minimumIntervals: 2, maximumExceptionAddresses: 16, maximumExceptionIntervals: 1}, validated); err != nil {
t.Fatal(err)
}
for name, configuration := range map[string]updateConfig{
"minimum addresses": {minimumAddresses: 258, maximumAddresses: 1 << 32, minimumIntervals: 1, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: maxExceptions},
"maximum addresses": {minimumAddresses: 1, maximumAddresses: 256, minimumIntervals: 1, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: maxExceptions},
"minimum intervals": {minimumAddresses: 1, maximumAddresses: 1 << 32, minimumIntervals: 3, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: maxExceptions},
"maximum exception addresses": {minimumAddresses: 1, maximumAddresses: 1 << 32, minimumIntervals: 1, maximumExceptionAddresses: 15, maximumExceptionIntervals: maxExceptions},
"maximum exception intervals": {minimumAddresses: 1, maximumAddresses: 1 << 32, minimumIntervals: 1, maximumExceptionAddresses: 1 << 32, maximumExceptionIntervals: 0},
} {
t.Run(name, func(t *testing.T) {
if err := validatePolicy(configuration, validated); err == nil {
t.Fatal("expected policy-bound failure")
}
})
}
}
func TestAddStaticExceptions(t *testing.T) {
now := time.Date(2026, 7, 12, 0, 0, 0, 0, time.UTC)
validated, err := validateBundle(now, validBundle(now))
if err != nil {
t.Fatal(err)
}
validated, err = addStaticExceptions(validated, []string{"203.0.113.16", "198.51.100.50"})
if err != nil {
t.Fatal(err)
}
var output bytes.Buffer
if err := render(&output, validated, "ens3"); err != nil {
t.Fatal(err)
}
for _, expected := range []string{"198.51.100.50", "203.0.113.0-203.0.113.16"} {
if !strings.Contains(output.String(), expected) {
t.Errorf("rendered output does not contain %q", expected)
}
}
}