msc-smart-contract-auditing/vulnerable-functions-base huggingface dataset:
Vulnerability: bitcoin-spv - Bitcoin output script length is not checked in wpkhSpendSighash  Won't Fix
Severity: medium

Description:
`CheckBitcoinSigs.wpkhSpendSighash` calculates the sighash of a Bitcoin transaction. Among its parameters, it accepts `bytes memory _outpoint`, which is a 36-byte UTXO id consisting of a 32-byte transaction hash and a 4-byte output index.\\nThe function in question should not accept an `_outpoint` that is not 36-bytes, but no length check is made:\\n```\\nfunction wpkhSpendSighash(\\n    bytes memory \\_outpoint,  // 36 byte UTXO id\\n    bytes20 \\_inputPKH,       // 20 byte hash160\\n    bytes8 \\_inputValue,      // 8-byte LE\\n    bytes8 \\_outputValue,     // 8-byte LE\\n    bytes memory \\_outputScript    // lenght-prefixed output script\\n) internal pure returns (bytes32) {\\n    // Fixes elements to easily make a 1-in 1-out sighash digest\\n    // Does not support timelocks\\n    bytes memory \\_scriptCode = abi.encodePacked(\\n        hex"1976a914",  // length, dup, hash160, pkh\\_length\\n        \\_inputPKH,\\n        hex"88ac");  // equal, checksig\\n    bytes32 \\_hashOutputs = abi.encodePacked(\\n        \\_outputValue,  // 8-byte LE\\n        \\_outputScript).hash256();\\n    bytes memory \\_sighashPreimage = abi.encodePacked(\\n        hex"01000000",  // version\\n        \\_outpoint.hash256(),  // hashPrevouts\\n        hex"8cb9012517c817fead650287d61bdd9c68803b6bf9c64133dcab3e65b5a50cb9",  // hashSequence(00000000)\\n        \\_outpoint,  // outpoint\\n        \\_scriptCode,  // p2wpkh script code\\n        \\_inputValue,  // value of the input in 8-byte LE\\n        hex"00000000",  // input nSequence\\n        \\_hashOutputs,  // hash of the single output\\n        hex"00000000",  // nLockTime\\n        hex"01000000"  // SIGHASH\\_ALL\\n    );\\n    return \\_sighashPreimage.hash256();\\n}\\n```\\n

Impact:

Recommended mitigation:
Check that `_outpoint.length` is 36.
Vulnerability: keep-core - stake operator should not be eligible if undelegatedAt is set
Severity: low

Description:
An operator's stake should not be eligible if they stake an amount and immediately call `undelegate` in an attempt to indicate that they are going to recover their stake soon.\\n```\\nbool notUndelegated = block.number <= operator.undelegatedAt || operator.undelegatedAt == 0;\\n\\nif (isAuthorized && isActive && notUndelegated) {\\n    balance = operator.amount;\\n}\\n```\\n

Impact:

Recommended mitigation:
A stake that is entering undelegation is indicated by `operator.undelegatedAt` being non-zero. Change the `notUndelegated` check block.number <= `operator.undelegatedAt` || `operator.undelegatedAt` == 0 to `operator.undelegatedAT == 0` as any value being set indicates that undelegation is in progress.\\nEnforce that within the initialization period stake is canceled instead of being undelegated.
Vulnerability: keep-tecdsa - Change state-mutability of checkSignatureFraud to view
Severity: low

Description:
```\\nfunction submitSignatureFraud(\\n    uint8 \\_v,\\n    bytes32 \\_r,\\n    bytes32 \\_s,\\n    bytes32 \\_signedDigest,\\n    bytes calldata \\_preimage\\n) external returns (bool \\_isFraud) {\\n    require(publicKey.length != 0, "Public key was not set yet");\\n\\n    bytes32 calculatedDigest = sha256(\\_preimage);\\n    require(\\n        \\_signedDigest == calculatedDigest,\\n        "Signed digest does not match double sha256 hash of the preimage"\\n    );\\n\\n    bool isSignatureValid = publicKeyToAddress(publicKey) ==\\n        ecrecover(\\_signedDigest, \\_v, \\_r, \\_s);\\n\\n    // Check if the signature is valid but was not requested.\\n    require(\\n        isSignatureValid && !digests[\\_signedDigest],\\n        "Signature is not fraudulent"\\n    );\\n\\n    return true;\\n}\\n```\\n

Impact:

Recommended mitigation:
Declare method as `view`. Consider renaming `submitSignatureFraud` to e.g. `checkSignatureFraud` to emphasize that it is only checking the signature and not actually changing state.
Vulnerability: keep-core - Specification inconsistency: TokenStaking.slash() is never called
Severity: low

Description:
According to the keep specification stake should be slashed if a staker violates the protocol:\\nSlashing If a staker violates the protocol of an operation in a way which can be proven on-chain, they will be penalized by having their stakes slashed.\\nWhile this functionality can only be called by the approved operator contract, it is not being used throughout the system. In contrast `seize()` is being called when reporting unauthorized signing or relay entry timeout.\\n```\\n/\\*\\*\\n \\* @dev Slash provided token amount from every member in the misbehaved\\n \\* operators array and burn 100% of all the tokens.\\n \\* @param amount Token amount to slash from every misbehaved operator.\\n \\* @param misbehavedOperators Array of addresses to seize the tokens from.\\n \\*/\\nfunction slash(uint256 amount, address[] memory misbehavedOperators)\\n    public\\n    onlyApprovedOperatorContract(msg.sender) {\\n    for (uint i = 0; i < misbehavedOperators.length; i++) {\\n        address operator = misbehavedOperators[i];\\n        require(authorizations[msg.sender][operator], "Not authorized");\\n        operators[operator].amount = operators[operator].amount.sub(amount);\\n    }\\n\\n    token.burn(misbehavedOperators.length.mul(amount));\\n}\\n```\\n

Impact:

Recommended mitigation:
Implement slashing according to the specification.
Vulnerability: tbtc - Remove notifyDepositExpiryCourtesyCall and allow exitCourtesyCall exiting the courtesy call at term
Severity: low

Description:
Following a deep dive into state transitions with the client it was agreed that `notifyDepositExpiryCourtesyCall` should be removed from the system as it is a left-over of a previous version of the deposit contract.\\nAdditionally, `exitCourtesyCall` should be callable at any time.\\n```\\n/// @notice Goes from courtesy call to active\\n/// @dev Only callable if collateral is sufficient and the deposit is not expiring\\n/// @param \\_d deposit storage pointer\\nfunction exitCourtesyCall(DepositUtils.Deposit storage \\_d) public {\\n    require(\\_d.inCourtesyCall(), "Not currently in courtesy call");\\n    require(block.timestamp <= \\_d.fundedAt + TBTCConstants.getDepositTerm(), "Deposit is expiring");\\n    require(getCollateralizationPercentage(\\_d) >= \\_d.undercollateralizedThresholdPercent, "Deposit is still undercollateralized");\\n    \\_d.setActive();\\n    \\_d.logExitedCourtesyCall();\\n}\\n```\\n

Impact:

Recommended mitigation:
Remove the `notifyDepositExpiryCourtesyCall` state transition and remove the requirement on `exitCourtesyCall` being callable only before the deposit expires.
Vulnerability: keep-tecdsa - withdraw should check for zero value transfer
Severity: low

Description:
Requesting the withdrawal of zero `ETH` in `KeepBonding.withdraw` should fail as this would allow the method to succeed, calling the user-provided destination even though the sender has no unbonded value.\\n```\\nfunction withdraw(uint256 amount, address payable destination) public {\\n    require(\\n        unbondedValue[msg.sender] >= amount,\\n        "Insufficient unbonded value"\\n    );\\n\\n    unbondedValue[msg.sender] -= amount;\\n\\n    (bool success, ) = destination.call.value(amount)("");\\n    require(success, "Transfer failed");\\n}\\n```\\n\\nAnd a similar instance in BondedECDSAKeep:\\n```\\n/// @notice Withdraws amount of ether hold in the keep for the member.\\n/// The value is sent to the beneficiary of the specific member.\\n/// @param \\_member Keep member address.\\nfunction withdraw(address \\_member) external {\\n    uint256 value = memberETHBalances[\\_member];\\n    memberETHBalances[\\_member] = 0;\\n\\n    /\\* solium-disable-next-line security/no-call-value \\*/\\n    (bool success, ) = tokenStaking.magpieOf(\\_member).call.value(value)("");\\n\\n    require(success, "Transfer failed");\\n}\\n```\\n

Impact:

Recommended mitigation:
Require that the amount to be withdrawn is greater than zero.
Vulnerability: tbtc - Signer collusion may bypass increaseRedemptionFee flow
Severity: low

Description:
DepositRedemption.increaseRedemptionFee is used by signers to approve a signable bitcoin transaction with a higher fee, in case the network is congested and miners are not approving the lower-fee transaction.\\nFee increases can be performed every 4 hours:\\n```\\nrequire(block.timestamp >= \\_d.withdrawalRequestTime + TBTCConstants.getIncreaseFeeTimer(), "Fee increase not yet permitted");\\n```\\n\\nIn addition, each increase must increment the fee by exactly the initial proposed fee:\\n```\\n// Check that we're incrementing the fee by exactly the redeemer's initial fee\\nuint256 \\_previousOutputValue = DepositUtils.bytes8LEToUint(\\_previousOutputValueBytes);\\n\\_newOutputValue = DepositUtils.bytes8LEToUint(\\_newOutputValueBytes);\\nrequire(\\_previousOutputValue.sub(\\_newOutputValue) == \\_d.initialRedemptionFee, "Not an allowed fee step");\\n```\\n\\nOutside of these two restrictions, there is no limit to the number of times `increaseRedemptionFee` can be called. Over a 20-hour period, for example, `increaseRedemptionFee` could be called 5 times, increasing the fee to `initialRedemptionFee * 5`.\\nRather than calling `increaseRedemptionFee` 5 times over 20 hours, colluding signers may immediately create and sign a transaction with a fee of `initialRedemptionFee * 5`, wait for it to be mined, then submit it to `provideRedemptionProof`. Because `provideRedemptionProof` does not check that a transaction signature signs an approved digest, interested parties would need to monitor the bitcoin blockchain, notice the spend, and provide an ECDSA fraud proof before `provideRedemptionProof` is called.

Impact:

Recommended mitigation:
Resolution\\nIssue addressed in keep-network/tbtc#522\\nTrack the latest approved fee, and ensure the transaction in `provideRedemptionProof` does not include a higher fee.
Vulnerability: tbtc - liquidating a deposit does not send the complete remainder of the contract balance to recipients
Severity: low

Description:
`purchaseSignerBondsAtAuction` might leave a wei in the contract if:\\nthere is only one wei remaining in the contract\\nthere is more than one wei remaining but the contract balance is odd.\\ncontract balances must be > 1 wei otherwise no transfer is attempted\\nthe division at line 271 floors the result if dividing an odd balance. The contract is sending `floor(contract.balance / 2)` to the keep group and liquidationInitiator leaving one 1 in the contract.\\n```\\nif (contractEthBalance > 1) {\\n    if (\\_wasFraud) {\\n        initiator.transfer(contractEthBalance);\\n    } else {\\n        // There will always be a liquidation initiator.\\n        uint256 split = contractEthBalance.div(2);\\n        \\_d.pushFundsToKeepGroup(split);\\n        initiator.transfer(split);\\n    }\\n}\\n```\\n

Impact:

Recommended mitigation:
Define a reasonable minimum amount when awarding the fraud reporter or liquidation initiator. Alternatively, always transfer the contract balance. When splitting the amount use the contract balance after the first transfer as the value being sent to the second recipient. Use the presence of locked funds in a contract as an error indicator unless funds were sent forcefully to the contract.
Vulnerability: tbtc - approveAndCall unused return parameter
Severity: low

Description:
`approveAndCall` always returns false because the return value `bool success` is never set.\\n```\\n/// @notice Set allowance for other address and notify.\\n/// Allows `\\_spender` to transfer the specified TDT\\n/// on your behalf and then ping the contract about it.\\n/// @dev The `\\_spender` should implement the `tokenRecipient` interface below\\n/// to receive approval notifications.\\n/// @param \\_spender Address of contract authorized to spend.\\n/// @param \\_tdtId The TDT they can spend.\\n/// @param \\_extraData Extra information to send to the approved contract.\\nfunction approveAndCall(address \\_spender, uint256 \\_tdtId, bytes memory \\_extraData) public returns (bool success) {\\n    tokenRecipient spender = tokenRecipient(\\_spender);\\n    approve(\\_spender, \\_tdtId);\\n    spender.receiveApproval(msg.sender, \\_tdtId, address(this), \\_extraData);\\n}\\n```\\n

Impact:

Recommended mitigation:
Return the correct success state.
Vulnerability: bitcoin-spv - Unnecessary memory allocation in BTCUtils  Pending
Severity: low

Description:
`BTCUtils` makes liberal use of `BytesLib.slice`, which returns a freshly-allocated slice of an existing bytes array. In many cases, the desired behavior is simply to read a 32-byte slice of a byte array. As a result, the typical pattern used is: `bytesVar.slice(start, start + 32).toBytes32()`.\\nThis pattern introduces unnecessary complexity and memory allocation in a critically important library: cloning a portion of the array, storing that clone in memory, and then reading it from memory. A simpler alternative would be to implement `BytesLib.readBytes32(bytes _b, uint _idx)` and other “memory-read” functions.\\nRather than moving the free memory pointer and redundantly reading, storing, then re-reading memory, `readBytes32` and similar functions would perform a simple length check and `mload` directly from the desired index in the array.\\nextractInputTxIdLE:\\n```\\n/// @notice Extracts the outpoint tx id from an input\\n/// @dev 32 byte tx id\\n/// @param \\_input The input\\n/// @return The tx id (little-endian bytes)\\nfunction extractInputTxIdLE(bytes memory \\_input) internal pure returns (bytes32) {\\n    return \\_input.slice(0, 32).toBytes32();\\n}\\n```\\n\\nverifyHash256Merkle:\\n```\\nuint \\_idx = \\_index;\\nbytes32 \\_root = \\_proof.slice(\\_proof.length - 32, 32).toBytes32();\\nbytes32 \\_current = \\_proof.slice(0, 32).toBytes32();\\n\\nfor (uint i = 1; i < (\\_proof.length.div(32)) - 1; i++) {\\n    if (\\_idx % 2 == 1) {\\n        \\_current = \\_hash256MerkleStep(\\_proof.slice(i \\* 32, 32), abi.encodePacked(\\_current));\\n    } else {\\n        \\_current = \\_hash256MerkleStep(abi.encodePacked(\\_current), \\_proof.slice(i \\* 32, 32));\\n    }\\n    \\_idx = \\_idx  1;\\n}\\nreturn \\_current == \\_root;\\n```\\n

Impact:

Recommended mitigation:
Implement `BytesLib.readBytes32` and favor its use over the `bytesVar.slice(start, start + 32).toBytes32()` pattern. Implement other memory-read functions where possible, and avoid the use of `slice`.\\nNote, too, that implementing this change in `verifyHash256Merkle` would allow `_hash256MerkleStep` to accept 2 `bytes32` inputs (rather than bytes), removing additional unnecessary casting and memory allocation.
Vulnerability: bitcoin-spv - ValidateSPV.validateHeaderChain does not completely validate input  Won't Fix
Severity: low

Description:
`ValidateSPV.validateHeaderChain` takes as input a sequence of Bitcoin headers and calculates the total accumulated difficulty across the entire sequence. The input headers are checked to ensure they are relatively well-formed:\\n```\\n// Check header chain length\\nif (\\_headers.length % 80 != 0) {return ERR\\_BAD\\_LENGTH;}\\n```\\n\\nHowever, the function lacks a check for nonzero length of `_headers`. Although the total difficulty returned would be zero, an explicit check would make this more clear.

Impact:

Recommended mitigation:
If `headers.length` is zero, return `ERR_BAD_LENGTH`
Vulnerability: bitcoin-spv - unnecessary intermediate cast
Severity: low

Description:
`CheckBitcoinSigs.accountFromPubkey()` casts the `bytes32` keccack256 hash of the `pubkey` to `uint256`, then `uint160` and then finally to `address` while the intermediate cast is not required.\\n```\\n/// @notice Derives an Ethereum Account address from a pubkey\\n/// @dev The address is the last 20 bytes of the keccak256 of the address\\n/// @param \\_pubkey The public key X & Y. Unprefixed, as a 64-byte array\\n/// @return The account address\\nfunction accountFromPubkey(bytes memory \\_pubkey) internal pure returns (address) {\\n    require(\\_pubkey.length == 64, "Pubkey must be 64-byte raw, uncompressed key.");\\n\\n    // keccak hash of uncompressed unprefixed pubkey\\n    bytes32 \\_digest = keccak256(\\_pubkey);\\n    return address(uint160(uint256(\\_digest)));\\n}\\n```\\n

Impact:

Recommended mitigation:
The intermediate cast from `uint256` to `uint160` can be omitted. Refactor to `return address(uint256(_digest))` instead.
Vulnerability: bitcoin-spv - unnecessary logic in BytesLib.toBytes32()
Severity: low

Description:
The heavily used library function `BytesLib.toBytes32()` unnecessarily casts `_source` to `bytes` (same type) and creates a copy of the dynamic byte array to check it's length, while this can be done directly on the user-provided `bytes` `_source`.\\n```\\nfunction toBytes32(bytes memory \\_source) pure internal returns (bytes32 result) {\\n    bytes memory tempEmptyStringTest = bytes(\\_source);\\n    if (tempEmptyStringTest.length == 0) {\\n        return 0x0;\\n    }\\n\\n    assembly {\\n        result := mload(add(\\_source, 32))\\n    }\\n}\\n```\\n

Impact:

Recommended mitigation:
```\\nfunction toBytes32(bytes memory \\_source) pure internal returns (bytes32 result) {\\n        if (\\_source.length == 0) {\\n            return 0x0;\\n        }\\n\\n        assembly {\\n            result := mload(add(\\_source, 32))\\n        }\\n    }\\n```\\n
Vulnerability: bitcoin-spv - redundant functionality  Won't Fix
Severity: low

Description:
The library exposes redundant implementations of bitcoins double `sha256`.\\nsolidity native implementation with an overzealous type correction issue 5.45\\n```\\n/// @notice Implements bitcoin's hash256 (double sha2)\\n/// @dev abi.encodePacked changes the return to bytes instead of bytes32\\n/// @param \\_b The pre-image\\n/// @return The digest\\nfunction hash256(bytes memory \\_b) internal pure returns (bytes32) {\\n    return abi.encodePacked(sha256(abi.encodePacked(sha256(\\_b)))).toBytes32();\\n}\\n```\\n\\nassembly implementation\\nNote this implementation does not handle errors when staticcall'ing the precompiled `sha256` contract (private chains).\\n```\\n/// @notice Implements bitcoin's hash256 (double sha2)\\n/// @dev sha2 is precompiled smart contract located at address(2)\\n/// @param \\_b The pre-image\\n/// @return The digest\\nfunction hash256View(bytes memory \\_b) internal view returns (bytes32 res) {\\n    assembly {\\n        let ptr := mload(0x40)\\n        pop(staticcall(gas, 2, add(\\_b, 32), mload(\\_b), ptr, 32))\\n        pop(staticcall(gas, 2, ptr, 32, ptr, 32))\\n        res := mload(ptr)\\n    }\\n}\\n```\\n

Impact:

Recommended mitigation:
We recommend providing only one implementation for calculating the double `sha256` as maintaining two interfaces for the same functionality is not desirable. Furthermore, even though the assembly implementation is saving gas, we recommend keeping the language provided implementation.
Vulnerability: bitcoin-spv - unnecessary type correction
Severity: low

Description:
The type correction `encodePacked().toBytes32()` is not needed as `sha256` already returns `bytes32`.\\n```\\nfunction hash256(bytes memory \\_b) internal pure returns (bytes32) {\\n    return abi.encodePacked(sha256(abi.encodePacked(sha256(\\_b)))).toBytes32();\\n}\\n```\\n

Impact:

Recommended mitigation:
Refactor to `return sha256(abi.encodePacked(sha256(_b)));` to save gas.
Vulnerability: tbtc - Where possible, a specific contract type should be used rather than address
Severity: low

Description:
Rather than storing addresses and then casting to the known contract type, it's better to use the best type available so the compiler can check for type safety.\\n`TBTCSystem.priceFeed` is of type `address`, but it could be type `IBTCETHPriceFeed` instead. Not only would this give a little more type safety when deploying new modules, but it would avoid repeated casts throughout the codebase of the form `IBTCETHPriceFeed(priceFeed)`, `IRelay(relay)`, `TBTCSystem()`, and others.\\n```\\nstruct Deposit {\\n\\n    // SET DURING CONSTRUCTION\\n    address TBTCSystem;\\n    address TBTCToken;\\n    address TBTCDepositToken;\\n    address FeeRebateToken;\\n    address VendingMachine;\\n    uint256 lotSizeSatoshis;\\n    uint8 currentState;\\n    uint256 signerFeeDivisor;\\n    uint128 undercollateralizedThresholdPercent;\\n    uint128 severelyUndercollateralizedThresholdPercent;\\n```\\n\\n```\\ncontract DepositFactory is CloneFactory, TBTCSystemAuthority{\\n\\n    // Holds the address of the deposit contract\\n    // which will be used as a master contract for cloning.\\n    address public masterDepositAddress;\\n    address public tbtcSystem;\\n    address public tbtcToken;\\n    address public tbtcDepositToken;\\n    address public feeRebateToken;\\n    address public vendingMachine;\\n    uint256 public keepThreshold;\\n    uint256 public keepSize;\\n```\\n\\nRemediation\\nWhere possible, use more specific types instead of `address`. This goes for parameter types as well as state variable types.

Impact:

Recommended mitigation:
Resolution\\nThis issue has been addressed with https://github.com/keep-network/tbtc/issues/507 and keep-network/tbtc#542.
Vulnerability: tbtc - Variable shadowing in DepositFactory
Severity: low

Description:
`DepositFactory` inherits from `TBTCSystemAuthority`. Both contracts declare a state variable with the same name, `tbtcSystem`.\\n```\\naddress public tbtcSystem;\\n```\\n

Impact:

Recommended mitigation:
Remove the shadowed variable.
Vulnerability: tbtc - Values may contain dirty lower-order bits  Pending
Severity: low

Description:
`FundingScript` and `RedemptionScript` use `mload` to cast the first bytes of a byte array to `bytes4`. Because `mload` deals with 32-byte chunks, the resulting `bytes4` value may contain dirty lower-order bits.\\nFundingScript.receiveApproval:\\n```\\n// Verify \\_extraData is a call to unqualifiedDepositToTbtc.\\nbytes4 functionSignature;\\nassembly { functionSignature := mload(add(\\_extraData, 0x20)) }\\nrequire(\\n    functionSignature == vendingMachine.unqualifiedDepositToTbtc.selector,\\n    "Bad \\_extraData signature. Call must be to unqualifiedDepositToTbtc."\\n);\\n```\\n\\nRedemptionScript.receiveApproval:\\n```\\n// Verify \\_extraData is a call to tbtcToBtc.\\nbytes4 functionSignature;\\nassembly { functionSignature := mload(add(\\_extraData, 0x20)) }\\nrequire(\\n    functionSignature == vendingMachine.tbtcToBtc.selector,\\n    "Bad \\_extraData signature. Call must be to tbtcToBtc."\\n);\\n```\\n

Impact:

Recommended mitigation:
Solidity truncates these unneeded bytes in the subsequent comparison operations, so there is no action required. However, this is good to keep in mind if these values are ever used for anything outside of strict comparison.
Vulnerability: tbtc - Revert error string may be malformed  Pending
Severity: low

Description:
`FundingScript` handles an error from a call to `VendingMachine` like so.\\n```\\n// Call the VendingMachine.\\n// We could explictly encode the call to vending machine, but this would\\n// involve manually parsing \\_extraData and allocating variables.\\n(bool success, bytes memory returnData) = address(vendingMachine).call(\\n    \\_extraData\\n);\\nrequire(success, string(returnData));\\n```\\n\\nOn a high-level revert, `returnData` will already include the typical “error selector”. As `FundingScript` propagates this error message, it will add another error selector, which may make it difficult to read the error message.\\nThe same issue is present in RedemptionScript:\\n```\\n(bool success, bytes memory returnData) = address(vendingMachine).call(\\_extraData);\\n// By default, `address.call` will catch any revert messages.\\n// Converting the `returnData` to a string will effectively forward any revert messages.\\n// https://ethereum.stackexchange.com/questions/69133/forward-revert-message-from-low-level-solidity-call\\n// TODO: there's some noisy couple bytes at the beginning of the converted string, maybe the ABI-coded length?\\nrequire(success, string(returnData));\\n```\\n

Impact:

Recommended mitigation:
Rather than adding an assembly-level revert to the affected contracts, ensure nested error selectors are handled in external libraries.