xanoutas/solidity-security-findings huggingface dataset:
Upgraded Q -> 2 from #82 [1713691250926]
Audit: 2024-04-gondi-findings
Severity: MEDIUM

Judge has assessed an item in Issue #82 as 2 risk. The relevant finding follows:

 ### Low-15 Collected fees are locked in the contract due to no sweeping or claiming methods
**Instances(1)**
In src/lib/pools/Pool.sol, fees are collected during loan [repayments](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/pools/Pool.sol#L444) or loan [liquidation](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/pools/Pool.sol#L461) flows and added to `uint256 public getCollectedFees;`. However, there are no methods to collect these fees, causing fees to be locked.

Recommendations:
Add a method to claim `getCollectedFees`.
Loan hashing method is missing protocolFee field, which allows 0 or incorrect protocolFee to pass in multiple flows. ProtocolFees can be lost.
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/utils/Hash.sol#L128-L135


# Vulnerability details

## Impact
Loan hashing method is missing protocolFee field, which allows 0 or incorrect protocolFee to pass in multiple flows. ProtocolFees can be lost.

## Proof of Concept
The loan hashing method in Hash.sol is missing the procotolFee field in struct Loan. As a result, all loan hash checks (if(_loan.hash() != _loans[_loanId]){//revert) will be invalid in the sense that any protocolFee input will pass the check. 
struct Loan has `protocolFee` field:
```solidity
//src/interfaces/loans/IMultiSourceLoan.sol
    struct Loan {
        address borrower;
        uint256 nftCollateralTokenId;
        address nftCollateralAddress;
        address principalAddress;
        uint256 principalAmount;
        uint256 startTime;
        uint256 duration;
        Tranche[] tranche;
|>      uint256 protocolFee;
    }
```
(https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/interfaces/loans/IMultiSourceLoan.sol#L132)
Hashing method doesn't encode procotolFee field:
```solidity
//src/lib/utils/Hash.sol
    function hash(IMultiSourceLoan.Loan memory _loan) internal pure returns (bytes32) {
...
     
        return keccak256(
            abi.encode(
                _MULTI_SOURCE_LOAN_HASH,
                _loan.borrower,
                _loan.nftCollateralTokenId,
                _loan.nftCollateralAddress,
                _loan.principalAddress,
                _loan.principalAmount,
                _loan.startTime,
                _loan.duration,
|>                keccak256(trancheHashes) //@audit missing protocolFee field
            )
        );
```
(https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/utils/Hash.sol#L128-L135)
As a result, multiple flows will have an invalid loan hashing check([_baseLoanChecks()](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L645-L647)). These flows include refianceFull, refinancePartial,refianceFromLoanExecutionData,addNewTranche, repayLoan, etc. This allows any protocolFee (e.g.0) to pass, protocol might lose fees.

## Tools Used
Manual

## Recommended Mitigation Steps
Add protocolFee field in loan hashing method.


## Assessed type

Other
Invalid maxTranches check can result in maxTranche cap to be exceeded
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L359-L360


# Vulnerability details

## Impact
Invalid maxTranches check can result in maxTranche cap to be exceeded

## Proof of Concept
In src/lib/loans/MultiSourceLoan.sol, there's max cap for the number of tranches in a loan as defined as [getMaxTranches](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L50-L51). This cap can be exceeded.

There are two main vulnerabilities:
(1) `getMaxTranches` is not checked in some key flows where tranches can be added. These including [emitLoan()](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L124), refinancePartial()(->[_addTrancheFromPartial()](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L289)), [refinanceFromLoanExecutionData()](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L306).

(2) Where `getMaxTranches` is checked, the check is invalid. Only `_loan.tranche.length == getMaxTranches` is checked. But combined with (1), when number of tranches exceeds `getMaxTranches` in other flows, this check is invalid.

```solidity
//src/lib/loans/MultiSourceLoan.sol
    function addNewTranche(
        RenegotiationOffer calldata _renegotiationOffer,
        Loan memory _loan,
        bytes calldata _renegotiationOfferSignature
    ) external nonReentrant returns (uint256, Loan memory) {
...
          //@audit change to _loan.tranch.length >= getMaxTranches
|>        if (_loan.tranche.length == getMaxTranches) {
            revert TooManyTranchesError();
        }

...
```
(https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L359)

## Tools Used
Manual
## Recommended Mitigation Steps
Add missing checks on `getMaxTranches` for all flows that might add tranches. And in addNewTranche, change into _loan.tranch.length >= getMaxTranches.


## Assessed type

Other
Anyone can call `UserVault::mint` for any deployed vault
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/UserVault.sol#L110


# Vulnerability details

## Impact
There is a missing `onlyOwner` in the `mint` function definition that allows anyone to mint NFTs from this vault. This is not correct as it is an unauthorized minting of NFTs.

## Proof of Concept
The flawed code is in 

[**UserVault.sol#L110**](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/UserVault.sol#L110)

```solidity
    /// @inheritdoc IUserVault
    function mint() external returns (uint256) {
        uint256 _vaultId;
        unchecked {
            _vaultId = ++_nextId;
        }
        _mint(msg.sender, _vaultId);
        return _vaultId;
    }
```

Moreover, the contract inherits from `Owned` which means there must be functions restricted via the `onlyOwner` modifier, which is not used in the whole file.

## Recommended Mitigation Steps

```diff
    /// @inheritdoc IUserVault
-   function mint() external returns (uint256) {
+   function mint() external returns (uint256) onlyOwner {
        uint256 _vaultId;
        unchecked {
            _vaultId = ++_nextId;
        }
        _mint(msg.sender, _vaultId);
        return _vaultId;
    }
```


## Assessed type

Access Control
A malicious user can take on a loan using an existing borrower's collateral in refinanceFromLoanExecutionData()
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L319


# Vulnerability details

## Impact
A malicious user can take on a loan using an existing borrower's collateral in `refinanceFromLoanExecutionData()`.

## Proof of Concept
In MultiSourceLoan.sol, `refinanceFromLoanExecutionData()` doesn't check whether _loan.borrower == _loanExecutionData.borrower, which is open rooms for exploits.

BorrowerB(malicious) can sign a `_loanExecutionData` offer and initiate a `refinanceFromLoanExecutionData()` call with BorrowerA's loan. BorrowerB will use BorrowerA's collateral for his loan. 

There are (2) vulnerabilities here:
(1) `_validateExecutionData` will not check whether _loan.borrower == _executionData.borrower. In addition,  it will directly [bypass the check](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L792) on executionData's borrower signature as long as msg.sender!=_loan.borrower.

(2) `refinanceFromLoanExecutionData()` doesn't check whether the new loanExecutiondata (`_loanExecutionData`) has the [same nft tokenId](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L334)(`executionData.tokenId`) as the [existing loan](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L308)(`_loan.nftCollateralTokenId`).

As a result, if _loanExecutionData.borrower(BorrowerB) initiates `refinanceFromLoanExecutionData()` call, the following would happen:
a) msg.sender != _loan.borrower(BorrwerA), this bypass _validateExecutionData's signature check. Also, tt will not revert because no checks on [address _borrower](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L319)(_loan.borrower_==_loanExecutionData.borrower;

b)There is no check on _loanExecutionData.tokenId . As long as _loan and _loanExecutionData has [the same principalAddress and the same nftCollateralAddress](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L322), `_processOffersFromExecutionData()` will succeed in [transferring principal loans to BorrowerB](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L1025).

c) as long as the _loan.borrower(BorrowerA) has the funds for [repayment](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L946). (Note: BorrowerA might have approved MultiSourceLoan.sol for asset transfer if they are ready for repayments.), the tx will succeed. And BorrowerA's collateral will continually be locked for BorrowerB's new loan.

Note that the above steps can also happen in a
The check on unlocked loan during refinance can be bypassed in addNewTranche
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L356-L358


# Vulnerability details

## Impact
The check on unlocked loan during refinance can be bypassed in addNewTranche

## Proof of Concept
In src/lib/loans/MultiSourceLoan.sol, when a lender initiated a refinance, checks ([isLoanlocked()](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L734)) need to be added to ensure only unlocked loans can be refinanced. 

However, this check can be bypassed through `addNewTranche`, which can also be initiated by the lender.

In `addNewTranche()`, there is no check on whether msg.sender is borrower or lender, only the lender signature (`_renegotiationOfferSignature`) is checked. As a result, a lender can initiate the refinance flow through `addNewTranche()` at any time bypassing `isLoanlocked()` check.

```solidity
//src/lib/loans/MultiSourceLoan.sol
    function addNewTranche(
        RenegotiationOffer calldata _renegotiationOffer,
        Loan memory _loan,
        bytes calldata _renegotiationOfferSignature
    ) external nonReentrant returns (uint256, Loan memory) {
...
        //@audit no check on isLoanLocked(), and no check on msg.sender
        _baseLoanChecks(loanId, _loan);
        _baseRenegotiationChecks(_renegotiationOffer, _loan);
        _checkSignature(_renegotiationOffer.lender, _renegotiationOffer.hash(), _renegotiationOfferSignature);
...
        Loan memory loanWithTranche = _addNewTranche(newLoanId, _loan, _renegotiationOffer);
...
```
(https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L356-L358)

For comparison, in [refinanceFull](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L178) or [refinancePartial](https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L242-L243) flows `isLoanLocked()` is checked when lender initiated the call. 

## Tools Used

Manual

## Recommended Mitigation Steps
Add check for `isLoanLocked()` in `addNewTranche` flow. 



## Assessed type

Other
The `getMaxTranches` check can be bypassed
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L377


# Vulnerability details

## Impact
In MultiSourceLoan, each loan has a limited number of tranches. This limit is held in the `getMaxTranches` variable. When users want to add a new tranche, this limit is checked to ensure a loan cannot have more tranches than the `getMaxTranches` limit. The check is presented in `addNewTranche()` function.

However, this check is absent in the `emitLoan()` function, making it easy to bypass the other check in function `addNewTranche()`.


## Proof of Concept
```solidity
function addNewTranche(
    RenegotiationOffer calldata _renegotiationOffer,
    Loan memory _loan,
    bytes calldata _renegotiationOfferSignature
) external nonReentrant returns (uint256, Loan memory) {
    ...
    if (_loan.tranche.length == getMaxTranches) {
        revert TooManyTranchesError();
    }
    ...
}
```

As shown above, the check uses the equal comparator. So, users could create a loan with `getMaxTranches + 1` tranches through the `emitLoan()` function, then they can always add more tranches. This is because the length of `_loan.tranche.length` is already larger than `getMaxTranches` and continues to grow after calling `addNewTranche()`.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Include a `getMaxTranches` check in the `emitLoan()` function and modify the check to
```solidity
if (_loan.tranche.length >= getMaxTranches) {
    revert TooManyTranchesError();
}
```



## Assessed type

Other
Function `addNewTranche()` should use `protocolFee` from `Loan` struct
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/loans/MultiSourceLoan.sol#L377


# Vulnerability details

## Impact
The protocol fee value is recorded and stored in the Loan struct when a new loan is issued. However, when adding a new tranche, the function uses the current value of `protocolFee.fraction` instead of the value stored in the Loan struct. This could result in inconsistencies in fee collection, as the protocol fee value might be updated by the admin, while the value stored in the Loan struct remains unchanged.

```solidity
if (_renegotiationOffer.fee > 0) {
    /// @dev Cached
    ProtocolFee memory protocolFee = _protocolFee;
    ERC20(_loan.principalAddress).safeTransferFrom(
        _renegotiationOffer.lender,
        protocolFee.recipient,
        _renegotiationOffer.fee.mulDivUp(protocolFee.fraction, _PRECISION) // @audit Use protocolFee from Loan instead
    );
}
```

## Proof of Concept
The protocol fee value is stored in the Loan struct when a new loan is opened.
```solidity
function _processOffersFromExecutionData(
  ...
) ... {
  Loan memory loan = Loan(
      _borrower,
      _tokenId,
      _nftCollateralAddress,
      _principalAddress,
      totalAmount,
      block.timestamp,
      _duration,
      tranche,
      protocolFee.fraction
  );
}
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Consider using `_loan.protocolFee` instead of `protocolFee.fraction` in the `addNewTranche()` function.



## Assessed type

Other
Function `Pool.validateOffer()` does not work correctly in case `principalAmount > currentBalance`
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/pools/Pool.sol#L411


# Vulnerability details

## Impact
In Pool contract, undeployed funds could be deposited to Aave or Lido to earn base yield. When an offer of Pool is accepted from MultiSourceLoan, the function `validateOffer()` is called to validate the terms and also to pull the undeployed funds back in case the contract balance is insufficient.

```solidity
if (principalAmount > undeployedAssets) {
    revert InsufficientAssetsError();
} else if (principalAmount > currentBalance) {
    IBaseInterestAllocator(getBaseInterestAllocator).reallocate(
        currentBalance, principalAmount - currentBalance, true // @audit Incorrect
    );
}
```

However, the input params of `reallocate()` are incorrect, resulting in the contract balance might still be insufficient for the loan after calling the function.

## Proof of Concept
Consider the scenario
1. Assume we have `currentBalance = 500`, `baseRateBalance = 1000 usdc` and `principalAmount = 700 usdc`.
2. Since the current contract balance is insufficient (500 < 700), `reallocate()` will be called with input
```solidity
reallocate(currentBalance, principalAmount - currentBalance, true)
reallocate(500, 200, true)
```
3. In function `reallocate()` shown in the code snippet below, we can see that in case `_currentIdle > _targetIdle`, the contract even deposits more funds to AavePool instead of withdrawing.

```solidity
function reallocate(uint256 _currentIdle, uint256 _targetIdle, bool) external {
    address pool = _onlyPool();
    if (_currentIdle > _targetIdle) {
        uint256 delta = _currentIdle - _targetIdle;
        ERC20(_usdc).transferFrom(pool, address(this), delta);
        IAaveLendingPool(_aavePool).deposit(_usdc, delta, address(this), 0);
    } else {
        uint256 delta = _targetIdle - _currentIdle;
        IAaveLendingPool(_aavePool).withdraw(_usdc, delta, address(this));
        ERC20(_usdc).transfer(pool, delta);
    }

    emit Reallocated(_currentIdle, _targetIdle);
}
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Call `reallocate(0, principalAmount - currentBalance, true)` instead.



## Assessed type

Other
Collected fees are never transferred out of Pool contract
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/pools/Pool.sol#L444


# Vulnerability details

## Impact
Lenders in the Gondi protocol could be EOA or Gondi Pool. The Gondi Pool, an ERC4626, allows anyone to deposit funds and earn yield from lending on Gondi. When a loan is repaid or liquidated, the pool deducts a fee from the received amount before adding the rest to the pool balance. As shown in the `loanRepayment()` function, the fees are calculated by calling `processFees()` and then added to `getCollectedFees`. After that, the accounting function `_loanTermination()` is called with the amount being `received - fees`.

However, this fee is credited to `getCollectedFees` but never transferred out of the pool. As a result, these funds remain locked in the contract indefinitely.

```solidity
function loanRepayment(
    uint256 _loanId,
    uint256 _principalAmount,
    uint256 _apr,
    uint256,
    uint256 _protocolFee,
    uint256 _startTime
) external override onlyAcceptedCallers {
    uint256 netApr = _netApr(_apr, _protocolFee);
    uint256 interestEarned = _principalAmount.getInterest(netApr, block.timestamp - _startTime);
    uint256 received = _principalAmount + interestEarned;
    uint256 fees = IFeeManager(getFeeManager).processFees(_principalAmount, interestEarned);
    getCollectedFees += fees; // @audit getCollectedFees is never transfer out
    _loanTermination(msg.sender, _loanId, _principalAmount, netApr, interestEarned, received - fees);
}
```

## Proof of Concept
The `processFees()` function only calculates the fee but doesn't transfer anything.
```solidity
function processFees(uint256 _principal, uint256 _interest) external view returns (uint256) {
    /// @dev cached
    Fees memory __fees = _fees;
    return _principal.mulDivDown(__fees.managementFee, PRECISION)
        + _interest.mulDivDown(__fees.performanceFee, PRECISION);
}
```

Then after `getCollectedFees` is credited for `fees`, we can see this `getCollectedFees` is never transferred out of the pool.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add a function to collect the credited fees `getCollectedFees` from the pool in the FeeManager contract.



## Assessed type

Other
Anyone can remove existing term without queueing through `setTerms()`
Audit: 2024-04-gondi-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-04-gondi/blob/b9863d73c08fcdd2337dc80a8b5e0917e18b036c/src/lib/pools/PoolOfferHandler.sol#L110


# Vulnerability details

## Impact
In PoolOfferHandler, new terms require a two-step process for setting (`setTerms()` and `confirmTerms()`). The `setTerm()` function is `onlyOwner`, but the `confirmTerms()` function can be called by anyone. This function uses the provided input `__terms` from the caller to execute the logic. This could enable an attacker to remove all existing terms, even if the owner does not intend to do so (without pending through the `setTerms()` function).

```solidity
function confirmTerms(TermsKey[] calldata _termKeys, Terms[] calldata __terms) external { 
    if (block.timestamp - pendingTermsSetTime < NEW_TERMS_WAITING_TIME) {
        revert TooSoonError();
    }
    for (uint256 i = 0; i < __terms.length; i++) {
        if (_termKeys[i].duration > getMaxDuration) {
            revert InvalidDurationError();
        }
        uint256 pendingAprPremium = _pendingTerms[_termKeys[i].collection][_termKeys[i].duration][_termKeys[i]
            .maxSeniorRepayment][__terms[i].principalAmount];
        // @audit Can be used to remove terms without pending through setTerm()
        if (pendingAprPremium != __terms[i].aprPremium) {
            revert InvalidTermsError();
        }
        _terms[_termKeys[i].collection][_termKeys[i].duration][_termKeys[i].maxSeniorRepayment][__terms[i]
            .principalAmount] = __terms[i].aprPremium;
        delete _pendingTerms[_termKeys[i].collection][_termKeys[i].duration][_termKeys[i]
            .maxSeniorRepayment][__terms[i].principalAmount];
    }
    pendingTermsSetTime = type(uint256).max;

    emit TermsSet(_termKeys, __terms);
}
```


## Proof of Concept
Consider the following scenario
1. The pool has 5 existing terms. Now the owner wants to create a new term in the Pool, so they call `setTerms()` with the `__terms` they want to set up. The new term is pending confirmation after a waiting period.
2. After `NEW_TERMS_WAITING_TIME`, an attacker calls `confirmTerms()` with `_termKeys` set to all 5 existing terms and `__terms.aprPremium = 0`.
3. The function executes with the input provided by the attacker. Since the `pendingAprPremium` of these terms is reset to 0 after it is confirmed earlier, the check `if (pendingAprPremium != __terms[i].aprPremium)` is bypassed. The attacker could set the `_terms[][][][]` mapping of existing loans to 0.


## Tools Used
Manual Review

## Recommended Mitigation Steps
Only allow the owner to call `confirmTerms()`.



## Assessed type

Invalid Validation