Ctrl K
ComunidadGuías…

pkg/audit/ignore_test.go

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

Archivo original ↗Preguntar a KiBot
package audit

import (
	"reflect"
	"testing"

	appsv1 "k8s.io/api/apps/v1"
	autoscalingv2 "k8s.io/api/autoscaling/v2"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

func annotated(d *appsv1.Deployment, value string) *appsv1.Deployment {
	d.Annotations = map[string]string{IgnoreChecksAnnotation: value}
	return d
}

func TestIgnoreAnnotation_SuppressesNamedCheckOnlyForThatResource(t *testing.T) {
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		Deployments: []*appsv1.Deployment{
			annotated(deploymentInNS("solo-ignored", "prod", 1, secureContainer("app", false)), "singleReplica"),
			deploymentInNS("solo-flagged", "prod", 1, secureContainer("app", false)),
		},
	}
	results := RunChecks(input)

	single := findingNames(results.Findings, "singleReplica")
	if single["solo-ignored"] {
		t.Error("singleReplica finding should be suppressed on annotated deployment")
	}
	if !single["solo-flagged"] {
		t.Error("singleReplica finding missing on unannotated deployment")
	}
	// Other checks on the annotated resource are unaffected.
	if !findingNames(results.Findings, "readinessProbeMissing")["solo-ignored"] {
		t.Error("readinessProbeMissing should still fire on the annotated deployment")
	}
}

func TestIgnoreAnnotation_MultipleChecksCommaSeparated(t *testing.T) {
	d := deploymentInNS("hostnet", "prod", 1, secureContainer("app", false))
	d.Spec.Template.Spec.HostNetwork = true
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		Deployments:              []*appsv1.Deployment{annotated(d, "singleReplica, hostNetwork")},
	}
	results := RunChecks(input)

	for _, id := range []string{"singleReplica", "hostNetwork"} {
		if findingNames(results.Findings, id)["hostnet"] {
			t.Errorf("%s finding should be suppressed by comma-separated annotation", id)
		}
	}
	if !findingNames(results.Findings, "readinessProbeMissing")["hostnet"] {
		t.Error("readinessProbeMissing should still fire on the annotated deployment")
	}
}

func TestIgnoreAnnotation_UnknownCheckNameIsHarmless(t *testing.T) {
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		Deployments: []*appsv1.Deployment{
			annotated(deploymentInNS("solo", "prod", 1, secureContainer("app", true)), "notARealCheck"),
		},
	}
	results := RunChecks(input)

	if !findingNames(results.Findings, "singleReplica")["solo"] {
		t.Error("unknown check name in annotation must not suppress anything")
	}
}

func TestIgnoreAnnotation_ExcludedFromCounts(t *testing.T) {
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		Deployments: []*appsv1.Deployment{
			annotated(deploymentInNS("solo-ignored", "prod", 1, secureContainer("a", true)), "singleReplica"),
			deploymentInNS("solo-flagged", "prod", 1, secureContainer("a", true)),
			deploymentInNS("scaled", "prod", 2, secureContainer("a", true)),
		},
	}
	results := RunChecks(input)

	// solo-ignored is excluded from the denominator: 2 evaluated (solo-flagged
	// failing, scaled passing), not 3.
	if got := results.CheckCounts["singleReplica"]; got != (CheckCount{Evaluated: 2, Passed: 1}) {
		t.Errorf("singleReplica counts = %+v, want {Evaluated:2 Passed:1}", got)
	}
	if got := results.EvaluatedByNamespace["singleReplica"]["prod"]; got != 2 {
		t.Errorf("EvaluatedByNamespace[singleReplica][prod] = %d, want 2", got)
	}
}

func TestIgnoreAnnotation_MultiContainerDecrementsEvaluatedOnce(t *testing.T) {
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		Deployments: []*appsv1.Deployment{
			// Two containers both missing the readiness probe → two raw
			// findings for one (resource, check) pair pre-merge.
			annotated(deploymentInNS("multi", "prod", 2, secureContainer("a", false), secureContainer("b", false)), "readinessProbeMissing"),
			deploymentInNS("ok", "prod", 2, secureContainer("a", true)),
		},
	}
	results := RunChecks(input)

	if findingNames(results.Findings, "readinessProbeMissing")["multi"] {
		t.Error("readinessProbeMissing should be suppressed on annotated multi-container deployment")
	}
	if got := results.CheckCounts["readinessProbeMissing"]; got != (CheckCount{Evaluated: 1, Passed: 1}) {
		t.Errorf("readinessProbeMissing counts = %+v, want {Evaluated:1 Passed:1}", got)
	}
}

func TestIgnoreAnnotation_ReportsIgnoredSubjects(t *testing.T) {
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		Deployments: []*appsv1.Deployment{
			// Two containers → two raw findings, but one ignored subject.
			annotated(deploymentInNS("multi", "prod", 2, secureContainer("a", false), secureContainer("b", false)), "readinessProbeMissing,singleReplica"),
			annotated(deploymentInNS("solo", "dev", 1, secureContainer("a", true)), "singleReplica"),
			// Passing subjects produce no finding, so nothing is ignored.
			annotated(deploymentInNS("ok", "prod", 2, secureContainer("a", true)), "readinessProbeMissing"),
		},
	}
	results := RunChecks(input)

	if results.IgnoreChecksAnnotation != IgnoreChecksAnnotation {
		t.Errorf("IgnoreChecksAnnotation = %q, want %q", results.IgnoreChecksAnnotation, IgnoreChecksAnnotation)
	}
	want := map[string]map[string]int{
		"readinessProbeMissing": {"prod": 1},
		"singleReplica":         {"dev": 1},
	}
	if !reflect.DeepEqual(results.AnnotationIgnoredByNamespace, want) {
		t.Errorf("AnnotationIgnoredByNamespace = %v, want %v", results.AnnotationIgnoredByNamespace, want)
	}

	filtered := ApplySettings(results, []string{"dev"}, []string{"readinessProbeMissing"})
	if len(filtered.AnnotationIgnoredByNamespace) != 0 {
		t.Errorf("ignored namespaces and disabled checks must drop their counts, got %v", filtered.AnnotationIgnoredByNamespace)
	}
	if filtered.IgnoreChecksAnnotation != IgnoreChecksAnnotation {
		t.Error("ApplySettings must keep IgnoreChecksAnnotation")
	}
	if !reflect.DeepEqual(results.AnnotationIgnoredByNamespace, want) {
		t.Error("ApplySettings must not mutate the cached scan's map")
	}
}

func annotatedUnstructured(u *unstructured.Unstructured, value string) *unstructured.Unstructured {
	u.SetAnnotations(map[string]string{IgnoreChecksAnnotation: value})
	return u
}

// CRD subjects are keyed by the group audit findings actually carry ("" — CRD
// kinds are absent from the builtin table), not by the object's real API group.
func TestIgnoreAnnotation_CRDSubject(t *testing.T) {
	const g = "traefik.io"
	route := annotatedUnstructured(
		traefikRoute(g, "IngressRoute", "app", "r1", []map[string]any{{"name": "missing-svc"}}, nil),
		"traefikRouteMissingService",
	)
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		AllServices:              []*corev1.Service{},
		IngressRoutes:            []*unstructured.Unstructured{route},
	}
	results := RunChecks(input)

	if findingNames(results.Findings, "traefikRouteMissingService")["r1"] {
		t.Error("traefikRouteMissingService should be suppressed on the annotated IngressRoute")
	}
	if got := results.CheckCounts["traefikRouteMissingService"].Evaluated; got != 0 {
		t.Errorf("Evaluated = %d, want 0 — the ignored route must leave the denominator", got)
	}
}

// The opt-out is per-resource: annotating one CRD subject must not silence the
// same check on its unannotated siblings, nor steal their evaluated tally.
func TestIgnoreAnnotation_CRDSubjectScopedToResource(t *testing.T) {
	const g = "traefik.io"
	input := &CheckInput{
		ServiceAccounts:          []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers: []*autoscalingv2.HorizontalPodAutoscaler{},
		AllServices:              []*corev1.Service{},
		IngressRoutes: []*unstructured.Unstructured{
			annotatedUnstructured(
				traefikRoute(g, "IngressRoute", "app", "ignored", []map[string]any{{"name": "missing-a"}}, nil),
				"traefikRouteMissingService",
			),
			traefikRoute(g, "IngressRoute", "app", "flagged", []map[string]any{{"name": "missing-b"}}, nil),
		},
	}
	results := RunChecks(input)

	names := findingNames(results.Findings, "traefikRouteMissingService")
	if names["ignored"] {
		t.Error("annotated route should be suppressed")
	}
	if !names["flagged"] {
		t.Error("unannotated sibling route must still be flagged")
	}
	if got := results.CheckCounts["traefikRouteMissingService"]; got != (CheckCount{Evaluated: 1, Passed: 0}) {
		t.Errorf("counts = %+v, want {Evaluated:1 Passed:0}", got)
	}
	if got := results.EvaluatedByNamespace["traefikRouteMissingService"]["app"]; got != 1 {
		t.Errorf("EvaluatedByNamespace[app] = %d, want 1 — the sibling's tally must survive", got)
	}
}

// A CRD kind whose name collides with a builtin (CNPG Cluster) must still be
// keyed under "" like every other CRD subject.
func TestIgnoreAnnotation_CRDSubjectCollidingKind(t *testing.T) {
	cluster := &unstructured.Unstructured{Object: map[string]any{
		"apiVersion": "postgresql.cnpg.io/v1",
		"kind":       "Cluster",
		"metadata": map[string]any{
			"name": "db", "namespace": "prod",
			"annotations": map[string]any{IgnoreChecksAnnotation: "cnpgNoDeclarativeBackup"},
		},
	}}
	input := &CheckInput{
		ServiceAccounts:                   []*corev1.ServiceAccount{},
		HorizontalPodAutoscalers:          []*autoscalingv2.HorizontalPodAutoscaler{},
		CNPGClusters:                      []*unstructured.Unstructured{cluster},
		CNPGScheduledBackupsAuthoritative: true,
	}
	results := RunChecks(input)

	if findingNames(results.Findings, "cnpgNoDeclarativeBackup")["db"] {
		t.Error("cnpgNoDeclarativeBackup should be suppressed on the annotated CNPG Cluster")
	}
}

func TestIgnoreAnnotation_NonWorkloadSubject(t *testing.T) {
	input := &CheckInput{
		ServiceAccounts: []*corev1.ServiceAccount{},
		Pods:            []*corev1.Pod{},
		Services: []*corev1.Service{{
			ObjectMeta: metav1.ObjectMeta{
				Name:        "headless-tool",
				Namespace:   "prod",
				Annotations: map[string]string{IgnoreChecksAnnotation: "serviceNoMatchingPods"},
			},
			Spec: corev1.ServiceSpec{Selector: map[string]string{"app": "nothing"}},
		}},
	}
	results := RunChecks(input)

	if findingNames(results.Findings, "serviceNoMatchingPods")["headless-tool"] {
		t.Error("serviceNoMatchingPods should be suppressed on annotated Service")
	}
}