# NounsDAOV3Proposals.cancel() should allow to cancel the proposal of the Expired state

Nouns DAO cancel() blocks Expired proposals, but a timelock GRACE_PERIOD upgrade can revive them to Queued, letting a stale proposal execute unexpectedly.

- Payout: Contest reward
- Severity: medium
- Program: Nouns DAO
- Credited: cccz
- Disclosed: 2023-07-31
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-07-nounsdao-findings/issues/55
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-nounsdao%3AM-03
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic vuln:governance

**Target:** NounsDAOV3Proposals governance contract / timelock
**Root cause:** Proposal Expired state is derived from the timelock's mutable GRACE_PERIOD rather than a fixed per-proposal expiry, so changing GRACE_PERIOD can move a proposal from Expired back to Queued. cancel() treats Expired as final and reverts, preventing cancellation before revival.
**Steps:**
1. Proposal A passes and is queued but cannot execute (insufficient timelock balance), so it reaches Expired state.
2. cancel() reverts with CantCancelProposalAtFinalState because Expired is treated as final.
3. Proposal B upgrades the timelock, extending GRACE_PERIOD (e.g. +7 days).
4. stateInternal() now returns Queued for proposal A since block.timestamp < eta + new GRACE_PERIOD.
5. Attacker funds the timelock shortfall and executes proposal A, sending funds to the now-compromised target.
**How found:** Code reading of cancel()'s final-state guard versus stateInternal()'s Expired computation; noticed Expired depends on mutable GRACE_PERIOD, unlike truly final states, making it reversible.
**Exploitation:** Requires a passed-but-unexecutable proposal that expired, plus a governance-approved GRACE_PERIOD extension. Attacker tops up the timelock balance and executes the revived proposal to route funds to a malicious/compromised destination.
**Escalation:** Chains a benign governance upgrade (GRACE_PERIOD change) with a stale queued proposal to bypass the intended finality of Expired state.
**Insight:** When a state machine derives a 'final' state from a mutable external parameter (timelock grace period, admin-configurable constants), that state is not actually final—check whether guards blocking actions on it can be bypassed by parameter changes.
