Skip to content

Bound the allowed-depositors list to prevent unbounded Vec growth in the vault #405

@greatest0fallt1me

Description

@greatest0fallt1me

Description

The vault stores its allowed-depositor allowlist as an unbounded Vec<Address> under StorageKey::DepositorList. Both set_allowed_depositor (contracts/vault/src/lib.rs:496) and the convenience alias add_address (:1251) push_back a new entry with no size limit, and is_authorized_depositor (:386) plus is_authorized_depositor-backed deposit iterate the list via list.contains(&caller). An owner who keeps adding depositors (or a buggy script) can grow this Vec until reading or scanning it during deposit exceeds Soroban's per-transaction resource budget, bricking the deposit path and the get_allowed_depositors / get_allowlist views.

This mirrors the bounded-batch hardening already applied elsewhere (vault MAX_BATCH_SIZE = 50, revenue-pool batch_distribute cap documented in SECURITY.md). This issue adds an explicit cap on the allowlist size.

Requirements and context

Functional

  • Introduce a constant, e.g. pub const MAX_ALLOWED_DEPOSITORS: u32 = 100; (choose a value and justify it against Soroban limits, like the existing MAX_BATCH_SIZE rationale).
  • In set_allowed_depositor and add_address, reject an addition that would exceed the cap with a new VaultError::AllowlistFull (only when the address is not already present — re-adding an existing entry is a no-op today and must stay so).
  • Keep the clear paths (clear_allowed_depositors at :526, clear_all at :1270, and set_allowed_depositor(None)) working so the owner can always shrink the list.
  • Document the bound: a depositor set larger than the cap should use a different access pattern (e.g. owner-as-sole-depositor with off-chain authorization).

Non-functional / repo conventions

  • No change to the deposit authorization semantics beyond the new cap; owner is always authorized (is_authorized_depositor returns true for owner).
  • Update SECURITY.md (Vault-Specific Risks: "Batch operations respect individual limits" and unbounded-growth note), docs/interfaces/vault.json (new error code + set_allowed_depositor panic list), and contracts/vault/STORAGE.md / ACCESS_CONTROL.md as relevant.
  • Keep WASM within budget (scripts/check-wasm-size.sh).

Acceptance criteria

  • MAX_ALLOWED_DEPOSITORS constant added and enforced in both set_allowed_depositor and add_address.
  • Adding beyond the cap returns VaultError::AllowlistFull; re-adding an existing address remains a no-op.
  • Clearing / removing always succeeds regardless of current size.
  • New error code documented in docs/interfaces/vault.json; SECURITY.md updated with the bound and rationale.
  • Boundary tests: fill to exactly the cap (succeeds), one more (rejected), duplicate add at cap (no-op success), clear then re-add.
  • Line coverage stays >= 95% (scripts/coverage.sh).

Suggested execution

1. Fork the repo and create a branch

git checkout -b fix/vault-bound-allowlist

2. Implement changes

  • contracts/vault/src/lib.rs — add MAX_ALLOWED_DEPOSITORS, VaultError::AllowlistFull, and length checks in set_allowed_depositor and add_address.
  • Update docs/interfaces/vault.json, SECURITY.md, contracts/vault/STORAGE.md / ACCESS_CONTROL.md.

3. Write/extend tests in contracts/vault/src/test.rs:

  • add MAX_ALLOWED_DEPOSITORS distinct depositors (all succeed), assert get_allowed_depositors().len() equals the cap;
  • the next distinct add returns AllowlistFull;
  • re-adding an already-present address at the cap is a no-op success;
  • clear_allowed_depositors empties the list and a subsequent add succeeds again;
  • owner can still deposit when the list is full.

4. Test and commit

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --workspace
./scripts/check-wasm-size.sh
./scripts/coverage.sh

Example commit message

fix(vault): bound allowed-depositors list with MAX_ALLOWED_DEPOSITORS

Guidelines

  • Coverage must stay >= 95% (scripts/coverage.sh), CI-enforced via .github/workflows/coverage.yml.
  • NatSpec-style /// docs on the touched entrypoints; keep docs/interfaces/vault.json and SECURITY.md authoritative.
  • Include a short note on the threat addressed (storage-exhaustion DoS of the deposit path) in the PR description.
  • Timeframe: 96 hours.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions