68955 sc low unconditional hasrole check in setmigrationpermit authorization entrapment
Submitted on Mar 12th 2026 at 04:38:52 UTC by @hyuunn for Audit Comp | Folks Finance: Staking Contracts
Report ID: #68955
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol
Impacts:
Description
Brief/Intro
When an admin revokes the MIGRATOR_ROLE from a migrator address, users who previously granted a migration permit to that address cannot revoke their permit. The permit persists indefinitely in storage. If the role is later re-granted to the same address, the stale permit automatically reactivates without the user's knowledge or fresh authorization.
Vulnerability Details
The setMigrationPermit() function applies the hasRole check to both grant and revoke operations:
// File: src/Staking.sol:84-87
function setMigrationPermit(address _migrator, bool _isMigrationPermitted) external {
if (!hasRole(MIGRATOR_ROLE, _migrator)) revert MigratorNotFound(_migrator);
migrationPermits[_migrator][msg.sender] = _isMigrationPermitted;
emit MigrationPermitUpdated(_migrator, msg.sender, _isMigrationPermitted);
}When an admin revokes MIGRATOR_ROLE, users cannot call setMigrationPermit(migrator, false) — the check reverts. The permit remains true in storage indefinitely. This violates the design principle documented in IMigratorV1.sol: "migrator should not be able to migrate stakes if not approved by user" — user approval must always be revocable at their discretion.
Impact Details
Authorization Entrapment: Users cannot revoke stale permits after role revocation, losing control over their migration consent
Surprise Re-Migration Risk: If the role is re-granted to the same address (or if the address is an upgradeable proxy), the permit silently reactivates without fresh user authorization
No Immediate Fund Loss: Impact is authorization integrity, not fund theft — but violates core security principle that users must control their own approvals
Conditions for Insight Value: Admin revokes a migrator role while users still hold permits; user later wants to revoke; role could potentially be re-granted
References
src/Staking.sol:84-87—setMigrationPermit()with unconditionalhasRolechecksrc/interfaces/IMigratorV1.sol— design intent: "migrator should not be able to migrate stakes if not approved by user"
Proof of Concept
Scenario Where Authorization Control Is Lost:
User calls
setMigrationPermit(migratorAddress, true)→ permit is granted, stored astrueAdmin calls
revokeRole(MIGRATOR_ROLE, migratorAddress)→ migrator loses the roleUser discovers the migrator no longer needs permission and calls
setMigrationPermit(migratorAddress, false)to clean upTransaction reverts:
MigratorNotFound(migratorAddress)— thehasRolecheck fails on revocationUser has no way to clear their permit — it remains
truein storage indefinitelyLater: Admin re-grants
MIGRATOR_ROLEto the same address (legitimate reason: replacement migrator)The stale permit becomes active again without the user's knowledge or fresh authorization
If the user created new stakes after the role was revoked, these new stakes are now vulnerable to migration without fresh consent
Was this helpful?