Skip to content

fix: prevent cross-namespace secret read via DmsAPIKeySecretRef#342

Open
anispate wants to merge 2 commits into
openshift:masterfrom
anispate:fix/rosaeng-61327-minimal
Open

fix: prevent cross-namespace secret read via DmsAPIKeySecretRef#342
anispate wants to merge 2 commits into
openshift:masterfrom
anispate:fix/rosaeng-61327-minimal

Conversation

@anispate

@anispate anispate commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes ROSAENG-61327 with a minimal, targeted change.

DmsAPIKeySecretRef is a corev1.SecretReference (Name + Namespace). The reconciler was passing the CR-supplied Namespace directly to utils.LoadSecretData, which uses the operator's cluster-wide secret-read privilege. Any principal with create on DeadmansSnitchIntegration could set DmsAPIKeySecretRef.Namespace to point at any namespace (e.g. hive credentials, cloud-provider creds) and have the operator transmit that secret to api.deadmanssnitch.com — a confused-deputy attack (CWE-441, CWE-639, CWE-200, CVSS 7.7).

Fix: one line in the controller — pass config.OperatorNamespace to LoadSecretData instead of the CR-supplied namespace. All production DMSI CRs already set namespace: deadmanssnitch-operator, so there is no behaviour change for legitimate use.

Changes

  • controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller.go — use config.OperatorNamespace (1 line)
  • controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller_test.go — add TestCrossNamespaceSecretRefRejected: places the secret only in a foreign namespace, asserts reconcile returns NotFound, proving the cross-namespace read path is closed

Test plan

  • go test ./controllers/deadmanssnitchintegration/... — all existing tests pass, new security regression test passes
  • No behaviour change for production DMSI CRs (all set dmsAPIKeySecretRef.namespace: deadmanssnitch-operator)

Fixes: ROSAENG-61327

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved how API key secrets are resolved so the operator now uses its own namespace, preventing accidental access to secrets from other namespaces.
    • Added validation coverage to ensure cross-namespace secret references are rejected during reconciliation.
  • Documentation

    • Added repository guidance covering setup, validation, architecture, testing, deployment options, and contribution practices.

DeadmansSnitchIntegration.Spec.DmsAPIKeySecretRef is a SecretReference
with both Name and Namespace. The reconciler was passing the CR-supplied
Namespace directly to utils.LoadSecretData, which runs a cluster-wide Get
using the operator's elevated secret-read privilege. Any principal with
create on DeadmansSnitchIntegration could point Namespace at any namespace
and have the operator transmit that secret to api.deadmanssnitch.com
(CWE-441 confused-deputy, CWE-639, CWE-200 — CVSS 7.7).

Fix: always pass config.OperatorNamespace to LoadSecretData, ignoring
whatever namespace the CR author specifies. All production DMSI CRs
already set namespace: deadmanssnitch-operator so there is no behaviour
change for legitimate use.

Adds TestCrossNamespaceSecretRefRejected: secret only present in a foreign
namespace, asserts reconcile returns NotFound, proving the cross-namespace
read path is closed.

Fixes: ROSAENG-61327

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Walkthrough

This PR restricts the DeadmansSnitchIntegration controller to always read the DMS API key secret from the operator's own namespace instead of the namespace specified in the CR spec, addressing a confused-deputy risk, and adds a corresponding regression test. A new CLAUDE.md documentation file is also added.

Changes

Cross-namespace secret restriction

Layer / File(s) Summary
Restrict API key secret lookup to operator namespace
controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller.go, controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller_test.go
Reconcile now loads the DMS API key secret using config.OperatorNamespace instead of the CR-specified namespace, with comments explaining the confused-deputy risk; a new test verifies reconciliation fails with a NotFound error when the secret ref points to a foreign namespace.

Repository documentation

Layer / File(s) Summary
CLAUDE.md guidance document
CLAUDE.md
Adds repository guidance covering overview, build/test commands, architecture, key packages, testing, code generation, deployment, and boilerplate usage rules.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Reconciler
  participant OperatorNamespaceSecrets
  participant ForeignNamespaceSecrets
  Reconciler->>OperatorNamespaceSecrets: LoadSecretData(config.OperatorNamespace)
  alt secret found in operator namespace
    OperatorNamespaceSecrets-->>Reconciler: Return secret data
  else secret only in foreign namespace
    OperatorNamespaceSecrets-->>Reconciler: NotFound error
    Reconciler-->>Reconciler: Fail reconciliation
  end
Loading

Suggested reviewers: rafael-azevedo, robotmaxtron

🚥 Pre-merge checks | ✅ 15
✅ Passed checks (15 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 summarizes the main security fix preventing cross-namespace secret reads via DmsAPIKeySecretRef.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 PASS: The only new test name is static Go TestCrossNamespaceSecretRefRejected; no Ginkgo It/Describe/Context/When titles or dynamic subtest names were added.
Test Structure And Quality ✅ Passed The new test is single-purpose, uses the repo’s fake-client/mock cleanup pattern, has no waits/timeouts, and includes explicit assertion messages.
Microshift Test Compatibility ✅ Passed The added test is a plain Go unit test (testing.T), not a Ginkgo e2e test, and it uses a fake client rather than MicroShift-sensitive cluster APIs.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The added test is a fake-client unit test, not a Ginkgo e2e test, and it makes no multi-node or topology assumptions.
Topology-Aware Scheduling Compatibility ✅ Passed Diff only changes secret lookup namespace plus a security test and CLAUDE.md; no replicas, affinities, node selectors, tolerations, or topology logic added.
Ote Binary Stdout Contract ✅ Passed HEAD diff is docs-only (CLAUDE.md); no main/init/TestMain/suite code changed, so no new stdout-contract risk.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo/e2e tests were added; the new unit test uses fake clients and has no IPv4 assumptions or external connectivity.
No-Weak-Crypto ✅ Passed No weak-crypto primitives, custom crypto, or insecure secret/token comparisons appear in the PR files; the change only adjusts secret namespace handling.
Container-Privileges ✅ Passed PR only changes docs/controller/test; no container/K8s manifests or privileged/host* / SYS_ADMIN / allowPrivilegeEscalation settings were introduced.
No-Sensitive-Data-In-Logs ✅ Passed The PR adds no new logging of secrets or PII; the controller change only alters secret lookup namespace and the test adds no logs.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@openshift-ci

openshift-ci Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: anispate

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

The pull request process is described 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

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 7, 2026
@codecov-commenter

codecov-commenter commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 43.76%. Comparing base (7f8a2d6) to head (254c22f).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #342      +/-   ##
==========================================
+ Coverage   43.28%   43.76%   +0.47%     
==========================================
  Files          11       11              
  Lines         834      834              
==========================================
+ Hits          361      365       +4     
+ Misses        424      422       -2     
+ Partials       49       47       -2     
Files with missing lines Coverage Δ
...ntegration/deadmanssnitchintegration_controller.go 63.14% <100.00%> (+0.60%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The agentic-sdlc-check pipeline (ROSA-730) validates that CLAUDE.md
contains build/test commands and architecture sections directly in the
file. The previous content was just '@AGENTS.md' (a Claude Code include
directive not readable by shell scripts). Inline the content from
AGENTS.md so both Claude Code and SDLC tooling can find the required
sections.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

@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.

🧹 Nitpick comments (1)
controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller.go (1)

101-106: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Correct fix for the confused-deputy vulnerability.

Forcing the lookup to always use config.OperatorNamespace instead of the CR-supplied dmsi.Spec.DmsAPIKeySecretRef.Namespace correctly closes the cross-namespace secret read. LoadSecretData fetches a Secret by the provided namespace/name and returns the specified data key, so passing the fixed operator namespace guarantees a CR author can no longer point at an arbitrary namespace to exfiltrate secrets.

One residual concern: DmsAPIKeySecretRef.Namespace remains a settable field on the CRD spec but is now silently ignored. A CR author who sets it to something other than the operator namespace will get a confusing NotFound error with no indication that the field is disregarded. Consider updating the field's godoc comment (in api/v1alpha1/deadmanssnitchintegration_types.go) to state that Namespace is ignored/must match the operator namespace, and/or logging a warning when a mismatch is detected, to aid future debugging.

📝 Example: clarify ignored field + warn on mismatch
 	// Always read from the operator's own namespace regardless of what DmsAPIKeySecretRef.Namespace
 	// says. Using the CR-supplied namespace would allow any principal with create on
 	// DeadmansSnitchIntegration to exfiltrate arbitrary Secrets via the operator's
 	// cluster-wide secret-read privilege (CWE-441 confused-deputy, ROSAENG-61327).
+	if dmsi.Spec.DmsAPIKeySecretRef.Namespace != "" && dmsi.Spec.DmsAPIKeySecretRef.Namespace != config.OperatorNamespace {
+		reqLogger.Info("DmsAPIKeySecretRef.Namespace is ignored; secret is always read from the operator namespace",
+			"specifiedNamespace", dmsi.Spec.DmsAPIKeySecretRef.Namespace, "operatorNamespace", config.OperatorNamespace)
+	}
 	dmsAPIKey, err := utils.LoadSecretData(r.Client, dmsi.Spec.DmsAPIKeySecretRef.Name,
 		config.OperatorNamespace, deadMansSnitchAPISecretKey)
🤖 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
`@controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller.go`
around lines 101 - 106, The secret lookup fix in the deadmanssnitchintegration
controller should keep using config.OperatorNamespace in LoadSecretData, but the
CR field DmsAPIKeySecretRef.Namespace is now silently ignored. Update the
DmsAPIKeySecretRef godoc in deadmanssnitchintegration_types.go to clearly say
Namespace is ignored/must match the operator namespace, and consider adding a
warning in reconcile when dmsi.Spec.DmsAPIKeySecretRef.Namespace differs so
users get a clear signal instead of a confusing NotFound error.
🤖 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.

Nitpick comments:
In
`@controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller.go`:
- Around line 101-106: The secret lookup fix in the deadmanssnitchintegration
controller should keep using config.OperatorNamespace in LoadSecretData, but the
CR field DmsAPIKeySecretRef.Namespace is now silently ignored. Update the
DmsAPIKeySecretRef godoc in deadmanssnitchintegration_types.go to clearly say
Namespace is ignored/must match the operator namespace, and consider adding a
warning in reconcile when dmsi.Spec.DmsAPIKeySecretRef.Namespace differs so
users get a clear signal instead of a confusing NotFound error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 8ef58ebd-f306-4805-91a9-5c448527bc7e

📥 Commits

Reviewing files that changed from the base of the PR and between 7f8a2d6 and 254c22f.

📒 Files selected for processing (3)
  • CLAUDE.md
  • controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller.go
  • controllers/deadmanssnitchintegration/deadmanssnitchintegration_controller_test.go

@openshift-ci

openshift-ci Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

@anispate: 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.

Comment thread CLAUDE.md
@@ -1 +1,91 @@
@AGENTS.md

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The flow this repo already uses is to put all context into AGENTS.md and then use this @AGENTS.md to inform Claude Code to consume AGENTS.md.

The information you've added here to CLAUDE.md is duplicating the information in AGENTS.md and will only cause toil to maintain and additional token use.

Suggestion is to remove this whole change to CLAUDE.md, its unnecessary.

@tiwillia

tiwillia commented Jul 8, 2026

Copy link
Copy Markdown
Member

The test and fix look correct. I verified the test fails without the fix accurately and succeeds with the fix.

The change to CLAUDE.md is incorrect though. Suggest to remove it, then merge.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants