Skip to content

Fix TOCTOU race in last-admin protection #28

Description

@comwanga

Summary

The last-admin protection introduced around PR #27 performs an unlocked admin-count read outside the transaction that applies the moderation change. Under concurrent admin actions, two requests can observe the same pre-update state and both proceed based on a stale count.

This creates a TOCTOU race in the last-admin guard.

Context

This was called out by the maintainer during review of #27 as a non-blocking follow-up to track separately.

Problem

The current flow is effectively:

  1. Read/count active admins.
  2. Decide whether the target admin can be blocked/demoted.
  3. Perform the state change separately.

Because the count read is not protected by the same transaction/locking boundary as the mutation, concurrent requests can both pass the guard before either write commits.

Expected behavior

The last-admin invariant should be enforced atomically so that concurrent moderation actions cannot both succeed when doing so would leave the system without an active admin.

Suggested direction

  • Move the relevant read/check and mutation into one transaction.
  • Use an appropriate locking or serialization strategy supported by the current database layer so the invariant is preserved under concurrent requests.
  • Add a regression test covering concurrent attempts to block/demote admins when only the minimum allowed admin set remains.

Acceptance criteria

  • Concurrent admin moderation requests cannot bypass the last-admin protection.
  • The validation and state change are atomic with respect to the last-admin invariant.
  • Regression coverage demonstrates the race is prevented.
  • Existing behavior from fix: prevent blocked admins from retaining access #27 remains intact.

Related: #27

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions