Repository navigation
sec(auth): execute:ri-exchange missing from adminCarvedOuts; admin:* bypasses every exchange guardrail #1644
Description
Activity
- addedtriagedItem has been triagedItem has been triagedpriority/p1Next up; this sprintNext up; this sprintseverity/highSignificant harmSignificant harmurgency/this-sprintWithin the current sprintWithin the current sprintimpact/manyAffects most usersAffects most userseffort/sHoursHourstype/securitySecurity findingSecurity finding
on Jul 28, 2026 Note from the #1550 work (PR #1737, now open): this is a one-line change once that PR lands, and it will then be covered end to end automatically.
PR #1737 adds a grant ceiling to the group-permission write paths. Two things there make this issue smaller than it currently looks:
-
grantCeilingAllows(internal/auth/group_ceiling.go) re-checksadminCarvedOutsinside itsadmin:*branch, deliberately mirroringpermissionsAllowbranch for branch. So adding{ActionExecute, ResourceRIExchange}toadminCarvedOutsis picked up by both the enforcement path and the new grant ceiling from the same edit; there is no second place to remember. -
checkGrantCeilingrefuses to grant any pair inadminCarvedOutsat all, so the momentexecute:ri-exchangejoins that map, an admin also stops being able to write it onto a group to route around the carve-out. That closes the permission-administration bypass for this verb at the same time, without a separate change.
Worth confirming before making the edit, since it is the substantive question this issue turns on and #1737 does not answer it:
ResourceRIExchange's doc comment ininternal/auth/types.gosays RI exchanges are financially irreversible because the AWS API has no rollback once submitted, which is the same argument #923 used for the three purchase verbs. If that reasoning holds, the omission looks like an oversight rather than a decision. But adding it toadminCarvedOutsmeans an admin holding onlyadmin:*can no longer execute an RI exchange, and every current admin would need explicit membership in a group granting the verb — the same operational step #923 imposed for purchases. That is a policy call, not a mechanical one.One consequence to weigh:
execute:ri-exchangeis constraint-gated viarequirePermissionConstraints, unlike the purchase verbs' usual path. PR #1737's ceiling handles constraint containment (a holder capped atMaxPurchaseAmount: 100cannot hand out an uncapped grant), so that dimension is already covered whichever way this goes.-
- added 5 commits that reference this issue
on Aug 8, 2026 - added 4 commits that reference this issue
on Aug 8, 2026
Summary
execute:ri-exchangeis missing fromadminCarvedOuts(internal/auth/types.go), which lists only{execute, purchases},{approve_any, purchases}and{retry_any, purchases}. Because of that omission,permissionsAllow(internal/auth/service_group.go) returns true unconditionally for anadmin:*holder onexecute:ri-exchange, bypassing the account, provider, service and region dimensions and the spend cap.Why this matters now
This is pre-existing, but it has just become load-bearing. The Azure RI exchange execute work (issue #596 / PR #1515) extends the verb's blast radius from AWS-only to Azure, on an operation the code itself repeatedly describes as financially irreversible. Every guardrail added on that path - including the source-region fix in PR #1515 - is skipped for an admin-wildcard holder.
The separation-of-duties rationale that put
execute:purchasesin the carve-out list (#923) applies at least as strongly here: an RI exchange consumes existing commitments and buys replacements, and cannot be undone.Fix direction
Add
{execute, ri-exchange}toadminCarvedOutsso an admin must hold an explicit, constrainedexecute:ri-exchangegrant like everyone else. Check for callers/tests that rely on the wildcard shortcut before landing, and mirror whatever migration/communication #923 used forexecute:purchases.Found during adversarial review of PR #1515. Out of scope for that PR.