Finding · 01
One URL exposed every customer's orders.
Summary
How a missing ownership check became a refund machine
How does any signed-in user end up able to read, and refund, another customer's orders?
The order endpoints trusted the ID in the URL. Change one number and you were looking at someone else's order: customer email, total, last four digits of the card. The refund endpoint trusted the same ID, so the same change issued a refund on someone else's purchase.
The handler had been generated by an AI coding assistant and passed review. It did exactly what the ticket asked. Nobody had specified that it should check who owned the order, so it didn't. A model working for an attacker would find this in its first pass.
The fix was small and structural: ownership enforced once, at the data layer, for every order route. Refunds over $500 now require a second factor. Both protections became rules that run on every release.
Review catches style and logic. It rarely catches the check nobody wrote a test for.
MATT research team
Key insights
What we learned
The bug was correct code
The handler did what the ticket asked. It fetched an order by ID. Nobody wrote down that the order had to belong to the caller, so the generated code never checked. Review passed because there was nothing visibly wrong.
Fix it once, in one place
Adding a check to each handler would have left the next AI-written route just as exposed. Ownership is now enforced at the data layer, so every current and future order route inherits it.
Turn the fix into a rule
Two plain-English rules now run on every release: no user may read or act on another user's orders, and refunds over $500 need a second factor. The finding cannot quietly come back.
In action
Two requests, before and after
Signed in as user B. Order 48213 belongs to user A. Identifiers are illustrative.
Where we start