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

Unbounded and Unvalidated Scope Array (`accounts[]`) Allows Misconfiguration, DoS, and Ambiguous Attack Surface

Author Revealed upon completion

Description

The createPool function accepts an accounts[] array that defines the attack scope for the ConfidencePool. The factory performs no validation on this array:

  • no maximum length,

  • no minimum length,

  • no duplicate filtering,

  • no structural or semantic checks.

This allows a pool creator to submit extremely large arrays, arrays containing repeated addresses, or arrays containing irrelevant or misleading contracts. These conditions can cause gas exhaustion, ambiguous scope interpretation, or incorrect Registry behavior, ultimately degrading the reliability of the Safe Harbor protection model.

function createPool(
address agreement,
address stakeToken,
uint256 expiry,
uint256 minStake,
address recoveryAddress,
address[] calldata accounts
) external whenNotPaused returns (address pool) {
if (agreement == address(0) || stakeToken == address(0)) revert ZeroAddress();
if (recoveryAddress == address(0)) revert ZeroAddress();
if (!allowedStakeToken[stakeToken]) revert StakeTokenNotAllowed();
if (expiry < block.timestamp + _MIN_EXPIRY_LEAD) revert ExpiryTooSoon();
// aderyn-fp-next-line(reentrancy-state-change)
if (!safeHarborRegistry.isAgreementValid(agreement)) revert InvalidAgreement();
// aderyn-fp-next-line(reentrancy-state-change)
if (IAgreement(agreement).owner() != msg.sender) revert UnauthorizedCreator();
// Salt incorporates the per-agreement index so an agreement can back many pools while
// keeping deterministic, collision-free clone addresses.
bytes32 salt = keccak256(abi.encode(agreement, _poolsByAgreement[agreement].length));
pool = Clones.cloneDeterministic(poolImplementation, salt);
// aderyn-fp-next-line(reentrancy-state-change)
IConfidencePool(pool)
.initialize(
agreement,
stakeToken,
address(safeHarborRegistry),
defaultOutcomeModerator,
expiry,
minStake,
recoveryAddress,
msg.sender,
accounts
);
_poolsByAgreement[agreement].push(pool);
emit PoolCreated(
agreement,
pool,
stakeToken,
expiry,
minStake,
recoveryAddress,
defaultOutcomeModerator,
address(safeHarborRegistry)
);
}

Risk

Likelihood:

Reason 1 — When pool creators provide large or malformed scope arrays**
This occurs whenever a creator supplies an excessively large accounts[] list (e.g., thousands of entries) or includes repeated addresses. Because the factory does not enforce any constraints, the initialization call may consume excessive gas or create a pool with an ambiguous or misleading scope.

Reason 2 — When stakers rely on scope correctness for risk evaluation**
This occurs whenever stakers assume the scope is accurate and meaningful. If the creator includes duplicate or irrelevant addresses, the Registry may behave unpredictably, and stakers may unknowingly stake into a pool with an incorrect or misleading attack surface.

Impact:

Moderate to Critical, depending on how the malformed scope affects Registry behavior.

  • DoS Risk: Extremely large arrays can cause pool initialization to run out of gas, preventing pool creation or causing partial initialization failures.

  • Ambiguous Scope: Duplicate or irrelevant addresses may cause Registry misclassification, leading to incorrect SURVIVED or CORRUPTED outcomes.

  • Staker Misleading: Stakers may believe certain contracts are protected when they are not, or vice versa.

  • Moderator Confusion: Moderators may misjudge CORRUPTED events due to unclear or bloated scope definitions.

Proof of Concept

  1. A pool creator submits a scope array containing 5,000 addresses, including duplicates and irrelevant contracts.

  2. The factory accepts the array without validation and deploys the pool.

  3. During initialization, gas usage spikes; some environments may fail to deploy due to out‑of‑gas.

  4. The Registry later attempts to evaluate the scope but encounters repeated or meaningless entries, causing inconsistent or misleading state transitions.

  5. A CORRUPTED event occurs in a contract that stakers believed was in scope, but due to misconfiguration, the Registry does not detect it.

  6. Moderator flags SURVIVED, and stakers lose their bonus despite the project being compromised.

Recommended Mitigation

1. Enforce Maximum Scope Length

Limit accounts.length to a reasonable upper bound (e.g., 50–100 entries) to prevent gas‑based DoS.

2. Reject Duplicate Addresses

Perform a uniqueness check on accounts[] before pool creation.

3. Validate Contract Types

Ensure each address in accounts[] is a contract and optionally enforce interface checks (e.g., bytecode size > 0).

4. Require Explicit Scope Declaration

Force creators to provide a minimal, well‑defined scope and reject empty or nonsensical arrays.

5. Add Off‑Chain or On‑Chain Scope Verification

Integrate Safe Harbor Registry or DAO governance to approve scope lists before pool creation.

Support

FAQs

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

Give us feedback!