Security · Menus · Food halls

Does dish 1,234 exist? Answered by a status code

Put a dish on a menu by its number and a real dish answered differently from a made-up one, so ids could be counted. Two obvious fixes broke meal deals.


Put a dish on a menu by its number. If the dish exists you get a success. If no dish has that number you got a different failure. Ask for one, two, three and upwards and the answers draw you a map of every dish id in the system, yours or anybody’s. Nothing was exposed but a yes or a no, and a yes or a no is enough to count another company’s menu.

How small the reach was

The menu itself was already fenced, so the most anyone could do was put somebody else’s dish on their own menu, where it was inert, because the screen only ever offers the venue’s own list. The leak was the difference in the answers, not the row that got written. The release before this one had taken the database text out of error responses and left this difference exactly where it was: a success against a failure, re-driven after that release to make sure.

Two fixes that would have broken the product

The first obvious fix is to refuse any dish that does not belong to your company. But the list a menu editor offers includes dishes with no venue and no company at all, the shared items the platform provides to every tenant. A company check would have refused a dish the screen had just offered.

The second obvious fix is to refuse any dish that does not belong to the deal’s own venue. But a meal deal in a food hall is the one kind of deal whose parts can come from a counter other than the one you are standing at. A hall-wide deal names a neighbouring kitchen’s dish on purpose. That check would have broken meal deals.

So the test is the feature’s own question: is this dish on the list this editor would offer? It uses the same condition the editor uses, widened to the whole hall when the deal has one, so it cannot drift from what the screen shows. Both refusals say the same thing, no such dish, for a dish that does not exist and for one this menu cannot reach. One sentence for both is the whole point.

Controls that carry weight

Seven tests. The finding is pinned as two requests with one answer, on the status, because the status was what was still leaking. Five controls: each route still takes the venue’s own dish, each still takes the shared dish, and a hall-wide deal still reaches the other kitchen. Reverting both checks fails two tests by name. Replacing the hall-wide set with the deal’s own venue alone fails exactly one, which is what makes that control load-bearing rather than decoration.


Try it on tonight’s service.

Nothing to install, no card. Not better by the weekend? Close the tab.