This is the bug where a logged-in user changes an ID in a request and gets back data belonging to someone else. OWASP calls it broken object-level authorisation, or BOLA. It's the most common serious flaw we found across 24 AI-generated applications, and it's near the top of the OWASP API Security Top 10 for good reason.
Why it happens
The code usually looks correct. There's an authentication check, it passes, and then the handler fetches the record that was asked for.
requireAuth confirms someone is logged in. Nothing confirms that this someone owns order 4471. Authentication answers "who are you"; authorisation answers "are you allowed this specific thing", and the second check is the one that goes missing.
It's an absence rather than a mistake, which is why it survives code review. There's no wrong line to spot.
The fix
Constrain the query by the authenticated user rather than checking afterwards.
Two details worth keeping. Put ownership in the where clause rather than fetching then comparing. The second version leaks through timing and through any code path that forgets the comparison. And return 404 rather than 403, so an attacker can't enumerate which IDs exist.
Do it once, not per endpoint
Fixing this endpoint by endpoint means getting it right every time forever, and that's not a bet worth taking on a codebase that's still growing.
Better options, roughly in order of durability:
Scope at the data layer. Postgres row-level security, or Supabase RLS policies, so the database refuses to return rows the current user doesn't own regardless of what the query asks for.
Scope at the ORM. A repository or middleware layer that injects the tenant or user filter into every query by default, so an unscoped query has to be written deliberately.
Use unguessable IDs. UUIDs instead of sequential integers. This is defence in depth, not a fix, it makes enumeration harder and changes nothing about the underlying flaw.
Spotting it in an existing codebase
Testing one endpoint is easy: log in as user A, request user B's record, see what comes back. Testing eighty isn't, and this is where it gets awkward.
Safe code and vulnerable code differ by a where clause that isn't there. A pattern matcher has nothing to match on. That shows up in our benchmark: across 26 scanners on 66 Python repositories, the best rule-based tool managed 0.063 recall against 0.89 for security-specialised systems, and the gap is widest on exactly this kind of bug, where correctness depends on intent rather than syntax.
Catching it reliably needs analysis that follows data through the codebase rather than matching known-bad patterns, which is the approach Kolega takes.