Vulnerability: keep-core - Service contract callbacks can be abused to call into other contracts
Severity: medium

Description:
`KeepRandomBeaconServiceImplV1` allows senders to specify an arbitrary method and contract that will receive a callback once the beacon generates a relay entry:\\n```\\n/\\*\\*\\n \\* @dev Creates a request to generate a new relay entry, which will include\\n \\* a random number (by signing the previous entry's random number).\\n \\* @param callbackContract Callback contract address. Callback is called once a new relay entry has been generated.\\n \\* @param callbackMethod Callback contract method signature. String representation of your method with a single\\n \\* uint256 input parameter i.e. "relayEntryCallback(uint256)".\\n \\* @param callbackGas Gas required for the callback.\\n \\* The customer needs to ensure they provide a sufficient callback gas\\n \\* to cover the gas fee of executing the callback. Any surplus is returned\\n \\* to the customer. If the callback gas amount turns to be not enough to\\n \\* execute the callback, callback execution is skipped.\\n \\* @return An uint256 representing uniquely generated relay request ID. It is also returned as part of the event.\\n \\*/\\nfunction requestRelayEntry(\\n    address callbackContract,\\n    string memory callbackMethod,\\n    uint256 callbackGas\\n) public nonReentrant payable returns (uint256) {\\n```\\n\\nOnce an operator contract receives the relay entry, it calls executeCallback:\\n```\\n/\\*\\*\\n \\* @dev Executes customer specified callback for the relay entry request.\\n \\* @param requestId Request id tracked internally by this contract.\\n \\* @param entry The generated random number.\\n \\* @return Address to receive callback surplus.\\n \\*/\\nfunction executeCallback(uint256 requestId, uint256 entry) public returns (address payable surplusRecipient) {\\n    require(\\n        \\_operatorContracts.contains(msg.sender),\\n        "Only authorized operator contract can call execute callback."\\n    );\\n\\n    require(\\n        \\_callbacks[requestId].callbackContract != address(0),\\n        "Callback contract not found"\\n    );\\n\\n    \\_callbacks[requestId].callbackContract.call(abi.encodeWithSignature(\\_callbacks[requestId].callbackMethod, entry));\\n\\n    surplusRecipient = \\_callbacks[requestId].surplusRecipient;\\n    delete \\_callbacks[requestId];\\n}\\n```\\n\\nArbitrary callbacks can be used to force the service contract to execute many functions within the keep contract system. Currently, the `KeepRandomBeaconOperator` includes an `onlyServiceContract` modifier:\\n```\\n/\\*\\*\\n \\* @dev Checks if sender is authorized.\\n \\*/\\nmodifier onlyServiceContract() {\\n    require(\\n        serviceContracts.contains(msg.sender),\\n        "Caller is not an authorized contract"\\n    );\\n    \\_;\\n}\\n```\\n\\nThe functions it protects cannot be targeted by the aforementioned service contract callbacks due to Solidity's `CALLDATASIZE` checking. However, the presence of the modifier suggests that the service contract is expected to be a permissioned actor within some contracts.

Impact:

Recommended mitigation:
Stick to a constant callback method signature, rather than allowing users to submit an arbitrary string. An example is `__beaconCallback__(uint256)`.\\nConsider disallowing arbitrary callback destinations. Instead, rely on contracts making requests directly, and default the callback destination to `msg.sender`. Ensure the sender is not an EOA.