# transferSAFEOwnership() does not fully transfer ownership

transferSAFEOwnership() in ODSafeManager fails to clear the safeCan permission mapping, so previously approved addresses retain access after a safe is transferred away and back.

- Payout: Contest reward
- Severity: medium
- Program: Open Dollar
- Credited: lsaudit
- Disclosed: 2023-12-20
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-10-opendollar-findings/issues/142
- Library page: https://bugbountylibrary.com/r/c4%3A2023-10-opendollar%3AM-15
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** ODSafeManager smart contract (safe ownership/permissions)
**Root cause:** transferSAFEOwnership() updates _usrSafes, _usrSafesPerCollat and _safeData.owner but never clears safeCan[oldOwner][safe], so prior allowSAFE grants persist across ownership changes.
**Steps:**
1. Owner A calls `allowSAFE(123, X, 1)` and `allowSAFE(123, Y, 1)`, setting `safeCan[A][123][X]=1` and `safeCan[A][123][Y]=1`.
2. A transfers safe 123 to B via `transferSAFEOwnership(123, B)`; safeCan entries for A remain.
3. Later safe 123 is transferred back to A via `transferSAFEOwnership(123, A)`.
4. X or Y calls any function guarded by `safeAllowed(123)`; modifier checks `safeCan[A][123][msg.sender] != 0` and passes, granting stale access.
**How found:** Code reading of the safeAllowed modifier and transferSAFEOwnership(): noticed the transfer function mutates owner and user-safe mappings but omits the safeCan permission mapping, then traced a transfer-away-and-back scenario.
**Exploitation:** Precondition: a safe cycles ownership back to a prior owner who had granted permissions. The previously approved address, now malicious, calls safeAllowed-guarded functions (e.g. allowSAFE, critical safe operations) and is still authorized because safeCan was never reset, allowing unauthorized manipulation of the safe.
**Insight:** When auditing ownership-transfer functions, enumerate every mapping keyed by the old owner (permissions, allowances, per-user indexes) and verify each is cleared or re-keyed; stale grants keyed by old owner resurface if ownership ever returns.
