# No way to revoke Approval in DelegateToken.approve leads to unauthorized calling of DelegateToken.transferFrom

DelegateToken lets an operator self-approve a tokenId; revoking setApprovalForAll does not clear that per-token approval, so the operator can still transferFrom.

- Payout: Audit contest
- Severity: medium
- Program: Delegate
- Found by: d4r3d3v1l
- Disclosed: 2023-11-15
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2023-09-delegate#m-01
- Library page: https://bugbountylibrary.com/r/c4%3A2023-09-delegate%3AM-01
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:access-control vuln:auth-bypass

**Target:** DelegateToken ERC721-style approval logic (approve/setApprovalForAll/transferFrom)
**Root cause:** DelegateToken.approve permits an operator (approvedForAll) to set itself as the tokenId-specific approved address, and setApprovalForAll(false) only clears the operator mapping, leaving readApproved intact. revertNotApprovedOrOperator still passes via msg.sender == readApproved.
**Steps:**
1. Alice (holder) calls `dt.setApprovalForAll(Bob, true)`.
2. Bob calls `dt.approve(Bob, delegateTokenId)` — allowed because Bob is an operator.
3. Alice calls `dt.setApprovalForAll(Bob, false)` to revoke.
4. Bob still calls `dt.transferFrom(Alice, to, delegateTokenId)`; revertNotApprovedOrOperator passes because `msg.sender == readApproved(delegateTokenInfo, delegateTokenId)`.
**How found:** Code review of revertNotApprovedOrOperator: noticed the OR condition on readApproved is independent of the operator mapping, and that approve() lacks an owner-only check unlike OZ ERC721, so an operator can seed a persistent per-token approval.
**Exploitation:** A previously-trusted operator, after being revoked via setApprovalForAll, retains per-token approval it granted itself and can move the holder's DelegateToken position to an arbitrary address.
**Insight:** When a contract reimplements ERC721 approvals, check whether approve() is restricted to owner/token-owner; if operators can self-approve, revocation of operator status leaves a dangling per-token approval that bypasses the operator check.

