Why
server/web/settings.go:113 (SaveSettings) loads the ACL, then writes the whole ACL object back when visibility changes. forge/access/store.go:89 (Set) has no compare-and-swap or version check.
A repository admin can race an instance admin’s role removal: the repo admin’s stale save can restore their old ACL entry.
Also:
metadata.FindApprovedMatchand the run page count approval files without checking that the approver is still active and currently a maintainer/admin.ACL.CanReportaccepts any name in the runners list, including retired/demoted accounts.
Scope
- Add optimistic concurrency to
access.Store. - Make
SaveSettingsupdate only the fields it owns; never rewrite entries/runners/approval policy. - Make
SaveRepositoryAccessandAddRepositoryAccessuse the same CAS path. - At merge time, count only approvals from currently active accounts holding
maintainoradmin. - Wire an active-account/current-role predicate into
ACL.CanReport. - Add a race test: settings save vs role removal; the removal must win.
Acceptance
- A repo admin cannot restore a revoked role by racing settings.
- Demoted or retired approvers and runners no longer satisfy gates.