hats-finance / Palmera-0x5fee7541ddcd51ba9f4af606f87b2c42eea655be

Palmera hierarchical module
0 stars 1 forks source link

Missing `disableSafeLeadRoles` Call for Root in `removeWholeTree` Function** #72

Open hats-bug-reporter[bot] opened 4 days ago

hats-bug-reporter[bot] commented 4 days ago

Github username: @0xmahdirostami Twitter username: 0xmahdirostami Submission hash (on-chain): 0xaad7afa14366c6b8c37347f008d96027c424ac49bfd1b2a4529532c1c739661f Severity: medium

Description: Description:

In the removeWholeTree function, the disableSafeLeadRoles function is not called for the root safe. The relevant code snippet is:

            disableSafeLeadRoles(safes[org][safe].safe);
            _exitSafe(safe);
            unchecked {
                ++j;
            }
        }
        // After Disconnect Root Safe
        _exitSafe(rootSafe);

This snippet shows that disableSafeLeadRoles is called for all safes but not for the root safe.

Impact:

Mitigation:

To mitigate this issue, ensure that disableSafeLeadRoles is also called for the root safe. The corrected code should look like this:

            disableSafeLeadRoles(safes[org][safe].safe);
            _exitSafe(safe);
            unchecked {
                ++j;
            }
        }
        // After Disconnect Root Safe
        emit Events.WholeTreeRemoved(
            org, rootSafe, caller, safes[org][rootSafe].name
        );
+        disableSafeLeadRoles(rootSafe);
        _exitSafe(rootSafe);
        if (indexSafe[org].length == 0) removeOrg(org);

This ensures that lead roles are disabled for the root safe, preventing any issues or security risks associated with the root safe retaining roles it should no longer have.

alfredolopez80 commented 1 day ago

@0xmahdirostami if you check this for loop https://github.com/hats-finance/Palmera-0x5fee7541ddcd51ba9f4af606f87b2c42eea655be/blob/1ac35880b5d45154267788e2db548eaaae0beaa0/src/PalmeraModule.sol#L523, This runs through the organization's entire safeId array that include the RootSafe

So, this issue like you explain is not valid

0xmahdirostami commented 1 day ago

@alfredolopez80, thank you, but the issue is valid.

0xmahdirostami commented 1 day ago

@alfredolopez80 @0xRizwan ,the root safe is not in loop. add the console log to code.

function removeWholeTree() external IsRootSafe(_msgSender()) requiresAuth {
        address caller = _msgSender();
        bytes32 org = getOrgHashBySafe(caller);
        uint256 rootSafe = getSafeIdBySafe(org, caller);
        uint256[] memory _indexSafe = getTreeMember(rootSafe, indexSafe[org]);
        RolesAuthority _authority = RolesAuthority(rolesAuthority);
        for (uint256 j; j < _indexSafe.length;) {
            uint256 safe = _indexSafe[j];
            DataTypes.Safe memory _safe = safes[org][safe];
            // Revoke roles to safe
            _authority.setUserRole(
                _safe.safe, uint8(DataTypes.Role.SUPER_SAFE), false
            );
            // Disable safe lead role
            disableSafeLeadRoles(safes[org][safe].safe);
+            console.log("disconnect", safe);
            _exitSafe(safe);
            unchecked {
                ++j;
            }
        }
        // After Disconnect Root Safe
        emit Events.WholeTreeRemoved(
            org, rootSafe, caller, safes[org][rootSafe].name
        );
        _exitSafe(rootSafe);
        if (indexSafe[org].length == 0) removeOrg(org);
    }

test:

    function testEventWhenRemoveWholeTree() public {
        (
            uint256 rootId,
            uint256 safeIdA1,
            uint256 subSafeIdA1,
            uint256 subSubSafeIdA1
        ) = palmeraSafeBuilder.setupOrgFourTiersTree(
            orgName, safeA1Name, subSafeA1Name, subSubSafeA1Name
        );

        address rootAddr = palmeraModule.getSafeAddress(rootId);
        vm.startPrank(rootAddr);
        vm.expectEmit(true, true, true, true);
        emit WholeTreeRemoved(
            keccak256(abi.encodePacked(orgName)), rootId, rootAddr, orgName
        );
        palmeraModule.removeWholeTree();
    }

Logs:

Ran 1 test for test/EventsChecker.t.sol:EventsChekers
[PASS] testEventWhenRemoveWholeTree() (gas: 2535909)
Logs:
  disconnect 2
  disconnect 3
  disconnect 4

As it shows, root is not in loop, so the issue is valid