Skip to content

sdk: updateApiKey patch data can override the target ID #76

Open

Description

@sweetcornna

What happened

The SDK builds the update request as { id, ...patch }, so an id property present on a runtime patch replaces the method's explicit target argument.

Reproduction:

  1. Create API keys intended and victim.
  2. Store a patch variable containing id: victim.id and disabled: true.
  3. Call updateApiKey(intended.id, patch).
  4. Inspect the request body and both credentials.

Expected: The first method argument should remain the stable target, and patch data should affect only editable fields.

Actual: The wire request targets the victim ID, the response identifies the victim, the victim is disabled, and the intended key remains enabled. TypeScript accepts an ID-bearing patch variable through structural typing.

Impact: An administrative caller intending to rename, expire, or disable one credential can mutate another credential instead. This is a targeting failure, not a privilege escalation.

dor doctor output

Not applicable — the targeting failure was reproduced through the SDK request and focused server behavior and does not depend on an installed-host diagnosis. dor doctor was not run.

Environment

  • Reproduction mode: Focused-source reproduction
  • Dormice commit: 4417ae14938273f5d937c476cf7cdc57e22e53a5
  • Verification: Verified through the current SDK source and real server behavior at upstream main 4417ae14938273f5d937c476cf7cdc57e22e53a5.

Activity

  1. bluzername commented on Sep 15, 2026

    @bluzername

    The closed PR #88 had these notes on this. Moving them here so they are not lost, as asked.

    • Cause: in packages/sdk/src/client.ts, updateApiKey build the request as this.rpc('updateApiKey', { id, ...patch }). patch spread after id, so an id field inside patch win over the real target id.
    • Fix: swap the order to { ...patch, id }, so id always come from the method's own argument, never from patch.
    • Verification: new test "updateApiKey ignores an id smuggled inside the patch object" in client.test.ts. It create two real keys over the in-memory server, call updateApiKey(intended.id, { id: victim.id, disabled: true }), and check the intended key get disabled while the victim key stay untouched. Fails before the fix, passes after.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions