Skip to content

fix(security): CVE-2026-10609 verify CLF creator authorization for SA token usage (LOG-9441) - #3383

Open
vparfonov wants to merge 1 commit into
openshift:release-6.5from
vparfonov:log9441
Open

fix(security): CVE-2026-10609 verify CLF creator authorization for SA token usage (LOG-9441)#3383
vparfonov wants to merge 1 commit into
openshift:release-6.5from
vparfonov:log9441

Conversation

@vparfonov

@vparfonov vparfonov commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

CVE-2026-10609: Authorize ServiceAccount usage in ClusterLogForwarder

Description

This PR addresses CVE-2026-10609, a privilege escalation vulnerability where users could reference any ServiceAccount in a ClusterLogForwarder (CLF) and forward its token to external log outputs without having explicit permission to use that ServiceAccount.

Root Cause

The operator did not validate whether a user had permission to use the ServiceAccount specified in a CLF. This allowed privilege escalation if a cluster admin had granted a user's pod SCC permissions but not SA usage rights the user could then create a CLF to steal that SA's token.

Solution

  • Added a ValidatingAdmissionWebhook: Intercepts all CREATE and UPDATE operations for ClusterLogForwarder resources.
  • SubjectAccessReview (SAR) Enforcement: Performs an unconditional SAR check using the requesting user's full identity (Username, UID, and Groups) to verify they possess the use verb on the referenced ServiceAccount.
  • Zero-Downtime Upgrades: Because the check is performed purely at the admission webhook level, existing CLFs already in etcd will continue to run without interruption upon operator upgrade.

Additionally

  • fix a few formatting issue
  • update hack/run-linter script shebangs to #!/usr/bin/env bash for better portability

/cc @Clee2691
/assign @jcantrill

Links

Summary by CodeRabbit

  • New Features

    • Added admission validation for ClusterLogForwarder resources that use ServiceAccount tokens.
    • Requests now verify authorization to use referenced ServiceAccounts.
    • Added automatic tracking of the requesting identity for auditing and validation.
    • Enabled mutating and validating webhooks for ClusterLogForwarder creation and updates.
  • Bug Fixes

    • Preserved compatibility for resources without modifier metadata.
    • Improved permission handling for infrastructure and audit log inputs.
  • Tests

    • Added unit and end-to-end coverage for authorized, denied, and legacy ServiceAccount usage.

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 3, 2026
@openshift-ci

openshift-ci Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Walkthrough

Changes

ClusterLogForwarder ServiceAccount authorization

Layer / File(s) Summary
Admission identity and authorization
internal/constants/annotations.go, internal/webhook/..., api/observability/v1/conditions.go
The webhook stores modifier UserInfo, validates ServiceAccount usage with SubjectAccessReview, and reports unauthorized access with a dedicated condition reason.
Pipeline permission validation
internal/validations/observability/...
Pipeline validation handles token-forwarding CLFs, checks modifier authorization, and preserves legacy unannotated CLFs.
Webhook runtime and deployment wiring
cmd/main.go, config/webhook/..., config/default/..., bundle/manifests/...
The manager starts and registers the webhook server. Kustomize and bundle manifests configure certificates, services, admission rules, and deployment resources.
End-to-end authorization coverage
test/e2e/collection/sa_authorization/...
End-to-end tests provision ServiceAccounts and permissions, then verify denied and allowed CLF creation and modifier annotations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ClusterLogForwarderWebhook
  participant SubjectAccessReview
  participant ClusterLogForwarderValidation
  User->>ClusterLogForwarderWebhook: Submit ClusterLogForwarder
  ClusterLogForwarderWebhook->>ClusterLogForwarderWebhook: Store modifier UserInfo
  ClusterLogForwarderWebhook->>SubjectAccessReview: Check ServiceAccount use permission
  SubjectAccessReview-->>ClusterLogForwarderWebhook: Return authorization status
  ClusterLogForwarderWebhook->>ClusterLogForwarderValidation: Validate pipeline permissions
  ClusterLogForwarderValidation-->>User: Accept or reject resource
Loading
🚥 Pre-merge checks | ✅ 12 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning The E2E suite has cleanup hooks, but all cluster oc helpers use unbounded exec.Command/CombinedOutput, and many new assertions omit failure messages. Use exec.CommandContext with defined timeouts for cluster commands and add diagnostic messages to setup and verification assertions.
Microshift Test Compatibility ⚠️ Warning The new untagged Ginkgo suite creates and queries ClusterLogForwarder resources in observability.openshift.io, an unavailable OpenShift API group on MicroShift. MicroShift compatibility notice: add [apigroup:observability.openshift.io], [Skipped:MicroShift], or an IsMicroShiftCluster skip; otherwise verify with the MicroShift e2e job.
✅ Passed checks (12 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All added Ginkgo titles use static string literals; generated namespace and ServiceAccount values appear only in test bodies, and no title contains runtime data.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The added Ginkgo suite tests CLF and ServiceAccount authorization through oc commands; it uses no node counts, scheduling, affinity, failover, scaling, or multi-endpoint assumptions.
Topology-Aware Scheduling Compatibility ✅ Passed The PR adds webhook ports, resources, and a certificate mount only. It adds no replica, affinity, spread, node-role selector, toleration, or PDB constraint; existing replicas:1 and linux nodeSelect...
Ote Binary Stdout Contract ✅ Passed PR process-level code has no direct stdout writes or Ginkgo suite configuration; TestWebhook and TestSuite only register failures and call RunSpecs.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The added Ginkgo suite uses only cluster oc operations and framework setup; it contains no IP literals, URL construction, public hosts, external downloads, or registry references.
No-Weak-Crypto ✅ Passed The PR adds no MD5, SHA1, DES, RC4, Blowfish, ECB, custom crypto, or secret comparisons; existing MD5 code is unchanged from the PR base.
Container-Privileges ✅ Passed No changed manifest sets privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation true; operator pods enforce runAsNonRoot and drop all capabilities.
No-Sensitive-Data-In-Logs ✅ Passed Changed production logs contain only CLF/namespace names and service-account identifiers; no passwords, tokens, API keys, UserInfo, or customer payloads are logged.
Title check ✅ Passed The title clearly identifies the security fix, affected resource, authorization check, CVE, and tracking issue.
Description check ✅ Passed The description explains the vulnerability, root cause, solution, implementation scope, reviewer assignments, and related JIRA issue.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@vparfonov

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@openshift-ci

openshift-ci Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: vparfonov
Once this PR has been reviewed and has the lgtm label, please assign cahartma for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (9)
internal/validations/observability/validate_permissions_test.go (2)

304-312: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the shadowed clfServiceAccount declaration.

The enclosing Context at Line 87 already declares clfServiceAccount with the same name, namespace, and type. This inner declaration shadows it and duplicates the fixture. Delete it and use the outer variable.

♻️ Proposed change
 		Context("when validating SA usage authorization", func() {
-			var (
-				clfServiceAccount = &corev1.ServiceAccount{
-					ObjectMeta: v1.ObjectMeta{
-						Name:      "test-serviceAccount",
-						Namespace: constants.OpenshiftNS,
-					},
-				}
-			)
-
 			It("should pass when modifier annotation is set and user is authorized to use SA", func() {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/validations/observability/validate_permissions_test.go` around lines
304 - 312, Remove the inner clfServiceAccount declaration from the “when
validating SA usage authorization” Context and reuse the existing outer
clfServiceAccount fixture declared by the enclosing Context, preserving its
current name, namespace, and type.

314-430: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated LokiStack output spec and add a malformed-annotation case.

Two points:

  • The three specs repeat the same LokiStack output block. Extract a helper such as lokiStackSATokenOutput() and reuse it.
  • No spec covers a malformed modifier annotation. validateModifierCanUseSA returns an error for invalid JSON at Lines 166-168 of internal/validations/observability/validate_permissions.go, and that branch stays untested. Add a spec that sets constants.AnnotationModifier to "not-json" and expects ReasonServiceAccountUsageNotAuthorized.

The second point matters more, because a malformed annotation must fail closed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/validations/observability/validate_permissions_test.go` around lines
314 - 430, The validation tests repeat the LokiStack service-account output
configuration and omit malformed modifier coverage. Add a shared helper such as
lokiStackSATokenOutput() and reuse it in the three affected specs, then add a
case setting constants.AnnotationModifier to "not-json" and assert
ValidatePermissions produces an unauthorized condition with
ReasonServiceAccountUsageNotAuthorized.
internal/validations/observability/validate_permissions.go (3)

19-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move ParseModifierUserInfo out of the webhook package.

The validation package now depends on the webhook package for one annotation parser. The dependency direction is inverted: admission code and validation code should both depend on a shared helper, not on each other. Place ParseModifierUserInfo next to constants.AnnotationModifier or in internal/api/observability, then let both callers use it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/validations/observability/validate_permissions.go` at line 19, Move
ParseModifierUserInfo from the webhook package into a shared package alongside
constants.AnnotationModifier or under internal/api/observability, then update
both the admission and validation callers to use the shared symbol and remove
the validation package’s direct webhook dependency.

164-190: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Pass the caller context instead of context.TODO().

validateModifierCanUseSA issues a blocking API call to create the SubjectAccessReview. With context.TODO() the call carries no deadline and no cancellation. If the API server stalls, the reconcile worker blocks. internalcontext.ForwarderContext is already available in ValidatePermissions, so thread a context.Context through the call.

As per coding guidelines: "context.Context for cancellation and timeouts".

♻️ Proposed change
-func validateModifierCanUseSA(k8sClient client.Client, clf obs.ClusterLogForwarder, serviceAccount corev1.ServiceAccount) error {
+func validateModifierCanUseSA(ctx context.Context, k8sClient client.Client, clf obs.ClusterLogForwarder, serviceAccount corev1.ServiceAccount) error {
 	userInfo, err := clfwebhook.ParseModifierUserInfo(clf.Annotations)
@@
-	if err := k8sClient.Create(context.TODO(), sar); err != nil {
+	if err := k8sClient.Create(ctx, sar); err != nil {
 		return fmt.Errorf("failed to check if user %q can use service account %q: %w", userInfo.Username, serviceAccount.Name, err)
 	}

Update the call site:

-		if err = validateModifierCanUseSA(k8sClient, *clf, *serviceAccount); err != nil {
+		if err = validateModifierCanUseSA(ctx, k8sClient, *clf, *serviceAccount); err != nil {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/validations/observability/validate_permissions.go` around lines 164
- 190, Thread the caller’s context from ValidatePermissions through
validateModifierCanUseSA and use it for the blocking k8sClient.Create call that
submits the SubjectAccessReview, replacing context.TODO(). Preserve the existing
authorization checks and error handling while ensuring the API request inherits
cancellation and deadlines from internalcontext.ForwarderContext.

Source: Path instructions


169-173: 🔒 Security & Privacy | 🔵 Trivial

Add observability for the legacy bypass.

The legacy path allows any ClusterLogForwarder that carries no modifier annotation. The comment explains the reasoning, and the mutating webhook restores the annotation on the next write. The bypass therefore lasts until the resource is next modified, which can be indefinite.

Two operational suggestions:

  • Emit a metric or a Warning event, not only a log line, so administrators can count unverified ClusterLogForwarder resources after upgrade.
  • Set the Authorized condition message to state that strict enforcement is pending, so the state is visible in oc get clf -o yaml.
    [operational]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/validations/observability/validate_permissions.go` around lines 169
- 173, Enhance the legacy bypass in the userInfo nil branch of the permissions
validation flow to emit a countable metric or Warning event in addition to the
existing log. Also set the ClusterLogForwarder Authorized condition message to
clearly indicate that strict RBAC enforcement is pending until the resource is
modified, while preserving the current allow behavior.
internal/webhook/clusterlogforwarder_webhook_test.go (2)

261-285: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider asserting the SAR contents inside the mocks.

Both mocks set Status.Allowed without checking the request. A regression that sends the wrong Verb, Resource, Namespace, or Name still passes. The mocks in internal/validations/observability/validate_permissions_test.go already gate on Resource == "serviceaccounts" and Verb == "use". Apply the same gate here.

♻️ Proposed change
 func (c *mockSARAllowClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error {
 	sar, ok := obj.(*authorizationapi.SubjectAccessReview)
 	if !ok {
 		return fmt.Errorf("unexpected object type: %T", obj)
 	}
+	Expect(sar.Spec.ResourceAttributes).ToNot(BeNil())
+	Expect(sar.Spec.ResourceAttributes.Resource).To(Equal("serviceaccounts"))
+	Expect(sar.Spec.ResourceAttributes.Verb).To(Equal("use"))
 	sar.Status.Allowed = true
 	return nil
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/webhook/clusterlogforwarder_webhook_test.go` around lines 261 - 285,
Update mockSARAllowClient.Create and mockSARDenyClient.Create to validate the
incoming SubjectAccessReview’s Resource and Verb fields before setting
Status.Allowed, matching the existing serviceaccount/use gate used by the
observability permission mocks; reject mismatched requests so incorrect SAR
contents cannot pass the tests.

149-257: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add specs for the error paths of validateSAUsage.

The current specs cover allow, deny, and skip. Three error paths stay uncovered:

  • admission.RequestFromContext fails, which happens when the context carries no admission request.
  • v.Client.Create returns an error for the SubjectAccessReview.
  • clf.Spec.ServiceAccount.Name is empty, which currently allows the request.

The third case matters most. It documents the intentional bypass at Lines 91-93 of internal/webhook/clusterlogforwarder_webhook.go.

I can generate these specs if you want.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/webhook/clusterlogforwarder_webhook_test.go` around lines 149 - 257,
Extend the validator specs around ValidateCreate and validateSAUsage to cover
the three missing paths: a context without an admission request, a Client.Create
error while creating the SubjectAccessReview, and an empty
clf.Spec.ServiceAccount.Name that remains allowed. Use suitable mock clients and
contexts, assert the expected error or successful validation, and preserve the
existing allow, deny, and skip cases.
test/e2e/collection/sa_authorization/sa_authorization_test.go (1)

141-182: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Register the oc-created RBAC objects for cleanup and tolerate re-creation.

grantCLFAccess and grantSAUsage create a Role and a RoleBinding with fixed names and call Fail on any error, including AlreadyExists. Building these objects with the typed client and e2e.Test.Recreate, as test/framework/e2e/auth.go does for ClusterRoleBinding, removes the ordering dependence and gives automatic cleanup. It also removes the dependence on an oc binary in the test environment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/collection/sa_authorization/sa_authorization_test.go` around lines
141 - 182, Replace the oc-based creation in grantCLFAccess and grantSAUsage with
typed RBAC client object creation, registering each Role and RoleBinding through
e2e.Test.Recreate so existing fixed-name objects are safely reused and
automatically cleaned up. Follow the established pattern in the framework’s
ClusterRoleBinding setup, preserving the current permissions, subjects,
namespaces, and resource names while removing direct exec.Command and
Fail-on-AlreadyExists behavior.
internal/webhook/clusterlogforwarder_webhook.go (1)

95-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Set Group explicitly on the ResourceAttributes.

serviceaccounts belongs to the core API group, so the empty Group value is correct today. State it explicitly to document intent and to avoid a wrong default if this block is copied for a non-core resource. internal/validations/observability/validate_permissions.go builds its SAR through createSubjectAccessReview, which always passes a group; the two paths differ in style.

♻️ Proposed change
 			ResourceAttributes: &authorizationapi.ResourceAttributes{
+				Group:     "",
 				Verb:      "use",
 				Resource:  "serviceaccounts",
 				Name:      saName,
 				Namespace: clf.Namespace,
 			},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/webhook/clusterlogforwarder_webhook.go` around lines 95 - 107,
Update the ResourceAttributes construction in the SubjectAccessReview within the
webhook handler to set Group explicitly to the empty core API group value. Keep
the existing serviceaccounts resource authorization unchanged, and only document
the intended core-group setting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/default/manager_webhook_patch.yaml`:
- Around line 7-23: Update the container entry in the webhook patch to target
the existing cluster-logging-operator container instead of appending a new
manager container. Preserve the webhook port and certificate volume settings,
and add readOnlyRootFilesystem: true plus the required CPU and memory limits to
that container.

In `@config/webhook/service.yaml`:
- Around line 6-14: Update the webhook Service selector to use name:
cluster-logging-operator, matching the manager Deployment pod label instead of
control-plane. Also update the webhook patch target from manager to
cluster-logging-operator so containerPort 9443 applies to the correct container,
preserving connectivity for both fail-closed webhooks.

In `@internal/validations/observability/validate_permissions.go`:
- Around line 152-153: Update the obs.InputTypeReceiver branch in the permission
validation logic to restore the previous receiver classification instead of
inserting obs.InputTypeInfrastructure. Preserve the token fallback’s
collect-infrastructure-logs handling for receiver-only CLFs so existing
collector deployments remain enabled, while retaining the CVE-related
ServiceAccount authorization changes elsewhere.

In `@test/e2e/collection/sa_authorization/sa_authorization_test.go`:
- Around line 107-117: The spec titled “should allow legacy CLF without modifier
annotation to continue running” currently verifies webhook annotation injection
instead of legacy handling. Either rename the test to describe the
annotation-present behavior, or update its setup to remove
constants.AnnotationModifier through a direct API write that bypasses admission
before validating continued operation; alternatively cover the nil-annotation
case in validateModifierCanUse using the existing unit-test pattern.
- Around line 100-104: Update the annotation verification in the sa
authorization test to treat the value from ocGetAnnotation as serialized
UserInfo JSON rather than a plain username. Unmarshal annotation into the
appropriate user-info structure and assert its Username field equals user, while
preserving the existing error checks.
- Around line 134-139: Update ocGetAnnotation so the jsonpath annotation key is
passed unchanged inside the bracket-quoted expression; remove the
strings.ReplaceAll call while preserving the existing command and output
handling. Keep the strings import only if other code, such as strings.NewReader,
still uses it.

---

Nitpick comments:
In `@internal/validations/observability/validate_permissions_test.go`:
- Around line 304-312: Remove the inner clfServiceAccount declaration from the
“when validating SA usage authorization” Context and reuse the existing outer
clfServiceAccount fixture declared by the enclosing Context, preserving its
current name, namespace, and type.
- Around line 314-430: The validation tests repeat the LokiStack service-account
output configuration and omit malformed modifier coverage. Add a shared helper
such as lokiStackSATokenOutput() and reuse it in the three affected specs, then
add a case setting constants.AnnotationModifier to "not-json" and assert
ValidatePermissions produces an unauthorized condition with
ReasonServiceAccountUsageNotAuthorized.

In `@internal/validations/observability/validate_permissions.go`:
- Line 19: Move ParseModifierUserInfo from the webhook package into a shared
package alongside constants.AnnotationModifier or under
internal/api/observability, then update both the admission and validation
callers to use the shared symbol and remove the validation package’s direct
webhook dependency.
- Around line 164-190: Thread the caller’s context from ValidatePermissions
through validateModifierCanUseSA and use it for the blocking k8sClient.Create
call that submits the SubjectAccessReview, replacing context.TODO(). Preserve
the existing authorization checks and error handling while ensuring the API
request inherits cancellation and deadlines from
internalcontext.ForwarderContext.
- Around line 169-173: Enhance the legacy bypass in the userInfo nil branch of
the permissions validation flow to emit a countable metric or Warning event in
addition to the existing log. Also set the ClusterLogForwarder Authorized
condition message to clearly indicate that strict RBAC enforcement is pending
until the resource is modified, while preserving the current allow behavior.

In `@internal/webhook/clusterlogforwarder_webhook_test.go`:
- Around line 261-285: Update mockSARAllowClient.Create and
mockSARDenyClient.Create to validate the incoming SubjectAccessReview’s Resource
and Verb fields before setting Status.Allowed, matching the existing
serviceaccount/use gate used by the observability permission mocks; reject
mismatched requests so incorrect SAR contents cannot pass the tests.
- Around line 149-257: Extend the validator specs around ValidateCreate and
validateSAUsage to cover the three missing paths: a context without an admission
request, a Client.Create error while creating the SubjectAccessReview, and an
empty clf.Spec.ServiceAccount.Name that remains allowed. Use suitable mock
clients and contexts, assert the expected error or successful validation, and
preserve the existing allow, deny, and skip cases.

In `@internal/webhook/clusterlogforwarder_webhook.go`:
- Around line 95-107: Update the ResourceAttributes construction in the
SubjectAccessReview within the webhook handler to set Group explicitly to the
empty core API group value. Keep the existing serviceaccounts resource
authorization unchanged, and only document the intended core-group setting.

In `@test/e2e/collection/sa_authorization/sa_authorization_test.go`:
- Around line 141-182: Replace the oc-based creation in grantCLFAccess and
grantSAUsage with typed RBAC client object creation, registering each Role and
RoleBinding through e2e.Test.Recreate so existing fixed-name objects are safely
reused and automatically cleaned up. Follow the established pattern in the
framework’s ClusterRoleBinding setup, preserving the current permissions,
subjects, namespaces, and resource names while removing direct exec.Command and
Fail-on-AlreadyExists behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7362f811-bc02-4343-8a8f-2ac6a2e0ee95

📥 Commits

Reviewing files that changed from the base of the PR and between 88ddecb and da0fa85.

📒 Files selected for processing (14)
  • api/observability/v1/conditions.go
  • cmd/main.go
  • config/default/kustomization.yaml
  • config/default/manager_webhook_patch.yaml
  • config/webhook/kustomization.yaml
  • config/webhook/manifests.yaml
  • config/webhook/service.yaml
  • internal/constants/annotations.go
  • internal/validations/observability/validate_permissions.go
  • internal/validations/observability/validate_permissions_test.go
  • internal/webhook/clusterlogforwarder_webhook.go
  • internal/webhook/clusterlogforwarder_webhook_test.go
  • test/e2e/collection/sa_authorization/sa_authorization_test.go
  • test/e2e/collection/sa_authorization/suite_test.go

Comment thread config/default/manager_webhook_patch.yaml
Comment thread config/webhook/service.yaml Outdated
Comment thread internal/validations/observability/validate_permissions.go Outdated
Comment thread test/e2e/collection/sa_authorization/sa_authorization_test.go Outdated
Comment thread test/e2e/collection/sa_authorization/sa_authorization_test.go Outdated
Comment thread test/e2e/collection/sa_authorization/sa_authorization_test.go Outdated
@vparfonov

Copy link
Copy Markdown
Contributor Author

/test e2e-target

@vparfonov

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qodo-for-rh-openshift

qodo-for-rh-openshift Bot commented Aug 3, 2026

Copy link
Copy Markdown

PR-Agent: could not find a component named e2e-target in a supported language in this PR.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bundle/manifests/cluster-logging.clusterserviceversion.yaml`:
- Around line 2375-2384: Use the declared cluster-logging-operator Deployment as
the sole webhook workload: in
bundle/manifests/cluster-logging.clusterserviceversion.yaml lines 2375-2384,
remove the image-less manager container and move its webhook-server port and
certificate volume mount onto the existing cluster-logging-operator container;
at lines 2456-2496, set both deploymentName values to cluster-logging-operator;
update bundle/manifests/cluster-logging-operator-webhook-service_v1_service.yaml
lines 9-14 so its selector matches that Deployment’s template labels.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5e884923-90da-42e5-81ff-9141d4a6a824

📥 Commits

Reviewing files that changed from the base of the PR and between da0fa85 and 5f3a44b.

📒 Files selected for processing (9)
  • bundle/manifests/cluster-logging-operator-webhook-service_v1_service.yaml
  • bundle/manifests/cluster-logging.clusterserviceversion.yaml
  • cmd/main.go
  • config/default/manager_webhook_patch.yaml
  • config/webhook/service.yaml
  • internal/validations/observability/validate_permissions.go
  • internal/validations/observability/validate_permissions_test.go
  • internal/webhook/clusterlogforwarder_webhook.go
  • test/e2e/collection/sa_authorization/sa_authorization_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • config/webhook/service.yaml
  • cmd/main.go
  • internal/validations/observability/validate_permissions_test.go
  • internal/webhook/clusterlogforwarder_webhook.go

Comment on lines +2375 to +2384
- name: manager
ports:
- containerPort: 9443
name: webhook-server
protocol: TCP
resources: {}
volumeMounts:
- mountPath: /etc/webhook-server/serving-certs
name: cert
readOnly: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Use one valid webhook workload across the bundle.

The CSV defines only cluster-logging-operator, but the webhook configuration targets cluster-logging-operator-webhook. The added manager container also has no image, so Kubernetes rejects the Deployment. The executable cluster-logging-operator container does not receive the certificate mount.

  • bundle/manifests/cluster-logging.clusterserviceversion.yaml#L2375-L2384: Move the port and certificate mount to the existing cluster-logging-operator container. Do not add an image-less manager container.
  • bundle/manifests/cluster-logging.clusterserviceversion.yaml#L2456-L2496: Set both deploymentName values to the declared Deployment name, or add a complete matching webhook Deployment.
  • bundle/manifests/cluster-logging-operator-webhook-service_v1_service.yaml#L9-L14: Change the selector to match the selected Deployment template labels.
📍 Affects 2 files
  • bundle/manifests/cluster-logging.clusterserviceversion.yaml#L2375-L2384 (this comment)
  • bundle/manifests/cluster-logging.clusterserviceversion.yaml#L2456-L2496
  • bundle/manifests/cluster-logging-operator-webhook-service_v1_service.yaml#L9-L14
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bundle/manifests/cluster-logging.clusterserviceversion.yaml` around lines
2375 - 2384, Use the declared cluster-logging-operator Deployment as the sole
webhook workload: in bundle/manifests/cluster-logging.clusterserviceversion.yaml
lines 2375-2384, remove the image-less manager container and move its
webhook-server port and certificate volume mount onto the existing
cluster-logging-operator container; at lines 2456-2496, set both deploymentName
values to cluster-logging-operator; update
bundle/manifests/cluster-logging-operator-webhook-service_v1_service.yaml lines
9-14 so its selector matches that Deployment’s template labels.

return nil, fmt.Errorf("expected ClusterLogForwarder, got %T", obj)
}

if !internalobs.Outputs(clf.Spec.Outputs).NeedServiceAccountToken() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We care about the SA usage regardless of the output since it can be granted SCC permissions and mounted to any workload

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern is that non-cluster-admin CLF creates will break without new RBAC. The webhook requires use on the referenced ServiceAccount. So anyone who is not cluster-admin and without a role granting use will start getting not authorized after upgrade.
@jcantrill

Comment thread internal/constants/annotations.go Outdated
return nil
}

func (v *ClusterLogForwarderValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introducing this webhook will allow us to move most all our "post create validations" here in future.

Comment thread internal/webhook/clusterlogforwarder_webhook.go Outdated
return nil, nil
}

sar := &authorizationapi.SubjectAccessReview{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we already have internal/runtime and buiders that do this from adding SAR to metric requests?

Comment thread internal/webhook/clusterlogforwarder_webhook.go
}

// validateModifierCanUseSA checks that the user who last modified the CLF has permission to use the referenced SA.
// Legacy CLFs created before the webhook was deployed will not have the modifier annotation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation runs post admission. The webhook runs on 'create' and 'update' which I don't believe either would run on upgrade of the operator. I think if we went this route it would be acceptible to validate permission on update and reject; this is something we should be able to note in the release notes. I'm not certain any of the following would be required then because it would already be validated in the webhook

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the reconciler checking

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main concern is that non-cluster-admin CLF creates will break without new RBAC. The webhook requires use on the referenced ServiceAccount. So anyone who is not cluster-admin and without a role granting use will start getting not authorized after upgrade.
Release notes must cover this, probably with a breaking changes mark.

@jcantrill

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@r2d2rnd Maybe you should know about this too and it would be interesting to hear your opinion

@vparfonov

Copy link
Copy Markdown
Contributor Author

/test e2e-target

@qodo-for-rh-openshift

qodo-for-rh-openshift Bot commented Aug 3, 2026

Copy link
Copy Markdown

PR-Agent: could not find a component named e2e-target in a supported language in this PR.

@vparfonov

Copy link
Copy Markdown
Contributor Author

/test e2e-target

@qodo-for-rh-openshift

qodo-for-rh-openshift Bot commented Aug 3, 2026

Copy link
Copy Markdown

PR-Agent: could not find a component named e2e-target in a supported language in this PR.

@vparfonov
vparfonov force-pushed the log9441 branch 2 times, most recently from 84d543b to 2c987c7 Compare August 4, 2026 07:52
@vparfonov
vparfonov marked this pull request as ready for review August 4, 2026 08:33
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 4, 2026
@openshift-ci
openshift-ci Bot requested review from Clee2691 and jcantrill August 4, 2026 08:33
@qodo-for-rh-openshift

qodo-for-rh-openshift Bot commented Aug 4, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Fix CVE-2026-10609: validate ServiceAccount 'use' for CLF via webhook

🐞 Bug fix ✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add a ValidatingAdmissionWebhook to authorize ServiceAccount references in ClusterLogForwarder.
• Enforce SubjectAccessReview checks for 'use' on the referenced ServiceAccount (create/update).
• Wire webhook server + manifests (service/certs/OLM CSV) and add unit + e2e coverage.
Diagram

graph TD
  U([Requesting user]) --> A["Kubernetes API Server"] --> V["ValidatingWebhookConfiguration"] --> S["Webhook Service :443"] --> W["CLO Webhook Server :9443"] --> R["SubjectAccessReview: use ServiceAccount"] --> D{Allowed?}
  D -->|yes| OK["Admit CLF"]
  D -->|no| NO["Reject CLF"]

  subgraph Legend
    direction LR
    _actor([User]) ~~~ _comp["Component"] ~~~ _dec{Decision}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Validate only when outputs use SA token
  • ➕ Less restrictive: only blocks CLFs that can actually exfiltrate SA tokens
  • ➕ Potentially fewer surprises for users setting serviceAccount for non-token use cases
  • ➖ More complex and brittle: needs correct detection across all outputs/auth modes
  • ➖ Risk of missing future token-using outputs and reintroducing the vulnerability
2. Enforce in reconciler (controller) instead of admission
  • ➕ No webhook infrastructure (service/certs/webhook config) to maintain
  • ➕ Can surface status conditions instead of hard admission failures
  • ➖ Not preventative: the insecure object is still persisted to etcd
  • ➖ Race window where reconciler may act on an unauthorized CLF before it is flagged
3. Use SelfSubjectAccessReview instead of SubjectAccessReview
  • ➕ Conceptually aligns with checking the caller's own permissions
  • ➕ May reduce the need to pass explicit user/group/uid fields
  • ➖ Admission webhook runs as the operator service account, so 'self' is not the requesting user
  • ➖ Still requires impersonation or explicit user identity propagation, which admission already provides

Recommendation: Keep the validating admission webhook approach and the unconditional SAR check. Admission-time enforcement is the most robust way to prevent persistence of an unauthorized CLF and avoids relying on output-specific parsing. The unconditional check trades minor strictness for long-term safety: any future feature that forwards SA credentials remains protected without needing new validation logic.

Files changed (16) +581 / -45

Bug fix (2) +97 / -0
main.goEnable controller-runtime webhook server and register CLF validator +11/-0

Enable controller-runtime webhook server and register CLF validator

• Configures the manager with a webhook server on port 9443 using the mounted cert directory. Registers the ClusterLogForwarder validating webhook with the manager during startup.

cmd/main.go

clusterlogforwarder_webhook.goAdd CLF validating webhook enforcing ServiceAccount 'use' via SAR +86/-0

Add CLF validating webhook enforcing ServiceAccount 'use' via SAR

• Introduces a controller-runtime CustomValidator for ClusterLogForwarder CREATE/UPDATE. Extracts the referenced serviceAccount name and performs a SubjectAccessReview using the requesting user's username/uid/groups to require the 'use' verb on the ServiceAccount in the CLF namespace.

internal/webhook/clusterlogforwarder_webhook.go

Refactor (1) +0 / -2
s3.goRemove trailing whitespace/newlines in S3 output generator +0/-2

Remove trailing whitespace/newlines in S3 output generator

• Cleans up file ending by removing trailing blank lines; no functional changes.

internal/generator/vector/output/aws/s3/s3.go

Tests (5) +342 / -34
configmap_test.goFix indentation/formatting in ConfigMaps hash tests +32/-32

Fix indentation/formatting in ConfigMaps hash tests

• Adjusts Ginkgo test indentation and block structure for readability without changing behavior.

internal/api/observability/configmap_test.go

s3_test.goFix formatting in S3 output generator tests +2/-2

Fix formatting in S3 output generator tests

• Normalizes spacing in Ginkgo table entries; no behavioral changes expected.

internal/generator/vector/output/aws/s3/s3_test.go

clusterlogforwarder_webhook_test.goAdd unit tests for CLF webhook SAR enforcement +146/-0

Add unit tests for CLF webhook SAR enforcement

• Adds Ginkgo tests covering allowed/denied SAR outcomes, missing SA name bypass, update behavior, and propagation of UID/groups into the SAR. Uses a fake client wrapper to capture and control SAR results.

internal/webhook/clusterlogforwarder_webhook_test.go

sa_authorization_test.goAdd e2e test for CLF ServiceAccount authorization (CVE-2026-10609) +149/-0

Add e2e test for CLF ServiceAccount authorization (CVE-2026-10609)

• Adds end-to-end coverage that impersonates a restricted serviceaccount user to create a CLF referencing another SA. Verifies creation is rejected without 'use' on the SA and succeeds once a Role/RoleBinding grants 'use' for that ServiceAccount.

test/e2e/collection/sa_authorization/sa_authorization_test.go

suite_test.goAdd Ginkgo suite for SA authorization e2e tests +13/-0

Add Ginkgo suite for SA authorization e2e tests

• Registers a dedicated Ginkgo test suite for the new sa_authorization e2e package.

test/e2e/collection/sa_authorization/suite_test.go

Other (8) +142 / -9
cluster-logging-operator-webhook-service_v1_service.yamlAdd OLM bundle Service for webhook endpoint +16/-0

Add OLM bundle Service for webhook endpoint

• Introduces a Service exposing port 443 targeting the operator webhook port 9443. Adds OpenShift serving cert annotation to provision the serving certificate secret.

bundle/manifests/cluster-logging-operator-webhook-service_v1_service.yaml

cluster-logging.clusterserviceversion.yamlWire webhook into CSV (ports, cert mounts, webhookdefinition) +42/-2

Wire webhook into CSV (ports, cert mounts, webhookdefinition)

• Updates the operator deployment spec in the CSV to expose port 9443, mount the serving cert secret, and set resource requests/limits. Adds a ValidatingAdmissionWebhook definition for CLF CREATE/UPDATE routed to /validate-observability-openshift-io-v1-clusterlogforwarder.

bundle/manifests/cluster-logging.clusterserviceversion.yaml

kustomization.yamlEnable webhook resources and deployment patch in default kustomize +7/-6

Enable webhook resources and deployment patch in default kustomize

• Turns on the webhook kustomization and adds the manager webhook patch target for the operator Deployment. This ensures local/dev installs include the webhook wiring by default.

config/default/kustomization.yaml

manager_webhook_patch.yamlPatch operator Deployment for webhook port, cert mount, and resources +32/-0

Patch operator Deployment for webhook port, cert mount, and resources

• Adds container port 9443, read-only cert volume mount, and secret volume for serving certs. Also sets explicit CPU/memory requests and limits when webhook is enabled via kustomize.

config/default/manager_webhook_patch.yaml

kustomization.yamlAdd webhook kustomization entrypoint +3/-0

Add webhook kustomization entrypoint

• Defines webhook resources to be applied: ValidatingWebhookConfiguration and its Service.

config/webhook/kustomization.yaml

manifests.yamlAdd ValidatingWebhookConfiguration for ClusterLogForwarder +27/-0

Add ValidatingWebhookConfiguration for ClusterLogForwarder

• Registers a validating webhook for observability.openshift.io/v1 ClusterLogForwarder CREATE/UPDATE operations. Enables CA bundle injection via OpenShift annotation and sets failurePolicy=Fail.

config/webhook/manifests.yaml

service.yamlAdd webhook Service with serving cert annotation +14/-0

Add webhook Service with serving cert annotation

• Creates the in-cluster Service used by the API server to reach the operator webhook. Requests an OpenShift serving certificate secret named webhook-server-cert.

config/webhook/service.yaml

run-linterMake linter script portable by switching to env bash shebang +1/-1

Make linter script portable by switching to env bash shebang

• Replaces /usr/bin/sh with /usr/bin/env bash to improve portability across environments that may not have sh at that path.

hack/run-linter

@vparfonov

Copy link
Copy Markdown
Contributor Author

/hold

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Aug 4, 2026
@qodo-for-rh-openshift

qodo-for-rh-openshift Bot commented Aug 4, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 9 rules

Grey Divider


Remediation recommended

1. Owner annotation overwritten ⊘ Outdated 🐞 Bug ◔ Observability
Description
The mutating webhook unconditionally overwrites observability.openshift.io/resource-owner on every
CREATE and UPDATE, so it will track the last editor rather than a stable creator/owner identity.
This makes the annotation semantics ambiguous (and potentially misleading) for audit/provenance
consumers.
Code

internal/webhook/clusterlogforwarder_webhook.go[R53-56]

+	if clf.Annotations == nil {
+		clf.Annotations = make(map[string]string)
+	}
+	clf.Annotations[constants.AnnotationResourceOwner] = utilsjson.MustMarshal(req.UserInfo)
Relevance

●● Moderate

Semantic change: “owner” could mean last modifier; no clear precedent on create-only vs update
behavior.

PR-#3133

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The webhook is configured to run on both create and update, and the defaulter overwrites the
annotation value each time. The annotation key name uses resource-owner, while tests refer to it
as a modifier annotation, demonstrating the semantic mismatch.

internal/webhook/clusterlogforwarder_webhook.go[18-19]
internal/webhook/clusterlogforwarder_webhook.go[38-59]
internal/constants/annotations.go[14-18]
test/e2e/collection/sa_authorization/sa_authorization_test.go[103-120]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`observability.openshift.io/resource-owner` is set by the mutating webhook on both CREATE and UPDATE, which means the value changes over time and represents the last modifier rather than a stable resource “owner/creator”. The annotation name (`resource-owner`) conflicts with this behavior and can mislead auditing and any future policy logic that expects creator semantics.

### Issue Context
- The mutating webhook is registered for `verbs=create;update`.
- The defaulter always assigns `AnnotationResourceOwner` from the current admission `UserInfo`.
- The e2e test text calls it a “modifier annotation”, while the constant name is `AnnotationResourceOwner` and the key string is `.../resource-owner`.

### Fix Focus Areas
- internal/webhook/clusterlogforwarder_webhook.go[18-19]
- internal/webhook/clusterlogforwarder_webhook.go[38-59]
- internal/constants/annotations.go[14-18]
- test/e2e/collection/sa_authorization/sa_authorization_test.go[103-120]

### Suggested direction
Pick one contract and make code/docs/tests match:
1) **Creator/owner semantics:** only set `resource-owner` on CREATE (or only if absent), and optionally introduce a separate `resource-modifier` annotation updated on UPDATE.
2) **Last-modifier semantics:** rename the constant/key to something like `observability.openshift.io/resource-modifier` (or similar), and update comments/tests accordingly.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread internal/webhook/clusterlogforwarder_webhook.go Outdated
@vparfonov
vparfonov force-pushed the log9441 branch 2 times, most recently from a592b25 to beb14f7 Compare August 4, 2026 09:50
@vparfonov
vparfonov marked this pull request as draft August 4, 2026 09:59
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 4, 2026
@vparfonov
vparfonov marked this pull request as ready for review August 4, 2026 10:48
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 4, 2026
@qodo-for-rh-openshift

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 7552c0d

@vparfonov
vparfonov force-pushed the log9441 branch 2 times, most recently from a5371ab to 5b796cc Compare August 4, 2026 16:15
…usage in CLF (LOG-9441)

Add a ValidatingAdmissionWebhook that performs a SubjectAccessReview to
verify the requesting user has 'use' permission on the ServiceAccount
referenced in a ClusterLogForwarder. This prevents unauthorized users
from forwarding SA tokens to external log outputs.

Signed-off-by: Vitalii Parfonov <vparfono@redhat.com>
@openshift-ci

openshift-ci Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@vparfonov: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants