isCoolerCallback can be bypassed
The `CoolerCallback.isCoolerCallback()` is intended to ensure that the lender implements the `CoolerCallback` abstract at line 241 when the parameter `isCallback_` is `true`.\\n```\\nfunction clearRequest(\\n    uint256 reqID_,\\n    bool repayDirect_,\\n    bool isCallback_\\n) external returns (uint256 loanID) {\\n    Request memory req = requests[reqID_];\\n\\n    // If necessary, ensure lender implements the CoolerCallback abstract.\\n    if (isCallback_ && !CoolerCallback(msg.sender).isCoolerCallback()) revert NotCoolerCallback();\\n\\n    // Ensure loan request is active. \\n    if (!req.active) revert Deactivated();\\n\\n    // Clear the loan request in memory.\\n    req.active = false;\\n\\n    // Calculate and store loan terms.\\n    uint256 interest = interestFor(req.amount, req.interest, req.duration);\\n    uint256 collat = collateralFor(req.amount, req.loanToCollateral);\\n    uint256 expiration = block.timestamp + req.duration;\\n    loanID = loans.length;\\n    loans.push(\\n        Loan({\\n            request: req,\\n            amount: req.amount + interest,\\n            unclaimed: 0,\\n            collateral: collat,\\n            expiry: expiration,\\n            lender: msg.sender,\\n            repayDirect: repayDirect_,\\n            callback: isCallback_\\n        })\\n    );\\n\\n    // Clear the loan request storage.\\n    requests[reqID_].active = false;\\n\\n    // Transfer debt tokens to the owner of the request.\\n    debt().safeTransferFrom(msg.sender, owner(), req.amount);\\n\\n    // Log the event.\\n    factory().newEvent(reqID_, CoolerFactory.Events.ClearRequest, 0);\\n}\\n```\\n\\nHowever, this function doesn't provide any protection. The lender can bypass this check without implementing the `CoolerCallback` abstract by calling the `Cooler.clearRequest()` function using a contract that implements the `isCoolerCallback()` function and returns a `true` value.\\nFor example:\\n```\\ncontract maliciousLender {\\n    function isCoolerCallback() pure returns(bool) {\\n        return true;\\n    }\\n    \\n    function operation(\\n        address _to,\\n        uint256 reqID_\\n    ) public {\\n        Cooler(_to).clearRequest(reqID_, true, true);\\n    }\\n    \\n    function onDefault(uint256 loanID_, uint256 debt, uint256 collateral) public {}\\n}\\n```\\n\\nBy being the `loan.lender` with implement only `onDefault()` function, this will cause the `repayLoan()` and `rollLoan()` methods to fail due to revert at `onRepay()` and `onRoll()` function. The borrower cannot repay and the loan will be defaulted.\\nAfter the loan default, the attacker can execute `claimDefault()` to claim the collateral.\\nFurthermore, there is another method that allows lenders to bypass the `CoolerCallback.isCoolerCallback()` function which is loan ownership transfer.\\n```\\n/// @notice Approve transfer of loan ownership rights to a new address.\\n/// @param  to_ address to be approved.\\n/// @param  loanID_ index of loan in loans[].\\nfunction approveTransfer(address to_, uint256 loanID_) external {\\n    if (msg.sender != loans[loanID_].lender) revert OnlyApproved();\\n\\n    // Update transfer approvals.\\n    approvals[loanID_] = to_;\\n}\\n\\n/// @notice Execute loan ownership transfer. Must be previously approved by the lender.\\n/// @param  loanID_ index of loan in loans[].\\nfunction transferOwnership(uint256 loanID_) external {\\n    if (msg.sender != approvals[loanID_]) revert OnlyApproved();\\n\\n    // Update the load lender.\\n    loans[loanID_].lender = msg.sender;\\n    // Clear transfer approvals.\\n    approvals[loanID_] = address(0);\\n}\\n```\\n\\nNormally, the lender who implements the `CoolerCallback` abstract may call the `Cooler.clearRequest()` with the `_isCoolerCallback` parameter set to `true` to execute logic when a loan is repaid, rolled, or defaulted.\\nBut the lender needs to change the owner of the loan, so they call the `approveTransfer()` and `transferOwnership()` functions to the contract that doesn't implement the `CoolerCallback` abstract (or implement only `onDefault()` function to force the loan default), but the `loan.callback` flag is still set to `true`.\\nThus, this breaks the business logic since the three callback functions don't need to be implemented when the `isCoolerCallback()` is set to `true` according to the dev note in the `CoolerCallback` abstract below:\\n/// @notice Allows for debt issuers to execute logic when a loan is repaid, rolled, or defaulted. /// @dev The three callback functions must be implemented if `isCoolerCallback()` is set to true.
Only allowing callbacks from the protocol-trusted address (eg., `Clearinghouse` contract).\\nDisable the transfer owner of the loan when the `loan.callback` is set to `true`.
The lender forced the Loan become default to get the collateral token, owner lost the collateral token.\\nBypass the `isCoolerCallback` validation.
```\\nfunction clearRequest(\\n    uint256 reqID_,\\n    bool repayDirect_,\\n    bool isCallback_\\n) external returns (uint256 loanID) {\\n    Request memory req = requests[reqID_];\\n\\n    // If necessary, ensure lender implements the CoolerCallback abstract.\\n    if (isCallback_ && !CoolerCallback(msg.sender).isCoolerCallback()) revert NotCoolerCallback();\\n\\n    // Ensure loan request is active. \\n    if (!req.active) revert Deactivated();\\n\\n    // Clear the loan request in memory.\\n    req.active = false;\\n\\n    // Calculate and store loan terms.\\n    uint256 interest = interestFor(req.amount, req.interest, req.duration);\\n    uint256 collat = collateralFor(req.amount, req.loanToCollateral);\\n    uint256 expiration = block.timestamp + req.duration;\\n    loanID = loans.length;\\n    loans.push(\\n        Loan({\\n            request: req,\\n            amount: req.amount + interest,\\n            unclaimed: 0,\\n            collateral: collat,\\n            expiry: expiration,\\n            lender: msg.sender,\\n            repayDirect: repayDirect_,\\n            callback: isCallback_\\n        })\\n    );\\n\\n    // Clear the loan request storage.\\n    requests[reqID_].active = false;\\n\\n    // Transfer debt tokens to the owner of the request.\\n    debt().safeTransferFrom(msg.sender, owner(), req.amount);\\n\\n    // Log the event.\\n    factory().newEvent(reqID_, CoolerFactory.Events.ClearRequest, 0);\\n}\\n```\\n