Skip to content

OLS-3886 Tighten ClusterRole RBAC to prevent privilege escalation - #1954

Open
xrajesh wants to merge 1 commit into
openshift:mainfrom
xrajesh:OLS-3886/tighten-clusterrole-rbac
Open

OLS-3886 Tighten ClusterRole RBAC to prevent privilege escalation#1954
xrajesh wants to merge 1 commit into
openshift:mainfrom
xrajesh:OLS-3886/tighten-clusterrole-rbac

Conversation

@xrajesh

@xrajesh xrajesh commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Secrets: Moved unrestricted cluster-wide secrets access (create/delete/deletecollection/get/list/patch/update/watch) from ClusterRole to namespace-scoped Role (openshift-lightspeed). Only pull-secret read access (pinned by resourceNames) remains cluster-scoped for telemetry.
  • RBAC objects: Restricted clusterroles, clusterrolebindings, and rolebindings write access to specific resourceNames the operator manages (lightspeed-app-server-sar-role, lightspeed-agentic-alerts-adapter-*), eliminating the ability to bind cluster-admin or create arbitrary roles.
  • NetworkPolicies: Moved from ClusterRole to namespace-scoped Role — all network policies are created in openshift-lightspeed only.

Motivation

A compromised operator ServiceAccount token could previously:

  1. Read every secret cluster-wide (TLS certs, registry creds, SA tokens)
  2. Create a ClusterRoleBinding to cluster-admin for full cluster takeover
  3. Delete network policies in any namespace to bypass isolation

Test plan

  • make manifests regenerates config/rbac/role.yaml correctly
  • make test — all controller tests pass
  • Deploy to a dev cluster and verify OLSConfig reconciliation succeeds
  • Verify the operator can still read pull-secret from openshift-config
  • Verify the operator can still create its named ClusterRoles/Bindings
  • Verify network policies are created in openshift-lightspeed

Fixes: https://redhat.atlassian.net/browse/OLS-3886

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Security

    • Restricted access to Secrets and NetworkPolicies within the openshift-lightspeed namespace.
    • Limited permissions to specific required resources instead of broad cluster-wide access.
    • Removed obsolete, overly permissive access rules.
  • Tests

    • Added validation to ensure required access permissions and namespace restrictions remain enforced.

@openshift-ci

openshift-ci Bot commented Aug 13, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign raptorsun 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 commented Aug 13, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The controller RBAC markers now restrict Secret and NetworkPolicy access by namespace. Named ClusterRole, ClusterRoleBinding, and RoleBinding permissions use separate create rules. New tests validate the generated RBAC YAML.

Changes

RBAC permission scoping

Layer / File(s) Summary
Restrict operator RBAC markers
internal/controller/olsconfig_controller.go
Secret and NetworkPolicy permissions are limited to the operator namespace. Telemetry pull-secret access remains cluster-scoped by name. Named RBAC resources use separate create permissions.
Validate generated RBAC rules
internal/rbac/role_yaml_test.go
Tests decode the configured ClusterRole and Role and verify namespaces, resource names, verbs, and required Secret and NetworkPolicy access.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: 🟡 Moderate · up to 36868

This PR narrows the operator’s permissions to reduce privilege escalation, but the current tests can still pass if unauthorized resource names or extra verbs are granted. That creates a concrete risk that a future RBAC regression could restore broader access without failing CI; the PR is not merge-ready until the assertions are tightened or the risk is explicitly accepted.

Possibly related PRs

Suggested reviewers: joshuawilson, blublinsky

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: tightening ClusterRole RBAC permissions to prevent privilege escalation.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 4

🤖 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 `@internal/controller/olsconfig_controller.go`:
- Line 126: The RBAC rules for managed ClusterRole, ClusterRoleBinding, and the
named Secret must support the operations performed by SetupWithManager(),
including unfiltered Owns() cache list/watch and controller Create calls. Remove
resourceNames restrictions or replace the watches/API access with matching
name-filtered behavior, and ensure the rules grant the required scopes and verbs
while retaining the broad Secret permissions needed by the cache.
- Line 126: Replace the cluster-scoped RBAC markers near the pull-secret rule
and alerts-adapter RoleBinding rule in the OLSConfig controller with
namespace-scoped Roles and RoleBindings (or the repository’s equivalent
installation mechanism): grant pull-secret access only in openshift-config and
alerts-adapter access only in openshift-monitoring. Do not rely on resourceNames
to scope namespaces, and provision create permissions through the
namespace-scoped resources rather than ClusterRole rules.
- Around line 132-134: Update the clusterrolebindings RBAC marker near the
OLSConfig controller to include lightspeed-agentic-alerts-adapter-proposals in
resourceNames, then regenerate the RBAC manifests so manager-role includes the
permission. Leave the namespaced roles and rolebindings rule unchanged.
- Around line 131-134: The RBAC markers around the controller’s ClusterRole and
ClusterRoleBinding rules incorrectly use resourceNames with create, preventing
top-level creation. Replace this authorization design with a supported bootstrap
or pre-provisioning approach, then update the generated role.yaml and OLM CSV
permissions consistently so managed resource creation succeeds without
unrestricted masking access.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1bb34ed7-a61e-4f12-b05a-650c26188879

📥 Commits

Reviewing files that changed from the base of the PR and between aa65d8a and c8ef065.

⛔ Files ignored due to path filters (1)
  • config/rbac/role.yaml is excluded by !config/rbac/role.yaml
📒 Files selected for processing (1)
  • internal/controller/olsconfig_controller.go

Comment thread internal/controller/olsconfig_controller.go
Comment thread internal/controller/olsconfig_controller.go Outdated
Comment on lines +132 to +134
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles,resourceNames=lightspeed-app-server-sar-role;lightspeed-agentic-alerts-adapter-agenticruns;lightspeed-agentic-alerts-adapter-proposals,verbs=get;list;create;update;delete;watch
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterrolebindings,resourceNames=lightspeed-app-server-sar-role-binding;lightspeed-agentic-alerts-adapter-agenticruns,verbs=get;list;create;update;delete;watch
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,resourceNames=lightspeed-agentic-alerts-adapter-alertmanager,verbs=get;list;create;update;delete;watch

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 | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 \
  'removeLegacy|AlertsAdapter(LegacyProposalsClusterRoleName|ConfigRoleBindingName|ConfigRoleName)|resources=clusterrolebindings|resources=rolebindings|resources=roles' \
  internal/controller

Repository: openshift/lightspeed-operator

Length of output: 12953


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- project specification entry point ---'
if [ -f .ai/spec/README.md ]; then
  sed -n '1,180p' .ai/spec/README.md
fi

printf '%s\n' '--- controller RBAC markers ---'
sed -n '118,148p' internal/controller/olsconfig_controller.go

printf '%s\n' '--- alerts-adapter cleanup and reconciliation ---'
sed -n '1,70p' internal/controller/alertsadapter/reconciler.go
sed -n '300,355p' internal/controller/alertsadapter/reconciler.go
sed -n '455,510p' internal/controller/alertsadapter/reconciler.go

printf '%s\n' '--- generated RBAC and bundle references ---'
rg -n -C 3 \
  'lightspeed-agentic-alerts-adapter-(proposals|agenticruns|config|alertmanager)|name: (lightspeed-agentic-alerts-adapter|alerts-adapter-config)' \
  config bundle deploy manifests 2>/dev/null || true

printf '%s\n' '--- RBAC generation and relevant watches ---'
rg -n -C 3 \
  'SetupWithManager|Owns\(|ClusterRole|ClusterRoleBinding|RoleBinding|removeLegacyProposalsClusterRBAC|AlertsAdapter' \
  internal/controller config bundle Makefile 2>/dev/null | head -n 500

Repository: openshift/lightspeed-operator

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- generated manager Role rules ---'
sed -n '145,215p' config/rbac/role.yaml

printf '%s\n' '--- CSV permissions for RBAC resources ---'
rg -n -C 12 \
  'clusterroles|clusterrolebindings|rolebindings|lightspeed-agentic-alerts-adapter-(proposals|agenticruns|alertmanager)' \
  bundle/manifests/lightspeed-operator.clusterserviceversion.yaml \
  bundle/manifests/*role*.yaml 2>/dev/null || true

printf '%s\n' '--- complete alerts-adapter deletion paths ---'
rg -n -C 8 \
  'func (RemoveAlertsAdapter|delete|removeLegacy)|r\.(Get|Delete)\(.*(Role|Binding|ServiceAccount|Deployment|NetworkPolicy)' \
  internal/controller/alertsadapter

printf '%s\n' '--- relevant constants ---'
rg -n -C 2 \
  'AlertsAdapter.*(Role|Binding|ServiceAccount|Deployment|NetworkPolicy).*Name' \
  internal/controller/utils/constants.go

printf '%s\n' '--- marker-to-cleanup consistency probe ---'
python3 - <<'PY'
from pathlib import Path
import re

controller = Path("internal/controller/olsconfig_controller.go").read_text()
reconciler = Path("internal/controller/alertsadapter/reconciler.go").read_text()
generated = Path("config/rbac/role.yaml").read_text()

markers = {}
for resource, names, verbs in re.findall(
    r'resources=([^,]+),resourceNames=([^,]+),verbs=([^\n]+)',
    controller,
):
    markers[resource] = {
        "names": set(names.split(";")),
        "verbs": set(verbs.split(";")),
    }

cleanup = sorted(set(re.findall(
    r'utils\.(AlertsAdapter[A-Za-z0-9]+Name)',
    reconciler,
)))
print("cleanup constants:", cleanup)
for constant in cleanup:
    value = re.search(
        rf'{re.escape(constant)}\s*=\s*"([^"]+)"',
        Path("internal/controller/utils/constants.go").read_text(),
    )
    print(constant, "=>", value.group(1) if value else "UNRESOLVED")

for resource in ("clusterroles", "clusterrolebindings", "rolebindings"):
    print(resource, markers.get(resource))
PY

Repository: openshift/lightspeed-operator

Length of output: 37493


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- generated namespace-scoped rules ---'
rg -n -C 8 \
  'namespace: openshift-lightspeed|resources:|roles|rolebindings' \
  config/rbac/role.yaml | head -n 180

printf '%s\n' '--- bundle manifest inventory ---'
find bundle/manifests -maxdepth 1 -type f -printf '%f\n' | sort | rg -i \
  'role|clusterrole|operator|csv|serviceaccount'

printf '%s\n' '--- bundle RBAC objects and CSV permissions ---'
for f in bundle/manifests/*; do
  if rg -q 'kind: (Role|ClusterRole|RoleBinding|ClusterRoleBinding)|clusterrolebindings|clusterroles|rolebindings|roles' "$f"; then
    printf '\n### %s\n' "$f"
    rg -n -C 5 \
      'kind: (Role|ClusterRole|RoleBinding|ClusterRoleBinding)|namespace: openshift-lightspeed|clusterrolebindings|clusterroles|rolebindings|roles|resourceNames' \
      "$f" | head -n 220
  fi
done

printf '%s\n' '--- legacy cleanup test coverage ---'
rg -n -C 6 \
  'LegacyProposals|proposals ClusterRoleBinding|lightspeed-agentic-alerts-adapter-proposals' \
  internal/controller/alertsadapter/*_test.go

Repository: openshift/lightspeed-operator

Length of output: 10100


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- generated RBAC object and remaining rules ---'
sed -n '1,12p' config/rbac/role.yaml
sed -n '180,270p' config/rbac/role.yaml

printf '%s\n' '--- source marker and generated-rule comparison ---'
python3 - <<'PY'
from pathlib import Path
import re

source = Path("internal/controller/olsconfig_controller.go").read_text()
generated = Path("config/rbac/role.yaml").read_text()
csv = Path("bundle/manifests/lightspeed-operator.clusterserviceversion.yaml").read_text()

for resource in ("clusterroles", "clusterrolebindings", "rolebindings"):
    marker = re.search(
        rf'resources={resource},resourceNames=([^,]+),verbs=([^\n]+)',
        source,
    )
    print(f"{resource} marker:", marker.groups() if marker else None)

for name in (
    "lightspeed-agentic-alerts-adapter-proposals",
    "lightspeed-agentic-alerts-adapter-agenticruns",
    "lightspeed-agentic-alerts-adapter-config",
    "lightspeed-agentic-alerts-adapter-alertmanager",
):
    print(
        name,
        "generated=", name in generated,
        "csv=", name in csv,
    )

print("generated has a roles/rolebindings rule:",
      bool(re.search(r"resources:\n(?:  - [^\n]+\n)*  - roles\b", generated)))
PY

printf '%s\n' '--- exact legacy cleanup test references ---'
rg -n -C 5 \
  'removeLegacyProposalsClusterRBAC|AlertsAdapterLegacyProposalsClusterRoleName|proposals ClusterRoleBinding' \
  internal/controller/alertsadapter

Repository: openshift/lightspeed-operator

Length of output: 6601


Allow cleanup of the legacy proposals ClusterRoleBinding.

removeLegacyProposalsClusterRBAC accesses lightspeed-agentic-alerts-adapter-proposals as a ClusterRoleBinding, but the generated manager-role omits this name. Add it to the clusterrolebindings resourceNames list and regenerate the RBAC manifests. Keep the namespaced roles;rolebindings rule for legacy config resources. Without this permission, cleanup returns Forbidden and alerts-adapter reconciliation fails.

🤖 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/controller/olsconfig_controller.go` around lines 132 - 134, Update
the clusterrolebindings RBAC marker near the OLSConfig controller to include
lightspeed-agentic-alerts-adapter-proposals in resourceNames, then regenerate
the RBAC manifests so manager-role includes the permission. Leave the namespaced
roles and rolebindings rule unchanged.

@xrajesh
xrajesh force-pushed the OLS-3886/tighten-clusterrole-rbac branch from c8ef065 to d194f9a Compare August 13, 2026 03:38
- Move secrets and networkpolicies from ClusterRole to namespace-scoped
  Role with namespace=system placeholder (kustomize/OLM replaces at
  deploy time).
- Split RBAC resource rules: create without resourceNames (Kubernetes
  silently denies create when resourceNames is set), get/update/delete
  with resourceNames pinned to operator-managed resources.
- Add static RBAC validation tests that parse role.yaml and enforce
  these invariants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@xrajesh
xrajesh force-pushed the OLS-3886/tighten-clusterrole-rbac branch from d194f9a to 368684f Compare August 13, 2026 04:05

@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: 3

🤖 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 `@internal/rbac/role_yaml_test.go`:
- Around line 39-48: Strengthen TestClusterRoleNoUnrestrictedSecrets in
internal/rbac/role_yaml_test.go:39-48 to require exactly one Secret resource
name, pull-secret, and fail any other Secret name or unrestricted rule. In
internal/rbac/role_yaml_test.go:88-120, add the managed RoleBinding name to the
allowlist and validate every rolebindings resource name against it, rejecting
unauthorized names.
- Around line 130-150: The TestRoleContainsSecretsAndNetworkPolicies test only
checks resource presence; update it to collect and compare each resource’s exact
verb set against the expected namespaced Role contract, requiring
deletecollection for secrets and excluding it for networkpolicies. Ensure
missing, extra, or incorrect verbs fail the test.
- Around line 14-150: Convert the manifest tests in loadRoleYAML and the
TestClusterRole*/TestRole* functions from testing.T assertions to Ginkgo/Gomega,
adding the package suite setup and structuring each test as Ginkgo specs with
appropriate matchers. Keep the existing YAML loading and RBAC validation
coverage unchanged, omit shared OLSConfig fixtures, and verify the suite through
make test.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5ca53683-3d58-438a-9c71-d32bbbc08303

📥 Commits

Reviewing files that changed from the base of the PR and between d194f9a and 368684f.

⛔ Files ignored due to path filters (1)
  • config/rbac/role.yaml is excluded by !config/rbac/role.yaml
📒 Files selected for processing (2)
  • internal/controller/olsconfig_controller.go
  • internal/rbac/role_yaml_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/controller/olsconfig_controller.go

Comment on lines +14 to +150
func loadRoleYAML(t *testing.T) (rbacv1.ClusterRole, rbacv1.Role) {
t.Helper()
_, thisFile, _, _ := runtime.Caller(0)
roleFile := filepath.Join(filepath.Dir(thisFile), "..", "..", "config", "rbac", "role.yaml")

data, err := os.ReadFile(roleFile)
if err != nil {
t.Fatalf("reading role.yaml: %v", err)
}

decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(data), 4096)

var clusterRole rbacv1.ClusterRole
if err := decoder.Decode(&clusterRole); err != nil {
t.Fatalf("decoding ClusterRole: %v", err)
}

var role rbacv1.Role
if err := decoder.Decode(&role); err != nil {
t.Fatalf("decoding Role: %v", err)
}

return clusterRole, role
}

func TestClusterRoleNoUnrestrictedSecrets(t *testing.T) {
clusterRole, _ := loadRoleYAML(t)
for _, rule := range clusterRole.Rules {
for _, res := range rule.Resources {
if res == "secrets" && len(rule.ResourceNames) == 0 {
t.Error("ClusterRole grants secrets access without resourceNames restriction")
}
}
}
}

func TestClusterRoleNoClusterWideNetworkPolicies(t *testing.T) {
clusterRole, _ := loadRoleYAML(t)
for _, rule := range clusterRole.Rules {
for _, res := range rule.Resources {
if res == "networkpolicies" {
t.Error("ClusterRole grants networkpolicies access — should be in namespaced Role")
}
}
}
}

func TestClusterRoleNoCreateWithResourceNames(t *testing.T) {
rbacResources := map[string]bool{
"clusterroles": true,
"clusterrolebindings": true,
"rolebindings": true,
"roles": true,
}
clusterRole, _ := loadRoleYAML(t)
for _, rule := range clusterRole.Rules {
hasRBACResource := false
for _, res := range rule.Resources {
if rbacResources[res] {
hasRBACResource = true
break
}
}
if !hasRBACResource || len(rule.ResourceNames) == 0 {
continue
}
for _, verb := range rule.Verbs {
if verb == "create" {
t.Errorf("create verb combined with resourceNames %v is silently denied by Kubernetes RBAC", rule.ResourceNames)
}
}
}
}

func TestClusterRoleRBACResourceNamesAllowed(t *testing.T) {
allowedClusterRoles := map[string]bool{
"lightspeed-app-server-sar-role": true,
"lightspeed-agentic-alerts-adapter-agenticruns": true,
"lightspeed-agentic-alerts-adapter-proposals": true,
}
allowedCRBs := map[string]bool{
"lightspeed-app-server-sar-role-binding": true,
"lightspeed-agentic-alerts-adapter-agenticruns": true,
}

clusterRole, _ := loadRoleYAML(t)
for _, rule := range clusterRole.Rules {
if len(rule.ResourceNames) == 0 {
continue
}
for _, res := range rule.Resources {
var allowed map[string]bool
switch res {
case "clusterroles":
allowed = allowedClusterRoles
case "clusterrolebindings":
allowed = allowedCRBs
default:
continue
}
for _, name := range rule.ResourceNames {
if !allowed[name] {
t.Errorf("ClusterRole grants access to unexpected %s: %s", res, name)
}
}
}
}
}

func TestRoleUsesSystemNamespace(t *testing.T) {
_, role := loadRoleYAML(t)
if role.Namespace != "system" {
t.Errorf("Role namespace = %q, want 'system' placeholder", role.Namespace)
}
}

func TestRoleContainsSecretsAndNetworkPolicies(t *testing.T) {
_, role := loadRoleYAML(t)
hasSecrets := false
hasNP := false
for _, rule := range role.Rules {
for _, res := range rule.Resources {
if res == "secrets" {
hasSecrets = true
}
if res == "networkpolicies" {
hasNP = true
}
}
}
if !hasSecrets {
t.Error("namespaced Role must include secrets access")
}
if !hasNP {
t.Error("namespaced Role must include networkpolicies access")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C2 'github\.com/onsi/(ginkgo|gomega)' go.mod
fd -a 'suite_test.go' . -x sed -n '1,180p' {}

Repository: openshift/lightspeed-operator

Length of output: 41005


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- role_yaml_test.go ---'
cat -n internal/rbac/role_yaml_test.go

printf '%s\n' '--- rbac test files ---'
git ls-files 'internal/rbac/*_test.go' | while read -r f; do
  printf '\n[%s]\n' "$f"
  sed -n '1,80p' "$f"
done

printf '%s\n' '--- test targets ---'
rg -n -A12 -B3 '^(test|test-e2e):|ginkgo|go test' Makefile makefile* 2>/dev/null || true

printf '%s\n' '--- repository test conventions ---'
rg -n 'func Test[A-Za-z0-9_]+\(t \*testing\.T\)|RegisterFailHandler|RunSpecs' --glob '*_test.go' internal/rbac internal/controller | head -120

Repository: openshift/lightspeed-operator

Length of output: 14919


Convert internal/rbac/role_yaml_test.go to Ginkgo/Gomega.

Add the package suite and replace testing.T assertions with Gomega matchers. Run the tests through make test. Shared OLSConfig fixtures are not needed for manifest-only tests.

🤖 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/rbac/role_yaml_test.go` around lines 14 - 150, Convert the manifest
tests in loadRoleYAML and the TestClusterRole*/TestRole* functions from
testing.T assertions to Ginkgo/Gomega, adding the package suite setup and
structuring each test as Ginkgo specs with appropriate matchers. Keep the
existing YAML loading and RBAC validation coverage unchanged, omit shared
OLSConfig fixtures, and verify the suite through make test.

Source: Coding guidelines

Comment on lines +39 to +48
func TestClusterRoleNoUnrestrictedSecrets(t *testing.T) {
clusterRole, _ := loadRoleYAML(t)
for _, rule := range clusterRole.Rules {
for _, res := range rule.Resources {
if res == "secrets" && len(rule.ResourceNames) == 0 {
t.Error("ClusterRole grants secrets access without resourceNames restriction")
}
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Enforce complete RBAC resource-name allowlists.

The tests accept unauthorized named resources. A ClusterRole can grant a Secret other than pull-secret, or a named rolebindings permission outside the managed RoleBinding, without failing.

  • internal/rbac/role_yaml_test.go#L39-L48: require a Secret rule for exactly pull-secret and reject every other Secret resource name.
  • internal/rbac/role_yaml_test.go#L88-L120: add the managed RoleBinding name to an allowlist and validate rolebindings resource names.
📍 Affects 1 file
  • internal/rbac/role_yaml_test.go#L39-L48 (this comment)
  • internal/rbac/role_yaml_test.go#L88-L120
🤖 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/rbac/role_yaml_test.go` around lines 39 - 48, Strengthen
TestClusterRoleNoUnrestrictedSecrets in internal/rbac/role_yaml_test.go:39-48 to
require exactly one Secret resource name, pull-secret, and fail any other Secret
name or unrestricted rule. In internal/rbac/role_yaml_test.go:88-120, add the
managed RoleBinding name to the allowlist and validate every rolebindings
resource name against it, rejecting unauthorized names.

Comment on lines +130 to +150
func TestRoleContainsSecretsAndNetworkPolicies(t *testing.T) {
_, role := loadRoleYAML(t)
hasSecrets := false
hasNP := false
for _, rule := range role.Rules {
for _, res := range rule.Resources {
if res == "secrets" {
hasSecrets = true
}
if res == "networkpolicies" {
hasNP = true
}
}
}
if !hasSecrets {
t.Error("namespaced Role must include secrets access")
}
if !hasNP {
t.Error("namespaced Role must include networkpolicies access")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Assert the exact namespaced Role verb sets.

The test passes when the Role grants only get, or grants extra verbs. Both cases violate the expected RBAC contract. Assert exact verbs for secrets and networkpolicies, including deletecollection only for secrets.

🤖 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/rbac/role_yaml_test.go` around lines 130 - 150, The
TestRoleContainsSecretsAndNetworkPolicies test only checks resource presence;
update it to collect and compare each resource’s exact verb set against the
expected namespaced Role contract, requiring deletecollection for secrets and
excluding it for networkpolicies. Ensure missing, extra, or incorrect verbs fail
the test.

@openshift-ci

openshift-ci Bot commented Aug 13, 2026

Copy link
Copy Markdown

@xrajesh: 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant