msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: claim - Incomplete and lenient implementation
Severity: high

Description:
In the current version of the code, the `claim` function is lacking crucial input validation logic as well as required state changes. Most of the process is implemented in other contracts or off-chain at the moment and is therefore out of scope for this audit, but there might still be issues caused by potential errors in the process. Moreover, pool manager and committee together have unlimited ownership of the deposits and can essentially withdraw all collateral to any desired address.\\n```\\nfunction claim(\\n uint256 policyIndex\\_,\\n uint256 amount\\_,\\n address receipient\\_\\n) external onlyPoolManager {\\n```\\n

Impact:

Recommended mitigation:
To ensure a more secure claiming process, we propose adding the following logic to the `claim` function:\\n`refund` should be called at the beginning of the `claim` flow, so that the recipient's true coverage amount will be used.\\n`policyIndex` should be added as a parameter to this function, so that `coverageMap` can be used to validate that the amount claimed on behalf of a recipient is covered.\\nThe payout amount should be subtracted in the `coveredMap` and `coverageMap` mappings.
Vulnerability: buy - insurance buyers trying to increase their coverage amount will lose their previous coverage
Severity: high

Description:
When a user is willing to `buy` insurance, he is required to specify the desired amount (denoted as amount_) and to pay the entire premium upfront. In return, he receives the ownership over an entry inside the `coverageMap` mapping. If a user calls the `buy` function more than once for the same policy and time frame, his entry in the `coverageMap` will not represent the accumulated amount that he paid for but only the last coverage amount, which means previous coverage will be lost forever (unless the contract is upgraded).\\n```\\nfor (uint256 w = fromWeek\\_; w < toWeek\\_; ++w) {\\n incomeMap[policyIndex\\_][w] =\\n incomeMap[policyIndex\\_][w].add(premium);\\n coveredMap[policyIndex\\_][w] =\\n coveredMap[policyIndex\\_][w].add(amount\\_);\\n\\n require(coveredMap[policyIndex\\_][w] <= maximumToCover,\\n "Not enough to buy");\\n\\n coverageMap[policyIndex\\_][w][\\_msgSender()] = Coverage({\\n amount: amount\\_,\\n premium: premium,\\n refunded: false\\n });\\n}\\n```\\n

Impact:

Recommended mitigation:
The coverage entry that represents the user's coverage should not be overwritten but should hold the accumulated amount of coverage instead.
Vulnerability: Several issues related to upgradeability of contracts
Severity: medium

Description:
We did not find a proxy contract or factory in the repository, but the README contains the following information:\\ncode/README.md:L11\\n```\\nEvery Pool is a standalone smart contract. It is made upgradeable with OpenZeppelin's Proxy Upgrade Pattern.\\n```\\n\\ncode/README.md:L56\\n```\\nAnd there will be multiple proxies and one implementation of the Pools, and one proxy and one implementation of EventAggregator.\\n```\\n\\nThere are several issues related to upgradeability or, generally, using the contracts as implementations for proxies. All recommendations in this report assume that it is not necessary to remain compatible with an existing deployment.\\nB. If upgradeability is supposed to work with inheritance, there should be dummy variables at the end of each contract in the inheritance hierarchy. Some of these have to be removed when “real” state variables are added. More precisely, it is conventional to use a fixed-size `uint256` array `__gap`, such that the consecutively occupied slots at the beginning (for the “real” state variables) add up to 50 with the size of the array. If state variables are added later, the gap's size has to be reduced accordingly to maintain this invariant. Currently, the contracts do not declare such a `__gap` variable.\\nC. Implementation contracts should not remain uninitalized. To prevent initialization by an attacker - which, in some cases, can have an impact on the proxy - the implementation contract's constructor should call `_disableInitializers`.

Impact:

Recommended mitigation:
Refamiliarize yourself with the subtleties and pitfalls of upgradeable `contracts`, in particular regarding state variables and the storage gap. A lot of useful information can be found here.\\nOnly import from `contracts-upgradeable`, not from `contracts`.\\nAdd appropriately-sized storage gaps at least to `PoolModel`, `NonReentrancy`, and `EventAggregator`. (Note that adding a storage gap to `NonReentrancy` will break compatibility with existing deployments.) Ideally, add comments and warnings to each file that state variables may only be added at the end, that the storage gap's size has to be reduced accordingly, and that state variables must not be removed, rearranged, or in any way altered (e.g., type, `constant`, immutable). No state variables should ever be added to the `Pool` contract, and a comment should make that clear.\\nAdd a constructor to `Pool` and `EventAggregator` that calls `_disableInitializers`.
Vulnerability: initialize - Committee members array can contain duplicates
Severity: medium

Description:
The initial committee members are given as array argument to the pool's `initialize` function. When the array is processed, there is no check for duplicates, and duplicates may also end up in the storage array `committeeArray`.\\n```\\nfor (uint256 i = 0; i < committeeMembers\\_.length; ++i) {\\n address member = committeeMembers\\_[i];\\n committeeArray.push(member);\\n committeeIndexPlusOne[member] = committeeArray.length;\\n}\\n```\\n\\nDuplicates will result in a discrepancy between the length of the array - which is later interpreted as the number of committee members - and the actual number of (different) committee members. This could lead to more problems, such as an insufficient committee size to reach the threshold.

Impact:

Recommended mitigation:
The `initialize` function should verify in the loop that `member` hasn't been added before. Note that `_executeAddToCommittee` refuses to add someone who is already in the committee, and the same technique can be employed here.
Vulnerability: Pool.buy- Users may end up paying more than intended due to changes in policy.weeklyPremium
Severity: medium

Description:
The price that an insurance buyer has to pay for insurance is determined by the duration of the coverage and the `weeklyPremium`. The price increases as the `weeklyPremium` increases. If a `buy` transaction is waiting in the mempool but eventually front-run by another transaction that increases `weeklyPremium`, the user will end up paying more than they anticipated for the same insurance coverage (assuming their allowance to the `Pool` contract is unlimited or at least higher than what they expected to pay).\\n```\\nuint256 premium = amount\\_.mul(policy.weeklyPremium).div(RATIO\\_BASE);\\nuint256 allPremium = premium.mul(toWeek\\_.sub(fromWeek\\_));\\n```\\n

Impact:

Recommended mitigation:
Consider adding a parameter for the maximum amount to pay, and make sure that the transaction will revert if `allPremium` is greater than this maximum value.
Vulnerability: Missing validation checks in execute
Severity: medium

Description:
The `Pool` contract implements a threshold voting mechanism for some changes in the contract state, where either the pool manager or a committee member can propose a change by calling `claim`, `changePoolManager`, `addToCommittee`, `removeFromCommittee`, or `changeCommitteeThreshold`, and then the committee has a time period for voting. If the threshold is reached during this period, then anyone can call `execute` to `execute` the state change.\\nWhile some validation checks are implemented in the proposal phase, this is not enough to ensure that business logic rules around these changes are completely enforced.\\n`_executeRemoveFromCommittee` - While the `removeFromCommittee` function makes sure that `committeeArray.length > committeeThreshold`, i.e., that there should always be enough committee members to reach the threshold, the same validation check is not enforced in `_executeRemoveFromCommittee`. To better illustrate the issue, let's consider the following example: `committeeArray.length = 5`, `committeeThreshold = 4`, and now `removeFromCommittee` is called two times in a row, where the second call is made before the first call reaches the threshold. In this case, both requests will be executed successfully, and we end up with `committeeArray.length = 3` and `committeeThreshold = 4`, which is clearly not desired.\\n`_executeChangeCommitteeThreshold` - Applying the same concept here, this function lacks the validation check of `threshold_ <= committeeArray.length`, leading to the same issue as above. Let's consider the following example: `committeeArray.length = 3`, `committeeThreshold = 2`, and now changeCommitteeThresholdis called with `threshold_ = 3`, but before this request is executed, `removeFromCommittee` is called. After both requests have been executed successfully, we will end up with `committeeThreshold = 3` and `committeeArray.length = 2`, which is clearly not desired.\\n```\\nfunction \\_executeRemoveFromCommittee(address who\\_) private {\\n```\\n\\n```\\nfunction \\_executeChangeCommitteeThreshold(uint256 threshold\\_) private {\\n```\\n

Impact:

Recommended mitigation:
Apply the same validation checks in the functions that execute the state change.
Vulnerability: Hard-coded minimum deposit amount
Severity: low

Description:
Resolution\\nFixed in 3bbafab926df0ea39f444ef0fd5d2a6197f99a5d by implementing the auditor's recommendation.\\nThe `deposit` function specifies a minimum amount of 1e12 units of the base token for a deposit:\\n```\\nuint256 constant AMOUNT\\_PER\\_SHARE = 1e18;\\n```\\n\\n```\\n// Anyone can be a seller, and deposit baseToken (e.g. USDC or WETH)\\n// to the pool.\\nfunction deposit(\\n uint256 amount\\_\\n) external noReenter {\\n require(enabled, "Not enabled");\\n\\n require(amount\\_ >= AMOUNT\\_PER\\_SHARE / 1000000, "Less than minimum");\\n```\\n\\nWhether that's an appropriate minimum amount or not depends on the base token. Note that the two example tokens listed above are USDC and WETH. With current ETH prices, 1e12 Wei cost an affordable 0.2 US Cent. USDC, on the other hand, has 6 decimals, so 1e12 units are worth 1 million USD, which is … steep.

Impact:

Recommended mitigation:
The minimum deposit amount should be configurable.
Vulnerability: Outdated Solidity version
Severity: low

Description:
The source files' version pragmas either specify that they need compiler version exactly 0.8.10 or at least 0.8.10:\\n```\\npragma solidity 0.8.10;\\n```\\n\\n```\\npragma solidity ^0.8.10;\\n```\\n\\nSolidity v0.8.10 is a fairly dated version that has known security issues. We generally recommend using the latest version of the compiler (at the time of writing, this is v0.8.20), and we also discourage the use of floating pragmas to make sure that the source files are actually compiled and deployed with the same compiler version they have been tested with.

Impact:

Recommended mitigation:
Resolution\\nFixed in 3bbafab926df0ea39f444ef0fd5d2a6197f99a5d by implementing the auditor's recommendation.\\nUse the Solidity compiler v0.8.20, and change the version pragma in all Solidity source files to `pragma solidity 0.8.20;`.
Vulnerability: Code used for testing purposes should be removed before deployment
Severity: low

Description:
Variables and logic have been added to the code whose only purpose is to make it easier to test. This might cause unexpected behavior if deployed in production. For instance, `onlyTest` and `setTimeExtra` should be removed from the code before deployment, as well as `timeExtra` in `getCurrentWeek` and `getNow`.\\n```\\nmodifier onlyTest() {\\n```\\n\\n```\\nfunction setTimeExtra(uint256 timeExtra\\_) external onlyTest {\\n```\\n\\n```\\nfunction getCurrentWeek() public view returns(uint256) {\\n return (block.timestamp + TIME\\_OFFSET + timeExtra) / (7 days);\\n}\\n```\\n\\n```\\nfunction getNow() public view returns(uint256) {\\n return block.timestamp + timeExtra;\\n}\\n```\\n

Impact:

Recommended mitigation:
For the long term, consider mimicking this behavior by using features offered by your testing framework.
Vulnerability: Missing events
Severity: low

Description:
Some state-changing functions do not emit an event at all or omit relevant information.\\nA. `Pool.setEventAggregator` should emit an event with the value of `eventAggregator_` so that off-chain services will be notified and can automatically adjust.\\n```\\nfunction setEventAggregator(address eventAggregator\\_) external onlyPoolManager {\\n eventAggregator = eventAggregator\\_;\\n}\\n```\\n\\nB. `Pool.enablePool` should emit an event when the pool is dis- or enabled.\\n```\\nfunction enablePool(bool enabled\\_) external onlyPoolManager {\\n enabled = enabled\\_;\\n}\\n```\\n\\nC. `Pool.execute` only logs the `requestIndex_` while it should also include the `operation` and `data` to better reflect the state change in the transaction.\\n```\\nif (eventAggregator != address(0)) {\\n IEventAggregator(eventAggregator).execute(\\n requestIndex\\_\\n );\\n}\\n```\\n

Impact:

Recommended mitigation:
State-changing functions should emit an event to have an audit trail and enable monitoring of smart contract usage.
Vulnerability: addPremium - A Back Runner May Cause an Insurance Holder to Lose Their Refunds by Calling addPremium Right After the Original Call
Severity: high

Description:
`addPremium` is a public function that can be called by anyone and that distributes the weekly premium payments to the pool manager and the rest of the pool share holders. If the collateral deposited is not enough to cover the total coverage offered to insurance holders for a given week, refunds are allocated pro rata for all insurance holders of that particular week and policy. However, in the current implementation, attackers can call `addPremium` right after the original call to `addPremium` but before the call to refund; this will cause the insurance holders to lose their refunds, which will be effectively locked forever in the contract (unless the contract is upgraded).\\n```\\nrefundMap[policyIndex\\_][week] = incomeMap[policyIndex\\_][week].mul(\\n allCovered.sub(maximumToCover)).div(allCovered);\\n```\\n

Impact:

Recommended mitigation:
`addPremium` should contain a validation check in the beginning of the function that reverts for the case of `incomeMap[policyIndex_][week] = 0`.
Vulnerability: refund - Attacker Can Lock Insurance Holder's Refunds by Calling refund Before a Refund Was Allocated
Severity: high

Description:
`addPremium` is used to determine the `refund` amount that an insurance holder is eligible to claim. The amount is stored in the `refundMap` mapping and can then later be claimed by anyone on behalf of an insurance holder by calling `refund`. The `refund` function can't be called more than once for a given combination of `policyIndex_`, `week_`, and `who_`, as it would revert with an “Already refunded” error. This gives an attacker the opportunity to call `refund` on behalf of any insurance holder with value 0 inside the `refundMap`, causing any future `refund` allocated for that holder in a given week and for a given policy to be locked forever in the contract (unless the contract is upgraded).\\n```\\nfunction refund(\\n uint256 policyIndex\\_,\\n uint256 week\\_,\\n address who\\_\\n) external noReenter {\\n Coverage storage coverage = coverageMap[policyIndex\\_][week\\_][who\\_];\\n\\n require(!coverage.refunded, "Already refunded");\\n\\n uint256 allCovered = coveredMap[policyIndex\\_][week\\_];\\n uint256 amountToRefund = refundMap[policyIndex\\_][week\\_].mul(\\n coverage.amount).div(allCovered);\\n coverage.amount = coverage.amount.mul(\\n coverage.premium.sub(amountToRefund)).div(coverage.premium);\\n coverage.refunded = true;\\n\\n IERC20(baseToken).safeTransfer(who\\_, amountToRefund);\\n\\n if (eventAggregator != address(0)) {\\n IEventAggregator(eventAggregator).refund(\\n policyIndex\\_,\\n week\\_,\\n who\\_,\\n amountToRefund\\n );\\n }\\n}\\n```\\n

Impact:

Recommended mitigation:
There should be a validation check at the beginning of the function that reverts if `refundMap[policyIndex_][week_] == 0`.
Vulnerability: addTidal, _updateUserTidal, withdrawTidal - Wrong Arithmetic Calculations
Severity: high

Description:
To further incentivize sellers, anyone - although it will usually be the pool manager - can send an arbitrary amount of the Tidal token to a pool, which is then supposed to be distributed proportionally among the share owners. There are several flaws in the calculations that implement this mechanism:\\nA. addTidal:\\n```\\npoolInfo.accTidalPerShare = poolInfo.accTidalPerShare.add(\\n amount\\_.mul(SHARE\\_UNITS)).div(poolInfo.totalShare);\\n```\\n\\nThis should be:\\n```\\npoolInfo.accTidalPerShare = poolInfo.accTidalPerShare.add(\\n amount\\_.mul(SHARE\\_UNITS).div(poolInfo.totalShare));\\n```\\n\\nNote the different parenthesization. Without SafeMath:\\n```\\npoolInfo.accTidalPerShare += amount\\_ \\* SHARE\\_UNITS / poolInfo.totalShare;\\n```\\n\\nB. _updateUserTidal:\\n```\\nuint256 accAmount = poolInfo.accTidalPerShare.add(\\n userInfo.share).div(SHARE\\_UNITS);\\n```\\n\\nThis should be:\\n```\\nuint256 accAmount = poolInfo.accTidalPerShare.mul(\\n userInfo.share).div(SHARE\\_UNITS);\\n```\\n\\nNote that `add` has been replaced with `mul`. Without SafeMath:\\n```\\nuint256 accAmount = poolInfo.accTidalPerShare \\* userInfo.share / SHARE\\_UNITS;\\n```\\n\\nC. withdrawTidal:\\n```\\nuint256 accAmount = poolInfo.accTidalPerShare.add(userInfo.share);\\n```\\n\\nAs in B, this should be:\\n```\\nuint256 accAmount = poolInfo.accTidalPerShare.mul(\\n userInfo.share).div(SHARE\\_UNITS);\\n```\\n\\nNote that `add` has been replaced with `mul` and that a division by `SHARE_UNITS` has been appended. Without SafeMath:\\n```\\nuint256 accAmount = poolInfo.accTidalPerShare \\* userInfo.share / SHARE\\_UNITS;\\n```\\n\\nAs an additional minor point, the division in `addTidal` will revert with a panic (0x12) if the number of shares in the pool is zero. This case could be handled more gracefully.

Impact:

Recommended mitigation:
Implement the fixes described above. The versions without `SafeMath` are easier to read and should be preferred; see issue 3.13.
Vulnerability: claim - Incomplete and Lenient Implementation
Severity: high

Description:
In the current version of the code, the `claim` function is lacking crucial input validation logic as well as required state changes. Most of the process is implemented in other contracts or off-chain at the moment and is therefore out of scope for this audit, but there might still be issues caused by potential errors in the process. Moreover, pool manager and committee together have unlimited ownership of the deposits and can essentially withdraw all collateral to any desired address.\\n```\\nfunction claim(\\n uint256 policyIndex\\_,\\n uint256 amount\\_,\\n address receipient\\_\\n) external onlyPoolManager {\\n```\\n

Impact:

Recommended mitigation:
To ensure a more secure claiming process, we propose adding the following logic to the `claim` function:\\n`refund` should be called at the beginning of the `claim` flow, so that the recipient's true coverage amount will be used.\\n`policyIndex` should be added as a parameter to this function, so that `coverageMap` can be used to validate that the amount claimed on behalf of a recipient is covered.\\nThe payout amount should be subtracted in the `coveredMap` and `coverageMap` mappings.
Vulnerability: buy - Insurance Buyers Trying to Increase Their Coverage Amount Will Lose Their Previous Coverage
Severity: high

Description:
When a user is willing to `buy` insurance, he is required to specify the desired amount (denoted as amount_) and to pay the entire premium upfront. In return, he receives the ownership over an entry inside the `coverageMap` mapping. If a user calls the `buy` function more than once for the same policy and time frame, his entry in the `coverageMap` will not represent the accumulated amount that he paid for but only the last coverage amount, which means previous coverage will be lost forever (unless the contract is upgraded).\\n```\\nfor (uint256 w = fromWeek\\_; w < toWeek\\_; ++w) {\\n incomeMap[policyIndex\\_][w] =\\n incomeMap[policyIndex\\_][w].add(premium);\\n coveredMap[policyIndex\\_][w] =\\n coveredMap[policyIndex\\_][w].add(amount\\_);\\n\\n require(coveredMap[policyIndex\\_][w] <= maximumToCover,\\n "Not enough to buy");\\n\\n coverageMap[policyIndex\\_][w][\\_msgSender()] = Coverage({\\n amount: amount\\_,\\n premium: premium,\\n refunded: false\\n });\\n}\\n```\\n

Impact:

Recommended mitigation:
The coverage entry that represents the user's coverage should not be overwritten but should hold the accumulated amount of coverage instead.
Vulnerability: Several Issues Related to Upgradeability of Contracts
Severity: medium

Description:
We did not find a proxy contract or factory in the repository, but the README contains the following information:\\nREADME.md:L11\\n```\\nEvery Pool is a standalone smart contract. It is made upgradeable with OpenZeppelin's Proxy Upgrade Pattern.\\n```\\n\\nREADME.md:L56\\n```\\nAnd there will be multiple proxies and one implementation of the Pools, and one proxy and one implementation of EventAggregator.\\n```\\n\\nThere are several issues related to upgradeability or, generally, using the contracts as implementations for proxies. All recommendations in this report assume that it is not necessary to remain compatible with an existing deployment.\\nB. If upgradeability is supposed to work with inheritance, there should be dummy variables at the end of each contract in the inheritance hierarchy. Some of these have to be removed when “real” state variables are added. More precisely, it is conventional to use a fixed-size `uint256` array `__gap`, such that the consecutively occupied slots at the beginning (for the “real” state variables) add up to 50 with the size of the array. If state variables are added later, the gap's size has to be reduced accordingly to maintain this invariant. Currently, the contracts do not declare such a `__gap` variable.\\nC. Implementation contracts should not remain uninitalized. To prevent initialization by an attacker - which, in some cases, can have an impact on the proxy - the implementation contract's constructor should call `_disableInitializers`.

Impact:

Recommended mitigation:
Refamiliarize yourself with the subtleties and pitfalls of upgradeable `contracts`, in particular regarding state variables and the storage gap. A lot of useful information can be found here.\\nOnly import from `contracts-upgradeable`, not from `contracts`.\\nAdd appropriately-sized storage gaps at least to `PoolModel`, `NonReentrancy`, and `EventAggregator`. (Note that adding a storage gap to `NonReentrancy` will break compatibility with existing deployments.) Ideally, add comments and warnings to each file that state variables may only be added at the end, that the storage gap's size has to be reduced accordingly, and that state variables must not be removed, rearranged, or in any way altered (e.g., type, `constant`, immutable). No state variables should ever be added to the `Pool` contract, and a comment should make that clear.\\nAdd a constructor to `Pool` and `EventAggregator` that calls `_disableInitializers`.