68994 sc low users cannot revoke migration permits after migrator role is removed
Submitted on Mar 12th 2026 at 09:24:35 UTC by @godwinudo for Audit Comp | Folks Finance: Staking Contracts
Report ID: #68994
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol
Description
Brief/Intro
The setMigrationPermit function validates that the target address currently holds the MIGRATOR_ROLE before allowing any change to the permit, including revocation. This means that if an admin removes the MIGRATOR_ROLE from a migrator address, users who previously granted that migrator permission can no longer revoke their own authorization. The permit remains true in storage permanently, and if the role is ever re-granted to the same address, the old permit becomes active again without the user's re-consent.
Vulnerability Details
The Staking.sol contract implements a migration system where users must explicitly authorize specific migrator contracts to move their staking positions. This authorization is managed through the setMigrationPermit function, which writes to the migrationPermits mapping:
mapping(address migrator => mapping(address user => bool isAuthorized)) public migrationPermits;When a user calls setMigrationPermit, the function first checks whether the target address currently holds the MIGRATOR_ROLE:
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);
}The problem is that this role check applies to both granting (true) and revoking (false) the permit.
The migrator can call migratePositionsFrom(user) and the permit check at line 173 passes:
The user never re-consented to this authorization. They may not even be aware that their old permit is active.
Impact Details
A user permanently loses the ability to revoke their migration authorization for a specific migrator address once that address's MIGRATOR_ROLE is removed. The permit remains true in storage with no way for anyone to clear it. If the same address later regains MIGRATOR_ROLE, it can migrate the user's positions without fresh consent.
Proof of Concept
Was this helpful?