Write a loop that calls every admin route as each lower role, including an anonymous caller, and asserts the server refuses it. Hiding the button in the interface proves nothing. The API itself has to say no, and a test should prove it on every build.
What this test catches
OWASP's Authorization Regression Testing Cheat Sheet calls this the "Role Demotion Check" and ties vertical escalation to CWE-269, improper privilege management. Vertical escalation is when a lower-privileged user reaches functions reserved for a higher role, such as /api/admin/users/delete.
It is different from the object-level problem. Here the user has no right to the function at all. The OWASP API Security Top 10 draws the same line: reaching an endpoint you should not reach is broken function level authorization, while manipulating an ID on an endpoint you may use is broken object level authorization.
Why UI hiding fails
A normal user never sees the admin menu, so the team assumes the routes are safe. But the API is reachable by anyone who can send an HTTP request. The regression sheet is plain: relying on UI hiding is insufficient, the API layer must enforce the check, and client-side controls are trivially bypassed.
New admin routes appear in every sprint. Each one is a chance to forget a check.
How to build the test
- Inventory the admin surface. Pull routes from your router, your OpenAPI file or a path prefix such as admin. Include methods, not just paths.
- List the roles below admin. Anonymous, basic user, read-only user, a tenant admin if the route is platform-level, and a service account.
- Create a token or session per role. Swap them in the Authorization header instead of logging in every time.
- Loop. For each route and each lower role, send a valid request shape.
- Assert denial. Expect 401 for anonymous and 403 for an authenticated but unauthorized role, or whatever your API contract says.
- Assert no side effect. After a denied delete, confirm the record still exists. A 403 returned after the action ran is a real bug.
- Run it in CI as a required check.
What to assert, as a table
| Caller | Admin route | Expected |
|---|---|---|
| No credentials | Any admin route | Rejected as unauthenticated |
| Basic user | Any admin route | Forbidden |
| Read-only user | Admin write route | Forbidden |
| Tenant admin | Platform admin route | Forbidden |
| Platform admin | Admin route | Allowed |
Keep the last row. A test that only checks denials cannot tell a working route from a route that is broken for everyone.
Common gaps to probe
- Method confusion. Admin allows DELETE but GET on the same path is open, or the reverse.
- Alternate paths. A versioned or legacy route that bypasses the new check.
- Batch and bulk endpoints. One call that performs many privileged actions.
- Newly added routes. Check that your inventory step picks them up automatically instead of relying on a hand-kept list.
- Expired and wrong-scope tokens. The regression sheet advises explicit fixtures for these, since a contract-driven tool will not generate them for you.
Deny by default as the structural fix
Tests find mistakes. A default that blocks unknown routes prevents many of them. The OWASP Authorization Cheat Sheet recommends denying by default and validating permissions on every request using global, application-wide configuration instead of per-method decoration, because an attacker only needs one missed check.
Try it yourself
Click through it at your own pace, or open it full screen. Watch how a running app is probed from the outside, which is the viewpoint a role demotion test takes.
Safeguard supports dependency scanning and policy gates, which helps you spot advisories in the frameworks that enforce your route checks. Try the free scan at gold.safeguard.sh/scan.
Quick check
Is 404 acceptable instead of 403 for admin routes?
Sometimes. Hiding that an admin route exists can be reasonable, but choose one behaviour, document it and test for exactly that.
How often should this run?
On every pull request. Admin routes are added often, and a once-a-year pen test will not catch the one added last week.
Sources
- OWASP Authorization Regression Testing Cheat Sheet
- OWASP API5:2023 Broken Function Level Authorization
- OWASP API1:2023 Broken Object Level Authorization
- OWASP Authorization Cheat Sheet
Last reviewed: 19 October 2026.