Ctrl K
ComunidadGuías…

pkg/audit/traefik_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package audit

import (
	"testing"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func traefikRoute(group, kind, ns, name string, svcRefs, mwRefs []map[string]any) *unstructured.Unstructured {
	route := map[string]any{}
	if svcRefs != nil {
		route["services"] = toIfaceSlice(svcRefs)
	}
	if mwRefs != nil {
		route["middlewares"] = toIfaceSlice(mwRefs)
	}
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": group + "/v1alpha1",
		"kind":       kind,
		"metadata":   map[string]any{"name": name, "namespace": ns, "uid": group + "/" + ns + "/" + name},
		"spec":       map[string]any{"routes": []any{route}},
	}}
}

func traefikObj(group, kind, ns, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": group + "/v1alpha1",
		"kind":       kind,
		"metadata":   map[string]any{"name": name, "namespace": ns},
	}}
}

func toIfaceSlice(maps []map[string]any) []any {
	out := make([]any, len(maps))
	for i, m := range maps {
		out[i] = m
	}
	return out
}

func svc(ns, name string) *corev1.Service {
	return &corev1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: name}}
}

// authoritative builds the group\x00Kind set the runner would produce when those
// target kinds are served by a synced cluster-wide informer.
func authoritative(keys ...string) map[string]bool {
	m := map[string]bool{}
	for _, k := range keys {
		m[k] = true
	}
	return m
}

func findingIDs(findings []Finding) map[string]int {
	m := map[string]int{}
	for _, f := range findings {
		m[f.CheckID]++
	}
	return m
}

func TestCheckTraefikDanglingRefs(t *testing.T) {
	const g = "traefik.io"

	t.Run("flags missing service and middleware, accepts present ones", func(t *testing.T) {
		input := &CheckInput{
			AllServices:               []*corev1.Service{svc("app", "present-svc")},
			Middlewares:               []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "present-mw")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRoute", "app", "r1",
					[]map[string]any{{"name": "present-svc"}, {"name": "missing-svc"}},
					[]map[string]any{{"name": "present-mw"}, {"name": "missing-mw"}},
				),
			},
		}
		ids := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))
		if ids["traefikRouteMissingService"] != 1 {
			t.Errorf("want 1 missing-service, got %d", ids["traefikRouteMissingService"])
		}
		if ids["traefikRouteMissingMiddleware"] != 1 {
			t.Errorf("want 1 missing-middleware, got %d", ids["traefikRouteMissingMiddleware"])
		}
	})

	t.Run("all refs present → no findings", func(t *testing.T) {
		input := &CheckInput{
			AllServices:               []*corev1.Service{svc("app", "s")},
			Middlewares:               []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "mw")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRoute", "app", "r",
					[]map[string]any{{"name": "s"}}, []map[string]any{{"name": "mw"}}),
			},
		}
		if got := checkTraefikDanglingRefs(newEvalTracker(), input); len(got) != 0 {
			t.Errorf("want no findings, got %v", got)
		}
	})

	t.Run("cross-namespace middleware ref RESOLVES against cluster-wide inventory", func(t *testing.T) {
		// Route in ns "app" references a Middleware explicitly in ns "shared".
		// The middleware exists there; since the inventory is cluster-wide and
		// authoritative, this must NOT be flagged.
		input := &CheckInput{
			AllServices:               []*corev1.Service{},
			Middlewares:               []*unstructured.Unstructured{traefikObj(g, "Middleware", "shared", "auth")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRoute", "app", "r", nil,
					[]map[string]any{{"name": "auth", "namespace": "shared"}}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 0 {
			t.Errorf("cross-namespace ref to an existing middleware must not be flagged, got %d", n)
		}
	})

	t.Run("not authoritative → skip (no false positive)", func(t *testing.T) {
		// Middleware kind NOT in the authoritative set (e.g. namespace-scoped
		// fallback). Even though the referenced mw is absent, do not assert.
		input := &CheckInput{
			AllServices:               []*corev1.Service{},
			Middlewares:               []*unstructured.Unstructured{},
			TraefikAuthoritativeKinds: authoritative(), // empty → nothing authoritative
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRoute", "app", "r", nil, []map[string]any{{"name": "ghost"}}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 0 {
			t.Errorf("non-authoritative middleware kind must not be asserted, got %d", n)
		}
	})

	t.Run("cross-group middleware does not satisfy the reference", func(t *testing.T) {
		input := &CheckInput{
			AllServices:               []*corev1.Service{},
			Middlewares:               []*unstructured.Unstructured{traefikObj("traefik.containo.us", "Middleware", "app", "mw")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRoute", "app", "r", nil, []map[string]any{{"name": "mw"}}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 1 {
			t.Errorf("traefik.io router must not be satisfied by a traefik.containo.us Middleware, got %d", n)
		}
	})

	t.Run("IngressRouteTCP resolves against MiddlewareTCP, per-kind independent", func(t *testing.T) {
		// Only Middleware (not MiddlewareTCP) is authoritative + present. A TCP
		// router's MiddlewareTCP ref must be skipped (its kind isn't authoritative),
		// NOT fabricated as missing.
		input := &CheckInput{
			AllServices:               []*corev1.Service{},
			Middlewares:               []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "mw")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"), // NOT MiddlewareTCP
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRouteTCP", "app", "r", nil, []map[string]any{{"name": "mw"}}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 0 {
			t.Errorf("MiddlewareTCP kind not authoritative → must skip, got %d (regression: CR-3)", n)
		}

		// Now MiddlewareTCP IS authoritative and the ref is absent → flag.
		input.TraefikAuthoritativeKinds = authoritative(g + "\x00MiddlewareTCP")
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikRouteMissingMiddleware"]; n != 1 {
			t.Errorf("authoritative MiddlewareTCP with absent ref → want 1, got %d", n)
		}
	})

	t.Run("service check independent of middleware authority", func(t *testing.T) {
		// Middlewares not authoritative, but services are listed → service ref
		// still checked (per-kind independence; CR-5).
		input := &CheckInput{
			AllServices:               []*corev1.Service{}, // non-nil, empty → listed
			TraefikAuthoritativeKinds: authoritative(),
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRoute", "app", "r",
					[]map[string]any{{"name": "missing"}}, []map[string]any{{"name": "ghost"}}),
			},
		}
		ids := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))
		if ids["traefikRouteMissingService"] != 1 {
			t.Errorf("service ref should be checked independently, got %d", ids["traefikRouteMissingService"])
		}
		if ids["traefikRouteMissingMiddleware"] != 0 {
			t.Errorf("middleware not authoritative → skip, got %d", ids["traefikRouteMissingMiddleware"])
		}
	})

	t.Run("findings carry their exact group", func(t *testing.T) {
		input := &CheckInput{
			AllServices:               []*corev1.Service{},
			TraefikAuthoritativeKinds: authoritative(),
			IngressRoutes: []*unstructured.Unstructured{
				traefikRoute(g, "IngressRoute", "app", "r", []map[string]any{{"name": "missing"}}, nil),
			},
		}
		got := checkTraefikDanglingRefs(newEvalTracker(), input)
		if len(got) != 1 {
			t.Fatalf("want 1 finding, got %d", len(got))
		}
		if got[0].Group != g {
			t.Errorf("Traefik finding lost its group, got %q", got[0].Group)
		}
	})

	t.Run("no Traefik installed → no-op", func(t *testing.T) {
		if got := checkTraefikDanglingRefs(newEvalTracker(), &CheckInput{}); got != nil {
			t.Errorf("want nil, got %v", got)
		}
	})
}

func traefikChainMw(group, ns, name string, chainRefs []map[string]any) *unstructured.Unstructured {
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": group + "/v1alpha1",
		"kind":       "Middleware",
		"metadata":   map[string]any{"name": name, "namespace": ns, "uid": group + "/chain/" + ns + "/" + name},
		"spec":       map[string]any{"chain": map[string]any{"middlewares": toIfaceSlice(chainRefs)}},
	}}
}

func traefikErrorsMw(group, ns, name string, svcRef map[string]any) *unstructured.Unstructured {
	return &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": group + "/v1alpha1",
		"kind":       "Middleware",
		"metadata":   map[string]any{"name": name, "namespace": ns, "uid": group + "/errors/" + ns + "/" + name},
		"spec":       map[string]any{"errors": map[string]any{"service": svcRef}},
	}}
}

func TestCheckTraefikMiddlewareSubjectRefs(t *testing.T) {
	const g = "traefik.io"

	t.Run("chain flags missing middleware, accepts present", func(t *testing.T) {
		input := &CheckInput{
			Middlewares:               []*unstructured.Unstructured{traefikObj(g, "Middleware", "app", "present")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
			MiddlewareSubjects: []*unstructured.Unstructured{
				traefikChainMw(g, "app", "c", []map[string]any{{"name": "present"}, {"name": "missing"}}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikChainMissingMiddleware"]; n != 1 {
			t.Errorf("want 1 missing chain middleware, got %d", n)
		}
	})

	t.Run("chain cross-namespace ref resolves against cluster-wide inventory", func(t *testing.T) {
		input := &CheckInput{
			Middlewares:               []*unstructured.Unstructured{traefikObj(g, "Middleware", "shared", "auth")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00Middleware"),
			MiddlewareSubjects: []*unstructured.Unstructured{
				traefikChainMw(g, "app", "c", []map[string]any{{"name": "auth", "namespace": "shared"}}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikChainMissingMiddleware"]; n != 0 {
			t.Errorf("cross-namespace ref to an existing middleware must not be flagged, got %d", n)
		}
	})

	t.Run("chain not authoritative → skip (no false positive)", func(t *testing.T) {
		input := &CheckInput{
			TraefikAuthoritativeKinds: authoritative(), // nothing authoritative
			MiddlewareSubjects: []*unstructured.Unstructured{
				traefikChainMw(g, "app", "c", []map[string]any{{"name": "ghost"}}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikChainMissingMiddleware"]; n != 0 {
			t.Errorf("non-authoritative middleware kind must not be asserted, got %d", n)
		}
	})

	t.Run("errors flags missing service, accepts present", func(t *testing.T) {
		input := &CheckInput{
			AllServices: []*corev1.Service{svc("app", "present")},
			MiddlewareSubjects: []*unstructured.Unstructured{
				traefikErrorsMw(g, "app", "e-missing", map[string]any{"name": "missing"}),
				traefikErrorsMw(g, "app", "e-present", map[string]any{"name": "present"}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikErrorsMissingService"]; n != 1 {
			t.Errorf("want 1 missing errors service, got %d", n)
		}
	})

	t.Run("errors.service kind=TraefikService resolves against TraefikService inventory", func(t *testing.T) {
		input := &CheckInput{
			AllServices:               []*corev1.Service{}, // listed, empty
			TraefikServices:           []*unstructured.Unstructured{traefikObj(g, "TraefikService", "app", "weighted")},
			TraefikAuthoritativeKinds: authoritative(g + "\x00TraefikService"),
			MiddlewareSubjects: []*unstructured.Unstructured{
				traefikErrorsMw(g, "app", "e", map[string]any{"name": "weighted", "kind": "TraefikService"}),
			},
		}
		if n := findingIDs(checkTraefikDanglingRefs(newEvalTracker(), input))["traefikErrorsMissingService"]; n != 0 {
			t.Errorf("present TraefikService ref must not be flagged, got %d", n)
		}
	})

	t.Run("middleware-only namespace (no routes) still runs the checks", func(t *testing.T) {
		// Regression guard: the early-return must not bail when there are only
		// middleware subjects and zero IngressRoutes.
		input := &CheckInput{
			AllServices: []*corev1.Service{},
			MiddlewareSubjects: []*unstructured.Unstructured{
				traefikErrorsMw(g, "app", "e", map[string]any{"name": "missing"}),
			},
		}
		got := checkTraefikDanglingRefs(newEvalTracker(), input)
		if len(got) != 1 {
			t.Fatalf("want 1 finding from a middleware-only namespace, got %d", len(got))
		}
		if got[0].Kind != "Middleware" || got[0].Group != g {
			t.Errorf("finding must preserve the Middleware group, got kind=%q group=%q", got[0].Kind, got[0].Group)
		}
	})
}