Skip to content

fix(auth): honor admin carve-outs on bearer-session permission checks - #1492

Merged
cristim merged 1 commit into
mainfrom
fix/1454-bearer-admin-carveout
Jul 22, 2026
Merged

cristim merged 1 commit into
mainfrom
fix/1454-bearer-admin-carveout

Conversation

@cristim

@cristim cristim commented Jul 22, 2026

Copy link
Copy Markdown
Member

Summary

Fixes a security bug where the bearer-session permission-check path (Service.HasPermission / permissionsAllow in internal/auth/service_group.go) granted the three money-spending verbs carved out for separation of duties (execute:purchases, approve-any:purchases, retry-any:purchases; see #923) to any holder of the admin:* wildcard permission, unconditionally.

AuthContext.HasPermission (internal/auth/types.go) already enforced this carve-out via the adminCarvedOuts map, falling through to the explicit-permission check for those three verbs instead of short-circuiting on admin:*. The bearer-session path (permissionsAllow) never got the same treatment, so the two authorization paths disagreed: a bare Administrators-group member (no explicit Purchaser-group grant) was correctly denied execute:purchases via AuthContext.HasPermission but incorrectly allowed it via Service.HasPermission/permissionsAllow.

Fix

Wire permissionsAllow through the same adminCarvedOuts map so it mirrors AuthContext.HasPermission's logic exactly: admin:* still short-circuits to allow for everything except the three carved-out verbs, for which it falls through to the explicit-permission check below (fails closed, never defaults to allow).

Also updates TestGroupOnlyAuthz_AdminEquivalence, a pre-existing test that asserted the old (buggy) behavior — that a bare admin holds execute:purchases and approve-any:purchases — to assert the correct carve-out semantics instead, consistent with TestAdminWildcardCarveOuts in types_test.go.

Note on issue linkage

This PR was scoped as closing "#1454", but verification shows GitHub #1454 is an already-merged, unrelated PR (fix(api): gate empty-account history rows on ownership + enforce API-key constraints on money paths). No open issue matching this specific bearer-session/AuthContext inconsistency was found in a search of open issues or the adversarial-review tracker (#1448). Flagging this so the correct tracking issue can be confirmed or filed; not claiming a closing reference here to avoid a false link.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • go test ./internal/auth/... — 603 passed
  • go test ./... (full repo) — 5900 passed, 38 packages
  • golangci-lint v2.10.1 (CI-pinned) on internal/auth/... — 0 issues
  • gocyclo -over 10 on changed files — clean
  • Regression tests (TestService_HasPermission/admin-only_user_is_denied_the_carved-out_purchase_verb, TestService_HasPermissionForConstraintsAPI/admin-only_user_is_denied_execute:purchases..., TestGroupOnlyAuthz_AdminEquivalence) confirmed to fail against the pre-fix code and pass after restoring the fix

Service.HasPermission (the bearer-session auth path) granted the three
money-spending verbs carved out for separation of duties (execute,
approve-any, and retry-any on purchases; issue #923) to any admin:*
holder, unconditionally. AuthContext.HasPermission already enforced
the carve-out, so the two paths disagreed: a bare Administrators-group
member could execute or approve-any purchases through the bearer path
while being correctly denied through the auth-context path.

Wire permissionsAllow through the same adminCarvedOuts map so admin
falls through to the explicit-permission check for the carved-out
verbs instead of short-circuiting, mirroring AuthContext.HasPermission.

Also updates TestGroupOnlyAuthz_AdminEquivalence, which asserted the
pre-fix (buggy) behavior for execute:purchases and approve-any:purchases,
to match the intended carve-out semantics.

Closes #1454.
@cristim cristim added type/security Security finding priority/p1 Next up; this sprint severity/high Significant harm impact/many Affects most users urgency/this-sprint Within the current sprint triaged Item has been triaged labels Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd778dc6-8abb-4b32-9a59-d618c34c770b

📥 Commits

Reviewing files that changed from the base of the PR and between f8cdea6 and 775d61d.

⛔ Files ignored due to path filters (1)
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • internal/auth/service_api_test.go
  • internal/auth/service_group.go
  • internal/auth/service_group_only_authz_test.go
  • internal/auth/service_group_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1454-bearer-admin-carveout

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

@cristim

cristim commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor
✅ 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.

@cristim
cristim merged commit 4be321d into main Jul 22, 2026
19 checks passed
@cristim
cristim deleted the fix/1454-bearer-admin-carveout branch July 27, 2026 11:09
cristim added a commit that referenced this pull request Sep 27, 2026
…#1492)

Service.HasPermission (the bearer-session auth path) granted the three
money-spending verbs carved out for separation of duties (execute,
approve-any, and retry-any on purchases; issue #923) to any admin:*
holder, unconditionally. AuthContext.HasPermission already enforced
the carve-out, so the two paths disagreed: a bare Administrators-group
member could execute or approve-any purchases through the bearer path
while being correctly denied through the auth-context path.

Wire permissionsAllow through the same adminCarvedOuts map so admin
falls through to the explicit-permission check for the carved-out
verbs instead of short-circuiting, mirroring AuthContext.HasPermission.

Also updates TestGroupOnlyAuthz_AdminEquivalence, which asserted the
pre-fix (buggy) behavior for execute:purchases and approve-any:purchases,
to match the intended carve-out semantics.

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

Labels

impact/many Affects most users priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/security Security finding urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant