Skip to content

fix: prevent API keys from minting elevated-scope keys - #167

Open
Oluwasegun6921 wants to merge 3 commits into
determined-001:mainfrom
Oluwasegun6921:security/issue-159-6-6-an-api-key-can-mint-itself-a-more-powerful
Open

fix: prevent API keys from minting elevated-scope keys#167
Oluwasegun6921 wants to merge 3 commits into
determined-001:mainfrom
Oluwasegun6921:security/issue-159-6-6-an-api-key-can-mint-itself-a-more-powerful

Conversation

@Oluwasegun6921

Copy link
Copy Markdown

Overview

This PR closes a privilege-escalation vulnerability in the API-key creation flow. The apps/api/src/routes/api-keys.ts router is gated on api-keys:manage, then blindly issues a key carrying whatever scopes array the caller supplies. Because nothing checked that the requested scopes are a subset of the calling key's scopes, any combination of ALL_SCOPES was accepted — including offramp:initiate. A deliberately narrow integration key (e.g. scoped only to api-keys:manage for credential rotation) was therefore effectively a root key. The escalation reaches money: POST /links/:id/cash-out accepts a caller-supplied payoutFields object that overrides the seller's saved payout destination for that cash-out with no re-confirmation, so a leaked narrow key could self-issue offramp:initiate and redirect proceeds.

This PR enforces the subset rule: API-key-authenticated callers can only mint keys whose scopes are a subset of their own; any requested scope the calling key does not hold is rejected with 403 and the offending scopes named. Wallet-session callers remain the authority and can still request any scope.

Related Issue

Closes issue 6.6 — An API key can mint itself a more powerful key (tracked in ISSUES.md)

Changes

🔐 Subset Scope Enforcement

  • [MODIFY] apps/api/src/routes/api-keys.ts
    • Create-key handler now inspects the auth principal type exposed by the auth middleware.
    • When the caller is authenticated by API key, requested scopes are checked against the calling key's scopes; any scope the caller does not hold triggers 403 and names the offending scopes.
    • When the caller is authenticated by browser session, behavior is unchanged — any scope in ALL_SCOPES is accepted.

🧭 Auth Principal Distinction

  • [MODIFY] apps/api/src/middleware/auth.ts
    • Auth variables now clearly distinguish API-key authentication from browser-session authentication, so the route handler can apply the subset rule only to key-authenticated callers.

🛡️ Scope Validation Hardening

  • [MODIFY] apps/api/src/services/api-keys.ts
    • Central scope validation retained for well-formedness / ALL_SCOPES membership.
    • Documented the subset rule so any future key-update path applies the same restriction.

🧪 Regression Tests

  • [ADD] apps/api/src/routes/__tests__/api-keys.test.ts
    • Narrow key requesting a broader scope (offramp:initiate) → 403 with offending scopes named.
    • Narrow key requesting equal-or-narrower scopes → succeeds.
    • Session-authenticated caller requesting any scope → unaffected.

Verification Results

npm test -- apps/api/src/routes/__tests__/api-keys.test.ts
✅ 6/6 passed

Live acceptance check:
✅ Key without offramp:initiate creating a key with it → 403, scopes named
✅ Key without offramp:initiate creating equal-or-narrower key → 201
✅ Wallet-session caller issuing any scope → 201
✅ Cash-out payoutFields override no longer reachable from leaked narrow key
Acceptance Criteria Status
A key without offramp:initiate cannot create a key that has it 403 returned, offending scopes named in response
A wallet-session caller can still issue any scope they own ✅ Subset check bypassed for session-authenticated callers
Regression test covers both arms ✅ API-key and session auth paths covered in api-keys.test.ts

Closes #159

@drips-wave

drips-wave Bot commented Aug 28, 2026

Copy link
Copy Markdown

@Oluwasegun6921 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
quay-web Skipped Skipped Aug 28, 2026 1:16pm

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.

6.6 - An API key can mint itself a more powerful key

1 participant