DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

[M-01] Users Can Unblock Themselves by Calling `SouldbondProfileNFT::mintProfile()` again

Description:

Currently, the contract owner cannot block a user permanently without the user re-entering the protocol. This issue arises because there is no tracking of blocked addresses, which allows the affected user to bypass the block by simply calling the SoulboundProfileNFT::mintProfile() function again. This process can be repeated indefinitely, undermining the effectiveness of the blocking mechanism.

function blockProfile(address blockAddress) external onlyOwner {
uint256 tokenId = profileToToken[blockAddress];
require(tokenId != 0, "No profile found");
_burn(tokenId);
delete profileToToken[blockAddress];
delete _profiles[tokenId];
emit ProfileBurned(blockAddress, tokenId);
}

Impact:

Medium. Once a user is blocked, they should not be able to re-enter the protocol unless explicitly unblocked by the contract owner. The current system allows blocked users to mint a new profile and re-enter the app.

Proof of Concept:

A user can bypass a block and re-enter the dating app by minting their NFT again, even after being blocked. Here's an example of how the exploit works:

// setUp is equivalent to testSoulboundProfileNFT.t.sol
function testMyUserCanUnblockHimself() public {
vm.startPrank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
vm.stopPrank();
soulboundNFT.blockProfile(address(user));
assertEq(soulboundNFT.profileToToken(user), 0);
vm.startPrank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
vm.stopPrank();
}

Recommended Mitigation:

To resolve this issue, a mapping should be created to store blocked addresses. When the owner blocks an address, the mapping should be updated, and checks should be added during profile minting to prevent blocked users from re-entering the protocol:

+ mapping(address => bool) public blockedAddresses;
function blockProfile(address blockAddress) external onlyOwner {
uint256 tokenId = profileToToken[blockAddress];
require(tokenId != 0, "No profile found");
_burn(tokenId);
delete profileToToken[blockAddress];
delete _profiles[tokenId];
+ blockAddresses[blockAddress] = true;
emit ProfileBurned(blockAddress, tokenId);
}

Additionally, update the mintProfile() function to check whether the user is blocked before allowing them to mint a new profile:

function mintProfile(
string memory name,
uint8 age,
string memory profileImage
) external {
+ require(blockedAddresses[msg.sender] == false);
require(profileToToken[msg.sender] == 0, "Profile already exists");
uint256 tokenId = ++_nextTokenId;
_safeMint(msg.sender, tokenId);
// Store metadata on-chain
_profiles[tokenId] = Profile(name, age, profileImage);
profileToToken[msg.sender] = tokenId;
emit ProfileMinted(msg.sender, tokenId, name, age, profileImage);
}

By adding this mapping and check, the owner can effectively block users, and blocked users will no longer be able to bypass the block by re-minting their profiles.

Updates

Appeal created

n0kto Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_blocked_user_can_recreate_a_profil

Likelihood: Low, any blocked users. Impact: High, not really blocked.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.