DatingDapp

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

The SoulboundProfileNFT::blockProfile function is not effective at letting the contract owner block addresses.

Description

The SoulboundProfileNFT::blockProfile function claims to allow the app owner to block an address. The code in the function only burns the current profile held by the address to be blocked. There is no way to keep track of blocked addresses, or to prevent them from creating new profiles. Additionally, the LikeRegistry::likes and LikeRegistry::userBalances mappings use addresses to map the user to their likes and balances. This is not erased on blocking the user and deleting their profile. The blocked user can create a fresh profile and still have access to their information and funds in their old profile.

Impact

Owner cannot block addresses using the SoulboundProfileNFT::blockProfile function.

Proof of concept

The testBlockProfileAsOwner test can be modified as follows to prove that a blocked address can mint a new profile:

function testBlockProfileAsOwner() public {
vm.prank(user);
soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
uint256 tokenId = soulboundNFT.profileToToken(user);
assertEq(tokenId, 1, "Token should exist before blocking");
vm.prank(owner);
soulboundNFT.blockProfile(user);
uint256 newTokenId = soulboundNFT.profileToToken(user);
assertEq(newTokenId, 0, "Token should be removed after blocking");
+ vm.prank(user);
+ soulboundNFT.mintProfile("Alice", 25, "ipfs://profileImage");
+ uint256 tokenIdAgain = soulboundNFT.profileToToken(user);
+ assertEq(tokenIdAgain, 2, "Blocked user can still create a profile.");
}

Additional tests can be written for the LikeRegistry contract which demonstrate how the blocked user can recover their old like and balance data on creating a new profile. One such test is shown below:

contract LikeRegistryTest is Test {
SoulboundProfileNFT soulboundNFT;
LikeRegistry likeRegistry;
address user = address(0x123);
address user2 = address(0x456);
address owner = address(this); // Test contract acts as the owner
address uselessAddress;
function setUp() public {
soulboundNFT = new SoulboundProfileNFT();
likeRegistry = new LikeRegistry(address(soulboundNFT));
vm.prank(user);
soulboundNFT.mintProfile('Alice', 25, 'ipfs://image');
vm.prank(user2);
soulboundNFT.mintProfile('Ben', 25, 'ipfs://image');
vm.deal(user, 5 ether);
}
function testLikePersistence() public {
vm.prank(user);
likeRegistry.likeUser{value: 1 ether}(user2);
bool success = likeRegistry.likes(user, user2); //true
assertEq(success, true, "likeUser function failure");
vm.prank(owner);
soulboundNFT.blockProfile(user);
assertEq(soulboundNFT.profileToToken(user), 0, "user profile should be deleted");
vm.prank(user);
soulboundNFT.mintProfile('Alice', 25, 'ipfs://image');
assertEq(likeRegistry.likes(user, user2), success, 'New profile still has access to old data');
}
}

Tools Used

VSCode, Foundry

Recommendations

  1. The contract can keep track of blocked addresses with an (address => bool) mapping. Then, a new check can be added in SoulboundProfileNFT::mintProfile to prevent blocked addresses from minting a profile NFT.

  2. Alternatively, while blocking a user, all relevant information regarding their data should be deleted and any balance should be refunded back to the users who liked the blocked address.

Updates

Appeal created

n0kto Lead Judge 6 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.