RocketDAOProposal - expired, and defeated proposals can be canceled
The `RocketDAOProposal.getState` function defaults a proposal's state to `ProposalState.Defeated`. While this fallback can be considered secure, the remaining code does not perform checks that prevent defeated proposals from changing their state. As such, a user can transition a proposal that is `Expired` or `Defeated` to `Cancelled` by using the `RocketDAOProposal.cancel` function. This can be used to deceive users and potentially bias future votes.\\nThe method emits an event that might trigger other components to perform actions.\\n```\\n} else {\\n    // Check the votes, was it defeated?\\n    // if (votesFor <= votesAgainst || votesFor < getVotesRequired(\\_proposalID))\\n    return ProposalState.Defeated;\\n}\\n```\\n\\n```\\nfunction cancel(address \\_member, uint256 \\_proposalID) override public onlyDAOContract(getDAO(\\_proposalID)) {\\n    // Firstly make sure this proposal that hasn't already been executed\\n    require(getState(\\_proposalID) != ProposalState.Executed, "Proposal has already been executed");\\n    // Make sure this proposal hasn't already been successful\\n    require(getState(\\_proposalID) != ProposalState.Succeeded, "Proposal has already succeeded");\\n    // Only allow the proposer to cancel\\n    require(getProposer(\\_proposalID) == \\_member, "Proposal can only be cancelled by the proposer");\\n    // Set as cancelled now\\n    setBool(keccak256(abi.encodePacked(daoProposalNameSpace, "cancelled", \\_proposalID)), true);\\n    // Log it\\n    emit ProposalCancelled(\\_proposalID, \\_member, block.timestamp);\\n}\\n```\\n
Preserve the true outcome. Do not allow to cancel proposals that are already in an end-state like `canceled`, `expired`, `defeated`.
```\\n} else {\\n    // Check the votes, was it defeated?\\n    // if (votesFor <= votesAgainst || votesFor < getVotesRequired(\\_proposalID))\\n    return ProposalState.Defeated;\\n}\\n```\\n