Bulk operations: TOCTOU race on ticket status used for workflow validation #34

Open
opened 2026-08-31 21:29:43 -04:00 by jared · 0 comments
Owner

Severity: Medium

models/BulkOperationsModel.php (~lines 137-199): ticket state used for workflow-transition validation (ticketsById) is a snapshot read via getTicketsByIds() before begin_transaction(), and the later UPDATE (via TicketModel::updateTicket) has no WHERE status = ? guard or row lock re-checking that status hasn't changed since the snapshot.

Impact: Two concurrent admin actions on the same ticket (e.g. an overlapping single-ticket update and a bulk operation) can both validate against the same stale status and then both apply transitions — the second write overwrites without re-verifying the transition is still legal from the ticket's now-current status. This can bypass Workflow Designer rules under concurrency, similar in spirit to the bug fixed in #21 but via a race instead of a missing check.

Fix: Re-validate the transition against the current DB row inside the transaction (e.g. SELECT ... FOR UPDATE or a conditional UPDATE ... WHERE status = ? with affected-rows check) rather than trusting the pre-transaction snapshot.

**Severity:** Medium `models/BulkOperationsModel.php` (~lines 137-199): ticket state used for workflow-transition validation (`ticketsById`) is a snapshot read via `getTicketsByIds()` **before** `begin_transaction()`, and the later `UPDATE` (via `TicketModel::updateTicket`) has no `WHERE status = ?` guard or row lock re-checking that status hasn't changed since the snapshot. **Impact:** Two concurrent admin actions on the same ticket (e.g. an overlapping single-ticket update and a bulk operation) can both validate against the same stale status and then both apply transitions — the second write overwrites without re-verifying the transition is still legal from the ticket's now-current status. This can bypass Workflow Designer rules under concurrency, similar in spirit to the bug fixed in #21 but via a race instead of a missing check. **Fix:** Re-validate the transition against the current DB row inside the transaction (e.g. `SELECT ... FOR UPDATE` or a conditional `UPDATE ... WHERE status = ?` with affected-rows check) rather than trusting the pre-transaction snapshot.
jared added the concurrencypriority/mediumworkflow labels 2026-09-08 10:15:43 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/tinker_tickets#34