You tightened your Base44 app's permissions once, maybe after an audit or a scare, and you have told yourself it is handled ever since. But every time you ask the AI to add a screen or fix a bug, a small voice asks whether the access rules you fixed last month are still holding. That voice is right. Row-level security is not set-and-forget on this platform; it quietly drifts, and the only way to know it still works is to test it on purpose. This is the methodology we use, refined across 100+ Base44 apps, to prove RLS works rather than assume it does.
Base44 RLS testing means proving, not assuming, that one user cannot reach another user's rows. The method has four parts. First, write a threat model that names, in plain sentences, exactly what must never happen. Second, build fixtures of real second accounts spanning roles, tenants, and an unauthenticated session. Third, run a test matrix where each actor deliberately attempts forbidden read, list, create, update, and delete operations against records it does not own. Fourth, re-run that exact matrix after every deploy, because AI edits silently loosen rules without changing anything you can see on screen. The pass condition is that every cross-owner request is refused at the data layer itself, not merely hidden in the interface.
Start With a Threat Model, Not a Checklist
Before you test anything, you have to write down what must never happen, because a test you have not specified is a test you will not run. Most founders skip this step and jump straight to clicking, which is why their access control testing finds nothing: they are only ever exercising the paths that are supposed to work. The threat model flips your attention to the paths that are supposed to fail, and those are the ones that hurt you.
A Base44 threat model is not a forty-page document. It names, for each kind of data your app holds, the specific sentence you never want true. "Customer B can read customer A's invoices." "An unauthenticated visitor can list every user's email." "A standard user can promote themselves to admin." "A user in tenant X can update a record owned by tenant Y." Each sentence becomes a negative test, a request you deliberately attempt and expect refused. If it succeeds, you found a leak; if refused, that line passes.
The reason this matters on Base44 is the same reason RLS breaks here. The AI builder reasons about making features appear, never about who should be refused, so its failure modes are almost always missing refusals. A threat model is just the discipline of writing those refusals down so you can check each one. We go deeper in the Base44 security hardening checklist, and the OWASP Top 10 in Base44 maps these threats onto the standard categories, with broken access control at number one.
Build Your Fixtures: The Accounts You Test From
You cannot test row-level security from one account, full stop. The single most common reason a founder believes their app is safe when it is not is that they only ever logged in as themselves, and as yourself you only ever see your own rows. To test base44 row level security honestly, you need a fixed set of accounts, what we call fixtures, that represent every actor your threat model cares about.
The minimum fixture set is four actors. User A and user B live in separate tenants or organizations if your app has any concept of separation. An admin account, because admin over-permission is its own failure mode and admins often get blanket read access the AI never scoped. And an unauthenticated session, a plain browser with no login, because the most embarrassing leaks are reachable by anyone on the internet. Seed each account with bait data: name A's records "AAA-secret" and B's "BBB-secret" so any cross-contamination is unmistakable in a raw response.
| Fixture actor | Why it exists | The leak it catches |
|---|---|---|
| User A (tenant 1) | Baseline owner | Confirms legitimate access works |
| User B (tenant 2) | Cross-tenant attacker | A reading B's rows, the classic leak |
| Admin | Over-scoped power | Admin reading data outside its remit |
| Unauthenticated | Anyone on the internet | Public endpoints exposing private data |
Keep these fixtures permanent. The point of a methodology is repeatability: you run identical accounts against identical assertions after every change. Document the credentials, label the bait data, and never delete them, because rebuilding fixtures each time is how re-verification quietly stops happening. If you want this set built and documented for you, that is exactly what the $497 production audit delivers.
The RLS Test Matrix Every Base44 App Needs
This is the core of the methodology and the thing most people get wrong by under-scoping it. A real base44 rls verification is a matrix: every entity in your data model, crossed with every actor from your fixtures, crossed with every operation. The cells you care about most are the negative ones, the combinations that should be refused, because a refused-but-allowed cell is a live leak.
The operations are not just "read." A user who cannot read another tenant's record may still update or delete it if the write rule was scoped differently from the read rule, which happens constantly when the AI generates them at different times. So for each entity, test read, list, create, update, and delete from each actor. List is sneaky: an app often blocks a direct read of record 123 but happily returns it inside a "list all" call that forgot to filter by owner.
| Entity | Actor | Operation | Expected | How to verify |
|---|---|---|---|---|
| Orders | User B | read user A's order | Refused | Replay request with A's record id |
| Orders | User B | list all orders | Only B's rows | Inspect raw list response for AAA bait |
| Orders | Unauthenticated | list all orders | Refused | Hit data endpoint with no session |
| Profiles | User B | update user A's profile | Refused | Send write to A's profile id |
| Profiles | User A | promote self to admin | Refused | Attempt role-field write |
| Invoices | Admin | read across tenants | Per policy | Confirm admin scope matches intent |
| Files | User B | download A's upload | Refused | Request file URL owned by A |
| Files | Unauthenticated | download any upload | Refused | Open file URL with no session |
Run every negative cell hard. The "verify" column is the important part: you do not click the button the UI offers, because the UI will not even render the option to attack A's data, and that absence is the false comfort that hides leaks. You replay the underlying data request with a different record id or session, using browser developer tools or a request client, and read the raw response. If B's session returns a record tagged "AAA-secret," you have proven a leak no amount of clicking ever could. The Base44 AI builder audit page details which entities and operations we inspect when we run this matrix for you.
What AI Edits Silently Break (and Why It Recurs)
Here is the part that turns a one-time test into a methodology. On a normal codebase, you might verify access control once and trust it until someone deliberately touches the permission layer. On Base44 that assumption is fatal, because the AI touches the permission layer constantly without telling you. This is the single most important thing the lead engineer at Base44Devs wants every founder to internalize about this platform.
Ask the AI to "add a dashboard of recent activity" and it writes a fetch query. The fastest query that makes the demo work reads broadly, so it often drops the owner filter you previously added. Ask it to "fix the bug where my list is empty" and it frequently loosens the very rule that was correctly refusing rows, because to its narrow view an empty list looks like a bug to open up rather than a boundary doing its job. We have documented this as RLS getting out of sync after an AI edit, one of the most common engagements we take.
| AI request that breaks RLS | What it silently changes | Matrix cell that catches it |
|---|---|---|
| "Add an activity feed" | New broad read query, no owner filter | User B lists all activity |
| "My list shows nothing, fix it" | Loosens the refusing rule | User B reads A's rows |
| "Add an export button" | New endpoint bypassing existing filter | Unauthenticated export |
| "Let admins see everything" | Blanket read scoped too wide | Admin reads beyond policy |
| "Speed up this page" | Replaces filtered query with a cached broad one | Cross-tenant list leak |
Because the regression is invisible on screen, the only defense is mechanical: re-run the exact same matrix after every deploy. This is why permanent fixtures and a written matrix matter so much. Re-verification that depends on remembering what to test will not survive contact with a busy week. Re-verification that is a saved checklist of negative cells you re-run in twenty minutes will. If a regression has already shipped and you need it closed fast, a focused RLS fix sprint runs $1,500 with a 48 to 72 hour turnaround and a money-back guarantee if it is not resolved.
Cross-Tenant and Unauthenticated: The Tests People Skip
Two categories of test get skipped most often, and they are the two that produce the worst disclosure emails. The first is genuine cross-tenant testing. Many founders test "can another user see my data" using two accounts that share a tenant, which only proves intra-tenant isolation. If your app serves multiple organizations, the catastrophic leak is one company reading another company's records, and that requires fixtures in genuinely separate tenants. Seed them, then run every read and list cell across the tenant boundary specifically.
The second is unauthenticated testing, and it is the one that turns up in automated scanner reports and security researcher writeups. The test is brutally simple: take every data endpoint your app calls and hit it with no session at all. The UI never makes these calls anonymously, so the AI sometimes never enforced login at the data layer, only at the page layer. An attacker, or a bot, does not load your pages; it talks straight to your data. We have seen apps where the login screen was airtight and the underlying data calls answered to anyone, which is the silent cousin of the white screen and 405 errors after login problem, except instead of failing visibly it succeeds invisibly for an attacker.
A sequencing tip from running this dozens of times: test unauthenticated first, then cross-tenant, then intra-tenant, then privilege escalation. Find the widest-blast-radius leaks before the narrow ones, and an anonymous-access leak is as wide as it gets. This is the broken-access-control surface that the is my Base44 app secure overview names as the number-one realistic risk for an AI-built app, and the matrix above is how you prove you are clear of it.
Make Re-Verification a Deploy Ritual
A methodology that runs once is not a methodology; it is a snapshot that expires the moment the AI touches your app again. The discipline that actually keeps a Base44 app safe is turning the matrix into a ritual attached to every deploy, the same way a careful team runs tests before shipping. The good news is that after the first full run, re-verification is fast, because the fixtures already exist and the negative cells are already written down.
The ritual: before a change is shipped, log in as user B and run the cross-tenant read and list cells for any entity it touched, hit the relevant data endpoints unauthenticated, and confirm the privilege-escalation cell still refuses. For a small change this is fifteen to twenty minutes. For anything that touched data fetching, run the full matrix, under an hour once scripted into saved requests. That hour is nothing next to discovering, via a customer screenshot, that last Tuesday's quick fix reopened a leak you closed in the spring.
| Cadence | What to run | Roughly how long |
|---|---|---|
| Every deploy | Negative cells for touched entities + unauthenticated check | 15-20 minutes |
| Data-layer change | Full matrix, all entities and actors | Under 1 hour |
| Quarterly | Full matrix plus threat-model review | Half a day |
| After any AI "fix the empty list" edit | Full matrix, no exceptions | Under 1 hour |
If building and documenting this process is more than you want to own, that is precisely what we package. A $497 audit runs the full matrix once with a written report; if it surfaces a live leak, a bug-fix sprint at $1,500 closes it under the money-back guarantee, and a larger access-control rebuild runs $3,000. The fastest way to start is a free 15-minute scoping call you can book from our contact page, where we tell you honestly whether your app needs the full matrix or just the two-account spot check. Either way, stop assuming RLS works. Prove it, then prove it again after the next deploy.
Related reading
- Base44 Security Hardening Checklist — the full set of fixes that sit alongside correct RLS, from secret handling to input validation.
- OWASP Top 10 in Base44 — broken access control and the rest of the standard risk list, mapped to how Base44 apps actually fail.
- Is My Base44 App Secure? — the plain-English overview of why access control is the number-one realistic risk for an AI-built app.
- RLS Out of Sync After an AI Edit — the engagement we run when a deploy silently reopened a permission you had already fixed.