Skip to content

🐛 fix(webhook): validate spec-derived GVKs when the profile is missing - #308

Merged
konih merged 1 commit into
mainfrom
fix/clustertarget-admission-gvk-rules
Aug 18, 2026
Merged

🐛 fix(webhook): validate spec-derived GVKs when the profile is missing#308
konih merged 1 commit into
mainfrom
fix/clustertarget-admission-gvk-rules

Conversation

@konih

@konih konih commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #304. That PR made the reconcile backstop check every rule GVK, and made
admission skip the GVK check entirely when the KollectProfile is absent. This closes the
remaining admission gap: the GVKs that come from the spec are knowable without the
profile, so they stay enforced.

Problem

scope.CollectRuleGVKs returns the resourceRules GVKs and ignores profileGVK
entirely once resourceRules is non-empty. Only the profile targetGVK is unknowable
when the profile has not been created yet — the rule GVKs are right there in the spec.

Skipping the whole loop on IsNotFound therefore admitted a KollectClusterTarget whose
resourceRules name a GVK outside allowedGVKs. It was caught later by the reconcile
backstop (ScopeGVKDenied), but a failurePolicy=fail webhook that already has the data
should not defer a decision it can make.

Change

  • Resolve the profile via a switch, keeping profileResolved alongside profileGVK.
  • Run the GVK loop when profileResolved || len(spec.ResourceRules) > 0.
  • Collapse the duplicated namespace tail that 🐛 fix(webhook): enforce cluster-scope when the profile is missing #304 left in the NotFound branch, so the
    two paths cannot drift apart.
  • Correct the ScopeCeiling doc comment: it carries namespaces only; GVKs are enforced at
    admission and, as backstop, at reconcile.

Tests

  • TestKollectClusterTargetValidator_missingProfileSpecDerivedChecks — no profile object;
    asserts an in-scope target still admits, a denied resourceRules GVK rejects, and a
    profileRef.namespace outside allowedStaticRefNamespaces rejects. That last assertion
    covers a line 🐛 fix(webhook): enforce cluster-scope when the profile is missing #304 added but left unexercised, because its fixture never set
    allowedStaticRefNamespaces.
  • TestKollectClusterTargetReconciler_deniedRuleGVKDegrades — a permitted profile
    targetGVK must not launder a denied rule GVK. Green before this change; it locks the
    reconcile half of the same invariant.

Red before the fix on the resourceRules assertion.

Validation

go build ./..., the webhook and controller packages, task verify, and
hack/test/cluster_scope_rbac_test.sh all pass on top of current main.

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...l/webhook/v1alpha1/kollectclustertarget_webhook.go 91.66% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Follow-up to #304, which is the parent commit of this branch.

#304 defers the whole GVK check when profileRef does not resolve, but only the
profile targetGVK actually depends on the profile. `CollectRuleGVKs` returns the
`spec.resourceRules[].gvk` list and ignores the profile GVK entirely whenever
resourceRules is non-empty, so those GVKs are knowable at admission and were
being waved through. Not an exploit path — a target cannot collect while its
profile is missing, and reconcile now catches it once the profile appears — but
admission should reject a spec it can fully evaluate.

The NotFound branch also duplicated the happy path's two namespace checks. That
is the divergence shape that caused the bug #304 fixes: a third check added to
the tail later would silently not run for missing profiles. Resolve is now a
switch that yields `profileGVK` plus `profileResolved`, and the whole tail runs
once for both paths. GVK validation is skipped only when the profile is missing
*and* resourceRules is empty, where the alternative is comparing a zero GVK
against allowedGVKs and rejecting a legal create-before-profile.

Tests:
- `missingProfileSpecDerivedChecks` — denied resourceRules GVK and denied
  profileRef namespace, both with no profile object; red on the first before
  this change. It also covers the `ValidateClusterScopeStaticRefNamespace` call
  #304 added, which its own fixture could not exercise: allowedStaticRefNamespaces
  was unset there, so the call returned nil unconditionally.
- `deniedRuleGVKDegrades` — reconcile side, permitted profile targetGVK plus a
  denied resourceRules GVK. Green before this commit; it locks the rules path
  through CollectRuleGVKs that #304's test did not reach.
- ScopeCeiling's doc comment claimed "namespace and GVK checks" while the struct
  only carries namespaces, which points readers at the wrong enforcement point.

Gates: lint (golangci v2 + arch-lint) clean, verify ok, scrub ok, webhook and
controller unit tests green. envtest suites are CI-only on this host; a
GVK-denied spec in cluster_scope_enforce_envtest_test.go is left as follow-up
rather than added unverified.
@konih
konih force-pushed the fix/clustertarget-admission-gvk-rules branch from 25b0677 to c13d30c Compare August 18, 2026 09:52
@sonarqubecloud

Copy link
Copy Markdown

@konih
konih merged commit 3cac404 into main Aug 18, 2026
35 checks passed
@konih
konih deleted the fix/clustertarget-admission-gvk-rules branch August 18, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant