Their chef on my rota, at their pay rate
Four rota routes named a person in the body, where our venue gate never looks. A manager of one kitchen could log a shift for another company's chef.
A food hall runs several companies on one system. The thing that keeps them apart is a gate that runs before any route, reads every place a request names a venue, and refuses the request if the person asking is not allowed that venue. It reads the address of the request. It does not read the body. We have found that gap five times now, one route at a time, and each fix stopped at its own route. This time we asked the question properly: which routes name a person in the body, and what fences each one?
Eleven places read a staff id from a request body. Four of them had no fence on the person. Each was driven as a manager granted exactly one venue, against a seeded database with two companies in it, through the real request path rather than by reading the code.
Their employee, my venue, their pay rate
Logging a shift checks the venue. It did not check the person. A manager of one kitchen could log a shift for the other company's chef at their own venue, and the server answered with the chef's name, handed straight back. The chef's hourly rate was written into the shift as a snapshot, where it becomes pay on the manager's timesheet. The other company never sees the row, because their timesheets are fenced on venue, and the row carries the wrong one.
An oracle hiding in a useful check
Proposing a rota shift runs a clash check across every venue on purpose. The fault it exists to catch is one person booked in two places at once, and that only works if it looks everywhere. Asked about somebody who is not yours, it answered with their name, the venue they work at and their hours, from guessing a small integer. The cross-venue scope is right and is untouched. Asking it about a stranger is what was wrong, and that is the only part that changed.
Editing another company's grid
Editing and deleting a rota shift took no user at all and left the venue to the gate, the same way our courier routes do, correctly. But the gate had no entry for the rota table, so it had nothing to resolve. Rewriting another company's published shift answered ok, with the start, finish and note all mine to set. On a published row that marks the shift as amended, which is exactly what tells their staff their hours have changed. Fixed in the gate's table of families rather than in the handlers, for the same reason as the tab routes a few lines above it.
The one the gate could never catch
Bulk-accepting suggested shifts carries each shift's venue nested inside a list in the body. The gate collects a top-level venue id, and this was not one. A manager of one venue posted a shift naming another company's venue and got it written into their rota. Not their employee onto my grid, but a shift onto theirs. This one is checked in the handler, because there is nowhere else it can be checked.
Each fix proved on its own, and the controls that carry the weight
Thirteen tests. Reverting the gate entry fails three of them and nothing else. Reverting the person fence on shifts fails two. Reverting the rota's fails three. Reverting the bulk venue check fails one. No test passes for another fix's reason, which is the only way to know that four fixes are four fixes.
The controls matter more than the attacks, because a fence that refused everybody would pass every attack above. A manager logging a shift and bulk-accepting for their own staff at their own venue still works. A real clash at their own venue is still reported in full, by name and venue, so the oracle test is not never mention another shift. And the owner, who is allowed every venue, can still edit the shift the manager was refused.
One older test failed on purpose. It asserts as a premise that four tables are not on the path gate, and says in its message to re-read the test if one ever is. The rota table had just become one. Written by an earlier pass, it did exactly its job: the premise changed and the test said which. The other three tables are still ungated, and that file's findings still rest on them.