FoundrySolidityLayer 2
7.25 ETH
Submission Details
Impact: low
Likelihood: low

Scope Account Zero-Address Not Checked

Author Revealed upon completion

Root + Impact

Description

_replaceScope() validates each account against the agreement's isContractInScope and checks for duplicates, but never checks account != address(0). If the agreement contract returns true for address(0), a zero-address entry would be admitted into the pool scope. This would break any off-chain consumer iterating getScopeAccounts() and calling .balanceOf or similar on address(0).

// ConfidencePool.sol — _replaceScope() lines 764-772
@> for (uint256 i; i < accounts.length; ++i) {
address account = accounts[i];
if (isAccountInScope[account]) revert DuplicateAccount(account);
@> if (!IAgreement(agreement).isContractInScope(account)) revert AccountNotInAgreementScope(account);
// Missing: if (account == address(0)) revert ZeroAddress();
_scopeAccounts.push(account);

Risk

Likelihood: Low — requires a BattleChain agreement to report address(0) as in-scope, or the owner to pass it in the initial scope array.

Impact: Low — no funds lost. Off-chain consumers iterating the scope may encounter errors when attempting to query address(0).

Proof of Concept

File: L12-ScopeAccount-ZeroAddress.poc.t.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {ConfidencePool} from "src/ConfidencePool.sol";
import {IConfidencePool} from "src/interfaces/IConfidencePool.sol";
import {BaseConfidencePoolTest} from "test/helpers/BaseConfidencePoolTest.sol";
/// @notice PoC for L-12: Scope account elements not checked for address(0)
contract L12_ScopeAccount_ZeroAddress_POC is BaseConfidencePoolTest {
function testPOC_L12_scopeAccount_notCheckedForZero() external {
// _replaceScope checks for duplicates and agreement membership,
// but never checks account != address(0)
// If the agreement returns true for address(0), it would be admitted.
// Set up the agreement to accept address(0) in scope
agreementContract.setContractInScope(address(0), true);
ConfidencePool freshPool = _deployPool();
address[] memory scopeWithZero = new address[](1);
scopeWithZero[0] = address(0);
// This succeeds — no zero-address check in _replaceScope
freshPool.setPoolScope(scopeWithZero);
// address(0) is now in the pool's scope
assertTrue(freshPool.isAccountInScope(address(0)));
}
}

Run: forge test --match-path 'L12-ScopeAccount-ZeroAddress.poc.t.sol' -vv

Recommended Mitigation

Add zero-address validation in the _replaceScope loop:

+ if (account == address(0)) revert ZeroAddress();

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!