Locations
Summary
A validator-key removal is bound to a cached numeric position rather than the pubkey the manager selected. An ordered earlier removal can therefore make a still-valid stale transaction delete a different key.
Root cause
The removal workflow filters a cached key array and converts the selected offset into totalDepositedKeys + start. It passes the selected pubkeys to the SDK, but they are not encoded in calldata, and neither validation nor submission refreshes the inventory or verifies that the current range still contains those pubkeys.
- Inventory queries may remain stale for ten minutes and do not refresh on focus or reconnect.
- The contract checks only manager authority, the deposited prefix, and the current numeric range.
- Removing a non-tail key compacts the last key into the removed position.
Impact
A manager can authorize removal of key B in two concurrent sessions yet have the later stale transaction remove key C. In CSM, the unintended successful removal also incurs another configured bond-funded removal charge; recovery requires retaining C's deposit material and re-adding it, potentially after losing queue placement.
This is low severity because both transactions require the operator manager's authorization, the blast radius is limited to that operator, and the key can generally be re-added. Nevertheless, the resulting on-chain mutation does not match the reviewed key identity, and the extra removal charge is not automatically recoverable.
Scenario
- The cached inventory is
[D0,D1,A,B,C], with the first two keys deposited.
- A manager selects B; the widget constructs
removeKeys(nodeOperatorId,3,1).
- Another session submits an intentional B-removal as the predecessor transaction.
- EOA nonce order, multisig nonce order, or smart-account operation order executes that removal first, producing
[D0,D1,A,C].
- The stale transaction remains range-valid and removes the key now at index 3, which is C.
- The widget reports that one key was removed without comparing the emitted pubkey to B, while C must be re-added and the CSM removal-charge path has executed a second time.
Drafted from LidoLens finding RES-03
Locations
csm-widget/features/remove-keys/remove-keys/context/remove-keys-data-provider.tsx:30-40csm-widget/features/remove-keys/remove-keys/context/use-remove-keys-flow.ts:35-46csm-widget/consts/react-query-strategies.ts:7-12Summary
A validator-key removal is bound to a cached numeric position rather than the pubkey the manager selected. An ordered earlier removal can therefore make a still-valid stale transaction delete a different key.
Root cause
The removal workflow filters a cached key array and converts the selected offset into
totalDepositedKeys + start. It passes the selected pubkeys to the SDK, but they are not encoded in calldata, and neither validation nor submission refreshes the inventory or verifies that the current range still contains those pubkeys.Impact
A manager can authorize removal of key B in two concurrent sessions yet have the later stale transaction remove key C. In CSM, the unintended successful removal also incurs another configured bond-funded removal charge; recovery requires retaining C's deposit material and re-adding it, potentially after losing queue placement.
This is low severity because both transactions require the operator manager's authorization, the blast radius is limited to that operator, and the key can generally be re-added. Nevertheless, the resulting on-chain mutation does not match the reviewed key identity, and the extra removal charge is not automatically recoverable.
Scenario
[D0,D1,A,B,C], with the first two keys deposited.removeKeys(nodeOperatorId,3,1).[D0,D1,A,C].Drafted from LidoLens finding RES-03