- Blog/
I Verified AI-Written RBAC Like a Pentester, Not a Code Reviewer
My wife is a special-education teacher. She wanted an app to track her students’ development goals, behavior observations, and study records — she doesn’t code, so the first version started as a single AI-generated HTML file. Eleven days later we had a real app running on Cloudflare Workers + D1, with three distinct roles. This is about the part I took on in that process: not the features, but verifying that the role boundaries actually held.
The setup #
The app runs three roles today: teacher, senior teacher, and
admin. A teacher can only see the child profiles assigned to them, a
senior teacher gets unlimited access plus blog management, admin adds
user management on top. Backend is a Cloudflare Worker, JWT-based auth,
D1 as the database — the AI got this architecture right on the first
pass. Routes existed, role checks were in the code, everything looked
like it worked.
Here’s the problem: the gap between “RBAC looks like it works” and “every role boundary holds every time” is exactly the gap I get paid to find in pentests. A teacher’s screen showing only their own students proves nothing about whether that same teacher can pull another teacher’s student ID by hitting the API directly.
Why manual QA doesn’t cut it #
The app has nine distinct resource types: child profiles, goals, behavior observations, study records, materials, the goal library, blog content, calendar, user management. Three roles. Roughly twenty-five endpoints. For every resource, the question “is this role allowed to do this” can be wrong in at least three separate ways:
- Vertical privilege escalation — can a teacher hit an admin endpoint and get a response back.
- Horizontal IDOR — can a teacher drop another teacher’s child profile ID into the URL and reach the data.
- Missing object ownership checks — the endpoint checks “are you logged in” but never checks “do you own this object.”
Try to test that by hand, clicking through the UI, and you get a matrix north of eighty combinations — three roles × nine resources × three failure classes. Passing it once proves nothing, because the next time someone refactors a route and forgets the role check, the only way to catch it is walking all eighty combinations by hand again. You won’t.
What I did #
I wrote a Go integration test suite — separate test files for auth, admin, children, goals, behaviors, materials, library, study records, and the state endpoint. Every file follows the same core pattern:
- Seed one test user per role, plus test data owned by each role.
- Hit every endpoint with each of the three roles’ tokens.
- Compare against the expected matrix — can this role write to this resource, read it, reach someone else’s record.
- Any unexpected 200 fails the build.
This isn’t really different from a pentest — the only difference is
doing it as code that runs on every commit in CI, instead of by hand
once. The line that checks whether a teacher role can pull data from
/goals using another teacher’s childId is answering the exact same
question as an “IDOR finding” in a pentest report — just not once, but
on every deploy.
The idea that AI writing code makes testing unnecessary is spreading; seeing a suite like this still prove that useful was a good reminder.
What it actually caught #
The valuable part wasn’t the finding itself — it was when it landed. During fast iteration, a new endpoint was added (file uploads, which came with R2 integration) and the role check was missing in the first draft. The test suite caught it at PR stage, before deploy — instead of finding out in production whether one teacher could reach a file another teacher had uploaded.
The AI-generated code was logically sound, syntax was correct, the happy path worked fine. What was missing were the “negative” scenarios nobody was asking — which is exactly the pentester’s job. You don’t trust RBAC by reading it and nodding along; you trust it by writing a test that actively tries to break every role boundary and watching it fail to.
Bottom line #
- RBAC breaks in three ways: vertical escalation, horizontal IDOR, missing object ownership — a code read easily misses two of the three.
- The role × resource × failure-class matrix grows fast; manual testing becomes impossible past a certain point, and automated integration tests stop being optional.
- AI-generated code can be architecturally sound and still carry access control gaps — the two are independent axes, and one doesn’t guarantee the other.
I do web app pentests on the side — access control, RBAC, IDOR, and the rest of the OWASP top 10 territory. If you’re deploying something with more than one role and want to know the boundaries actually hold: [email protected].