exerciseOptionsReceiver() Lack of Ownership Check for oTAP, Allowing Anyone to Use oTAPTokenID
In UsdoOptionReceiverModule.exerciseOptionsReceiver(): For this method to execute successfully, the `owner` of the `oTAPTokenID` needs to approve it to `address(usdo)`. Once approved, anyone can front-run execute `exerciseOptionsReceiver()` and utilize this authorization.\\nIn `USDO.lzCompose()`, it is possible to specify `_msgType == MSG_TAP_EXERCISE` to execute `USDO.exerciseOptionsReceiver()` across chains.\\n```\\n    function exerciseOptionsReceiver(address srcChainSender, bytes memory _data) public payable {\\n// rest of code\\n            ITapiocaOptionBroker(_options.target).exerciseOption(\\n              _options.oTAPTokenID,\\n                address(this), //payment token\\n                _options.tapAmount\\n            );\\n            _approve(address(this), address(pearlmit), 0);\\n            uint256 bAfter = balanceOf(address(this));\\n\\n            // Refund if less was used.\\n            if (bBefore > bAfter) {\\n                uint256 diff = bBefore - bAfter;\\n                if (diff < _options.paymentTokenAmount) {\\n                    IERC20(address(this)).safeTransfer(_options.from, _options.paymentTokenAmount - diff);\\n                }\\n            }\\n// rest of code\\n```\\n\\nFor this method to succeed, USDO must first obtain approve for the `oTAPTokenID`.\\nExample: The owner of `oTAPTokenID` is Alice.\\nalice in A chain execute lzSend(dstEid = B) with\\ncomposeMsg = [oTAP.permit(usdo,oTAPTokenID,v,r,s) 2.exerciseOptionsReceiver(oTAPTokenID,_options.from=alice) 3. oTAP.revokePermit(oTAPTokenID)]\\nin chain B USDO.lzCompose() will\\nexecute oTAP.permit(usdo,oTAPTokenID)\\nexerciseOptionsReceiver(srcChainSender=alice,_options.from=alice,oTAPTokenID )\\noTAP.revokePermit(oTAPTokenID)\\nThe signature of `oTAP.permit` is public, allowing anyone to use it.\\nNote: if alice call approve(oTAPTokenID,usdo) in chain B without signature, but The same result\\nThis opens up the possibility for malicious users to front-run use this signature. Let's consider an example with Bob:\\nBob in Chain A uses Alice's signature (v, r, s):\\ncomposeMsg = [oTAP.permit(usdo, oTAPTokenID, v, r, s), exerciseOptionsReceiver(oTAPTokenID, _options.from=bob)]-----> (Note: `_options.from` should be set to Bob.)\\nIn Chain B, when executing `USDO.lzCompose(dstEid = B)`, the following actions occur:\\nExecute `oTAP.permit(usdo, oTAPTokenID)`\\nExecute `exerciseOptionsReceiver(srcChainSender=bob, _options.from=bob, oTAPTokenID)`\\nAs a result, Bob gains unconditional access to this `oTAPTokenID`.\\nIt is advisable to check the ownership of `oTAPTokenID` is `_options.from` before executing `ITapiocaOptionBroker(_options.target).exerciseOption()`.
add check `_options.from` is owner or be approved\\n```\\n    function exerciseOptionsReceiver(address srcChainSender, bytes memory _data) public payable {\\n\\n// rest of code\\n            uint256 bBefore = balanceOf(address(this));\\n// Add the line below\\n           address oTap = ITapiocaOptionBroker(_options.target).oTAP();\\n// Add the line below\\n           address oTapOwner = IERC721(oTap).ownerOf(_options.oTAPTokenID);\\n// Add the line below\\n           require(oTapOwner == _options.from\\n// Add the line below\\n                         || IERC721(oTap).isApprovedForAll(oTapOwner,_options.from)\\n// Add the line below\\n                         || IERC721(oTap).getApproved(_options.oTAPTokenID) == _options.from\\n// Add the line below\\n                        ,"invalid");\\n            ITapiocaOptionBroker(_options.target).exerciseOption(\\n                _options.oTAPTokenID,\\n                address(this), //payment token\\n                _options.tapAmount\\n            );\\n            _approve(address(this), address(pearlmit), 0);\\n            uint256 bAfter = balanceOf(address(this));\\n\\n            // Refund if less was used.\\n            if (bBefore > bAfter) {\\n                uint256 diff = bBefore - bAfter;\\n                if (diff < _options.paymentTokenAmount) {\\n                    IERC20(address(this)).safeTransfer(_options.from, _options.paymentTokenAmount - diff);\\n                }\\n            }\\n        }\\n```\\n
The `exerciseOptionsReceiver()` function lacks ownership checks for `oTAP`, allowing anyone to use `oTAPTokenID`.
```\\n    function exerciseOptionsReceiver(address srcChainSender, bytes memory _data) public payable {\\n// rest of code\\n            ITapiocaOptionBroker(_options.target).exerciseOption(\\n              _options.oTAPTokenID,\\n                address(this), //payment token\\n                _options.tapAmount\\n            );\\n            _approve(address(this), address(pearlmit), 0);\\n            uint256 bAfter = balanceOf(address(this));\\n\\n            // Refund if less was used.\\n            if (bBefore > bAfter) {\\n                uint256 diff = bBefore - bAfter;\\n                if (diff < _options.paymentTokenAmount) {\\n                    IERC20(address(this)).safeTransfer(_options.from, _options.paymentTokenAmount - diff);\\n                }\\n            }\\n// rest of code\\n```\\n