# Ended locks can be extended

VotingEscrowTruf.extendLock lacks an expiry check, letting an already-ended lock be extended to a past timestamp so the owner can unstake anytime while keeping points.

- Payout: Contest reward
- Severity: medium
- Program: Truflation
- Credited: Kow
- Disclosed: 2023-12-22
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-12-truflation-judging/issues/82
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-12-truflation%3AM-3
- Tags: domain:web3 impact:integrity kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic

**Target:** VotingEscrowTruf.sol extendLock / lockups
**Root cause:** extendLock does not verify the lock has not already expired, so a new end earlier than block.timestamp can be set on an ended lock.
**Steps:**
1. Stake 100e18 for 5 days as alice via `_stake`.
2. `skip(5 days + 1)` so lockups[alice][0].ends < block.timestamp.
3. `skip(36 days)` further.
4. Call `veTRUF.extendLock(0, 30 days)` as alice.
5. Observe new ends still < block.timestamp.
6. Call `veTRUF.unstake(0)` immediately, withdrawing while retaining points.
**How found:** Code review of extendLock revealed no check on whether the lock already ended, then a Foundry PoC confirmed the past-timestamp extension and immediate unstake.
**Exploitation:** Lock owner waits for expiry, calls extendLock with a short duration so the new end remains in the past, then unstakes at will while accruing lock points/voting power.
**Insight:** In ve-style escrow contracts, audit every state-mutating function (extend, merge, delegate) for missing expiry guards, not just the stake/unstake paths; a past-dated extension silently converts a time-lock into a flexible lock.
