DatingDapp

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

SoulboundProfileNFT: No Permanent Ban Mechanism Results in Ineffective Blocking

Summary

The contract’s method of blocking a user (via blockProfile) is limited to burning the user’s NFT. It does not prevent the same address from re-minting another profile afterward.

Vulnerability Details

  1. Ineffective Blocking

    • blockProfile only deletes the user’s current NFT. There is no “blocked” record in storage.

    • A blocked user can simply call mintProfile again and create a fresh identity.

  2. Lack of Persistent Tracking

    • There is no data structure (like a mapping) that permanently marks an address as blocked.

    • Burning an NFT leaves no persistent restriction on the address itself.

Impact

  • Circumvention of Owner’s Authority: The contract owner’s attempt to remove undesirable users is undercut, as the user can rejoin with the same address.

  • No Deterrence: Malicious or abusive participants can continue to re-mint profiles indefinitely, negating the concept of effective moderation.

Tools Used

  • Manual Review: Observed that blockProfile and burnProfile do not set any status that would prevent new mintProfile calls.

  • Static Analysis: Automated tools can highlight the absence of a persistent block or ban list.

Recommendations

  1. Implement a Ban Mapping

    • Introduce mapping(address => bool) blocked;.

    • Upon blocking, set blocked[user] = true; and require a require(!blocked[msg.sender], "Blocked") check in mintProfile.

  2. Optional Unblock Functionality

    • If desired, add a way for the contract owner to revert a block by setting blocked[user] = false;.

    • Emit events to maintain an auditable history of blocking and unblocking actions.

In the SoulboundProfileNFT.sol:

// Tracks whether an address is blocked from minting a profile
mapping(address => bool) public blocked;
event ProfileBlocked(address indexed user, uint256 tokenId);
event ProfileUnblocked(address indexed user);
/// @notice Block a user's profile and prevent future minting.
function blockProfile(address user) external onlyOwner {
uint256 tokenId = profileToToken[user];
require(tokenId != 0, "No profile found");
// Permanently block the user from minting again.
blocked[user] = true;
emit ProfileBlocked(user, tokenId);
}
/// @notice Unblock a previously blocked user.
function unblockProfile(address user) external onlyOwner {
require(blocked[user], "User is not blocked");
// Unset the blocked status so user can use the like functionality again
blocked[user] = false;
emit ProfileUnblocked(user);
}

In the LikeRegistry.sol:

function likeUser(address liked) external payable {
require(msg.value == 1 ether, "Must send 1 ETH");
require(!likes[msg.sender][liked], "Already liked");
require(msg.sender != liked, "Cannot like yourself");
require(profileNFT.profileToToken(msg.sender) != 0, "Must have a profile NFT");
require(profileNFT.profileToToken(liked) != 0, "Liked user must have a profile NFT");
+ // require the profile NFTs are not blocked
+ require(!profileNFT.blocked(msg.sender), "Sender profile is blocked");
+ require(!profileNFT.blocked(liked), "Liked profile is blocked");
likes[msg.sender][liked] = true;
emit Liked(msg.sender, liked);
// Check if mutual like
if (likes[liked][msg.sender]) {
matches[msg.sender].push(liked);
matches[liked].push(msg.sender);
emit Matched(msg.sender, liked);
matchRewards(liked, msg.sender);
}
}
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.