# Freezing roles in ERC721NFTProduct and ERC1155NFTProduct is moot

GranularRoles.hasRole override lets ADMIN_ROLE bypass DEFAULT_ADMIN_ROLE, so frozen roles can still be granted/revoked, defeating the freeze guardrail.

- Payout: Contest reward
- Severity: medium
- Program: Nftport
- Credited: 0x52
- Disclosed: 2022-10-18
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2022-10-nftport-judging/issues/81
- Library page: https://bugbountylibrary.com/r/sherlock%3A2022-10-nftport%3AM-6
- Tags: domain:web3 impact:privesc kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** ERC721NFTProduct/ERC1155NFTProduct GranularRoles access control
**Root cause:** GranularRoles.hasRole returns true if account has ADMIN_ROLE regardless of requested role, including DEFAULT_ADMIN_ROLE, so ADMIN_ROLE passes AccessControl's admin check on grantRole/revokeRole.
**Steps:**
1. Deploy ERC721NFTProduct/ERC1155NFTProduct with GranularRoles.
2. Freeze roles via the freeze mechanism expecting no further role changes.
3. As an account holding ADMIN_ROLE, call `grantRole(role, attacker)` or `revokeRole(role, victim)`.
4. Observe hasRole(ADMIN_ROLE, msg.sender) short-circuits the DEFAULT_ADMIN_ROLE check and the call succeeds.
**How found:** Code reading of GranularRoles#hasRole override and AccessControl's admin-check flow; noticed ADMIN_ROLE short-circuit also satisfies DEFAULT_ADMIN_ROLE, contradicting the assumption that no role admin was set.
**Exploitation:** An ADMIN_ROLE holder directly invokes AccessControlUpgradable#grantRole/revokeRole to add or strip roles despite the freeze, undermining the intended permission lock.
**Insight:** When a contract overrides hasRole to add a super-admin, check every inherited onlyRole/admin-gated function (grantRole, revokeRole, renounceRole) — the override silently satisfies DEFAULT_ADMIN_ROLE checks and defeats freeze/lock invariants.
