Menu

Security research2 min read

Why static analysis misses access-control bugs

A static scanner can tell you a query is unsafe. It cannot tell you that User B should not be able to read invoice 42. That rule lives in your business, not your syntax.

Cover: the same invoice request returns 200 for two different users

Key takeaways

  • Static analysis matches code against known-bad patterns. Injection fits that model. Authorization does not.
  • Whether a request is allowed depends on who sent it and who owns the data, which source code alone rarely shows.
  • Broken object-level authorization is first on the OWASP API Security Top 10.
  • Runtime tests that replay real requests as different users find these bugs directly.

What static analysis is good at

Static application security testing (SAST) reads source code without running it. It looks for patterns that are dangerous regardless of context: string-built SQL, unescaped output, hard-coded keys, unsafe deserialisation. These are real bugs, and SAST finds them cheaply and early.

It is no coincidence that these are also the bugs AI coding agents have mostly stopped writing. In Tenzai's test of five agents, none of the 15 apps had exploitable SQL injection.

Why authorization is different

Consider a handler that loads an invoice by ID for a signed-in user:

Signed in, but whose invoice?
export async function GET(req, { params }) {
  const user = await requireUser(req);
  const invoice = await db.invoice.findUnique({ where: { id: params.id } });
  return Response.json(invoice);
}

There is no dangerous pattern here. The query is parameterised and the route requires a session. The bug is a missing line, a check that invoice.userId === user.id, and only someone who knows the business rule can say that line should exist. Some invoices might legitimately be shared. Some routes are meant to be public.

OWASP lists this as API1:2023 Broken Object Level Authorization, the top API risk. Its sibling, broken function-level authorization, covers admin actions that ordinary users can call. Both are about who is asking, which is exactly the context static analysis does not have.

What runtime testing sees instead

A runtime test does not need to understand the business rule in the abstract. It can observe the consequence. Capture the request User A's browser makes for invoice 42. Replay it with User B's session. If the response contains User A's invoice, the rule was broken, whatever the code looks like.

The same idea finds unguarded routes: replay the request with no session at all. It also finds leaks that no code path makes obvious, such as a response that includes another user's email in a nested field.

Use both

This is not an argument against static analysis. Keep it in CI for the pattern bugs it finds well. Add a runtime check for the context bugs it cannot see, and run it against a deployed environment with at least two real test accounts.

Questions

Can SAST tools detect IDOR?
Rarely and unreliably. IDOR depends on whether a specific user should access a specific record, which is a business rule. Static tools can flag routes with no auth check at all, but not routes with the wrong one.

Routelint records the API calls your running app makes, then replays them with no session and as a second account. One scan needs no account.

Sources

  1. API1:2023 Broken Object Level Authorization — OWASP API Security Top 10
  2. API5:2023 Broken Function Level Authorization — OWASP API Security Top 10
  3. Vibe coding tested: AI agents nail SQLi but fail miserably on security controls — SecurityWeek

Published November 1, 2026 by the Routelint team.

Security research2 min read

What AI coding agents get wrong about security

When five leading coding agents built the same apps, none wrote SQL injection. All of them skipped the security controls the prompt never mentioned.