You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
sdk: updateApiKey patch data can override the target ID #76
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:
Create API keys intended and victim.
Store a patch variable containing id: victim.id and disabled: true.
Call updateApiKey(intended.id, patch).
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.
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.
Activity
bluzername commentedon Sep 15, 2026
The closed PR #88 had these notes on this. Moving them here so they are not lost, as asked.
packages/sdk/src/client.ts,updateApiKeybuild the request asthis.rpc('updateApiKey', { id, ...patch }).patchspread afterid, so anidfield insidepatchwin over the real target id.{ ...patch, id }, soidalways come from the method's own argument, never frompatch.client.test.ts. It create two real keys over the in-memory server, callupdateApiKey(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.