Normal behavior: Per docs/DESIGN.md §8, pool scope locks permanently once the underlying agreement has genuinely progressed past pre-attack staging — the intent being that once real risk activity begins, stakers' coverage commitment is frozen as the binding audit trail.
The issue: The lock fires on any observation of a state other than NOT_DEPLOYED/NEW_DEPLOYMENT — including the transient ATTACK_REQUESTED state, which is far from "real risk has begun." If the DAO subsequently rejects that attack request via rejectAttackRequest (a normal, routine governance action, not misuse), AttackRegistry deletes the agreement's info entirely and the registry reports NOT_DEPLOYED again — as if nothing ever happened. But ConfidencePool.scopeLocked is one-way and never resets, so the pool is permanently unable to have its scope edited, based on a request that was fully undone.
Likelihood: Medium — occurs whenever a sponsor takes the normal, expected first step (requestUnderAttack/requestUnderAttackForUnverifiedContracts) and the DAO rejects that specific request for any administrative reason, a routine governance path already built into AttackRegistry. A single pool interaction by anyone (not just the sponsor) is enough to observe ATTACK_REQUESTED and seal the lock before the rejection even happens.
Impact: Low — no funds are ever at risk; this is a pure loss of a documented sponsor capability (scope editing), not a fund-theft path. The sponsor permanently loses the ability to correct that specific pool's scope, even though the registry now looks identical to a never-touched agreement. Any staker who already deposited before the rejection is committed to a scope the sponsor can never fix, with no connection to any actual attack having occurred, until stakers coordinate withdrawing and migrating to a freshly created pool for the same agreement (the factory does support many pools per agreement, so this is a real but bounded workaround).
Fork test against the live BattleChain testnet (test/fork/BattleChainRejectedAttackScopeLock.fork.t.sol, run with BATTLECHAIN_TESTNET_RPC=https://testnet.battlechain.com forge test --match-path 'test/fork/*'). Deploys a fresh Agreement via the real AgreementFactory, creates a pool, drives the real registry through ATTACK_REQUESTED → soft-reject → NOT_DEPLOYED, and confirms the pool's lock survives:
Only lock scope on states that represent genuine progress into risk — the same boundary already used for risk-window semantics — rather than on ATTACK_REQUESTED, which the registry itself can cleanly reject and reset.
Note this is consistent with the pool's own existing philosophy elsewhere: ATTACK_REQUESTED is already one of the states where withdraw() stays open (stakers can still freely exit), so scope remaining editable through that same state doesn't trap anyone or contradict any other guarantee — it only defers the lock to the point real risk-relevant activity actually begins.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.