Automating Firebase Security Rules Audits with an AI Agent
At 4:47 on a Friday afternoon, a developer publishes a Firestore rules release to unblock a feature demo. The change loosens a match block from per-document ownership to "any signed-in user can read." The console records the release, replaces the previous ruleset, and pages nobody. By Monday, your orders collection has been readable by every account in the project for sixty hours, including the ones created through anonymous auth.
That failure mode is why a Firebase security rules audit can't be a quarterly event. This is part three of our Firebase security audit series. Part one mapped the misconfigurations that actually expose Firebase apps: test-mode rules that never got replaced, auth-only-but-any-user reads, missing App Check. Part two showed how to audit all of it with the Firebase CLI, the Rules Playground and the Emulator Suite.
If you worked through part two, you hit its closing problem head-on: the audit is point-in-time and per-project, and a release like the one above lands silently between audits. This part covers closing that gap with CloudThinker's security agent: continuous auditing, release diffing and approval-gated fixes.
Why rules releases beat scheduled audits
Firestore, Cloud Storage and Realtime Database rules are code that deploys like code, except in most teams they skip the review pipeline. A firebase deploy --only firestore:rules from a laptop, a paste into the console editor, a CI job with over-broad credentials: each one publishes a release that completely replaces the active ruleset. There's no partial deploy and no built-in "this release widened access" alarm.
The native safety nets each cover a slice:
- Emulator tests in CI catch regressions in rules that go through your repo. Console edits never touch CI.
- Release history records every version. Nobody is watching it.
- Cloud Audit Logs record who published. Nobody reads them unless something already went wrong.
- Scheduled audits sample the release stream once a quarter. Everything between samples is faith.
So the security posture of a Firebase project isn't a state you audit. It's a stream of releases you have to watch, and judging each one needs context the release doesn't carry: what the collection holds, which apps reach it, which sign-in providers are on.
Connecting: read-only, about five minutes
01Projects
Firebase
- Rules releasesFirestore, Storage, Realtime Database
- Project and app inventoryWeb, iOS and Android apps per project
- App Check and Auth configEnforcement, providers, domains
Viewer-level roles only
02Audit
CloudThinker
- Watch every releaseDiff against the previous ruleset
- Judge the changeData model, reachable apps, sign-in providers
- Propose the fixCorrected rules and a test to keep them
Cannot publish by default
03Where the team works
Chat and on-call
Slack or TeamsRelease alerts and approvals in the thread
PagerDutyCritical exposures page the security on-call
- JiraOwned follow-ups for data-model decisions
Nothing new to watch
CloudThinker connects to Firebase through a Google Cloud service account with viewer-level roles only: roles/firebaserules.viewer to read rulesets and releases, plus project-level viewer access for inventory. No editor or owner roles, no write scopes, no ability to publish a ruleset. You create the service account from the policy in the Firebase connection guide, upload the key or bind it through workload identity federation, and the first scan starts. Your security review can read the exact grants before anything connects.
Two grants are optional, and each unlocks one step in the walkthrough below. roles/datastore.viewer (Cloud Datastore Viewer, which covers Firestore) lets CloudThinker sample documents to check the data model. That's a read of real data, so grant it only if you want that check. roles/logging.viewer lets it read the Admin Activity audit logs that name who published a release. Without either, it works from rules, releases and config alone.
Project and app inventory
The first thing a scan produces is the thing most teams don't have: a complete inventory. Every Firebase project the credentials can see, every registered app (web, iOS, Android) inside it, and which services are actually live: Firestore, Realtime Database, Cloud Storage, Auth, App Check. For each one: does it have deployed rules, when was the last release, and does anyone appear to own it.
This matters because the riskiest Firebase project is rarely the one your team works in daily. It's the 2021 prototype still bound to a production billing account, with Realtime Database rules nobody has opened since the hackathon.
Continuous rules audit
For every project, CloudThinker pulls the active ruleset for Firestore, Storage and Realtime Database and checks it against the catalog from part one:
- Open rules.
allow read, write: if true;in any match block, or RTDB rules with".read": trueat the root. - Test mode that never ended. The console's time-boxed rules, especially ones where the deadline was hand-edited years into the future:
// Flagged: test-mode rules with the timestamp pushed out to 2030
match /{document=**} {
allow read, write: if request.time < timestamp.date(2030, 1, 1);
}
- Auth-only-but-any-user access.
allow read: if request.auth != null;on collections holding per-user data, with no check thatrequest.auth.uidmatches the document owner. With open sign-up or anonymous auth enabled, "any signed-in user" means "anyone." - Missing ownership checks on writes. Rules that validate authentication but not that the caller may modify this document, enabling cross-user overwrites.
- Storage broader than Firestore. A common drift: the database gets locked down, and the bucket holding its file attachments doesn't.
Each finding carries the ruleset excerpt, the match path, which registered apps can reach that service, and a severity. It's the evidence you assembled by hand in part two, refreshed on every release instead of every quarter.
A widening release, end to end
Here's the Friday release from the opening, from the moment it lands. A diff that widens access has three common readings, and they call for very different responses:
01Reading 1
Intended
The widened path holds shared data, and a reviewed change explains it.
02Reading 2
Lost ownership
A per-user collection lost its uid check while someone unblocked a demo.
03Reading 3
Stale deploy
The new ruleset matches an older one: a deploy from a branch that predates a fix.
1. Notice the release. The cloud.firestore release now points at a new ruleset with a createTime of 16:47. That's the trigger. No schedule involved.
2. Diff it. Against the previous ruleset, one block changed:
// Before
match /orders/{orderId} {
allow read: if request.auth != null
&& request.auth.uid == resource.data.ownerId;
}
// After
match /orders/{orderId} {
allow read: if request.auth != null;
}
A condition was removed, so access widened. That rules out nothing yet.
3. Check the history. The new ruleset doesn't match any earlier ruleset text, so it isn't a stale-branch deploy. Reading 3 is out.
4. Check the data model. With document read access granted, sampled orders documents all carry an ownerId. The collection is per-user by design, which makes reading 1 unlikely, and no reviewed change in the repo touched firestore.rules this week. Reading 2.
5. Size the exposure. Anonymous sign-in is enabled on this project, and three registered apps use it. Any visitor with the project ID from the web bundle can sign in anonymously and read every order.
01Signal
New release
cloud.firestore points at a new ruleset at 16:47.
02Diff
Access widened
The uid check on orders is gone. Any signed-in user can read.
03Context
Who can reach it
Anonymous sign-in is on; three registered apps use this project.
04Output
Alert and fix
Before and after, the exposed path, the corrected rule and a test.
The alert lands in Slack at 16:48 with the before and after, which collections are newly exposed, who can now reach them, and the publisher's identity from Cloud Audit Logs if you've granted roles/logging.viewer. Attached is a proposed fix: the ownership condition restored, plus an emulator test that would have failed the change in CI. Nothing publishes until a named person approves the ruleset text.
App Check and Auth configuration watch
Rules are the main gate, but part one covered the rest of the perimeter, and the agent watches it too:
- App Check status per service. Registered but unenforced is the most common finding. Enforcement turned off "temporarily" during a mobile release and never restored gets flagged as drift.
- Auth provider hygiene. Anonymous auth enabled on production projects, email enumeration protection off on older projects, sign-in providers nobody remembers enabling.
- Authorized domains. Entries pointing at long-dead staging hosts or third-party domains.
Nothing touches your rules without your say
CloudThinker acts within Auto Mode, so you decide what runs on its own and what waits for approval. A proposed fix is the exact ruleset text in front of a reviewer before release, which is the review step that Friday deploy never had.
Publishing a ruleset needs write access that the read-only connection deliberately doesn't have. You grant it separately, scoped, and only if you want approval-gated remediation at all. Every proposal, approval and release lands in the audit trail: finding, evidence, proposed diff, approver, timestamp.
What a first scan typically finds
The numbers below are illustrative, a composite first scan across a mid-market SaaS org with a dozen Firebase projects. Yours will differ.
| Finding | Detail | Severity |
|---|---|---|
| Open Realtime Database | 1 legacy project with ".read": true at root; 2.3 GB of user data | Critical |
| Expired test-mode rules | 3 projects on console-default rules; one deadline hand-set to 2030 | Critical |
| Any-authenticated-user reads | Firestore orders and profiles readable by any signed-in account | High |
| Missing write ownership checks | 2 collections allow cross-user document overwrites | High |
| Storage broader than Firestore | Attachments bucket readable by any authenticated user | High |
| App Check unenforced | Registered on 4 apps; enforcement off for Firestore in prod | Medium |
| Anonymous auth in prod | Enabled on 2 production projects, no rate limiting downstream | Medium |
| Unreviewed rules releases | 14 releases in 90 days, zero with a second reviewer | Medium |
The shape is typical: the critical items cluster in projects nobody audits, and the release-hygiene finding at the bottom explains how the ones at the top happened.
Why this beats the traditional approach
| Quarterly audit and CI tests | With CloudThinker on top | |
|---|---|---|
| A console edit on Friday | Found at the next audit, if then | Diffed and alerted within the hour |
| Projects covered | The ones someone budgets time for | Every project the credentials can see |
| Judging a hit | An engineer samples, simulates, checks providers | Done with the evidence attached |
| Fixes | Tickets competing with feature work | Proposed ruleset and test, waiting for approval |
| Record | An audit doc per pass | Every finding, approval and release logged |
The deeper difference is what triggers the audit. A schedule samples the release stream; the agent is triggered by the release itself. That's the only way the Friday change gets reviewed before Monday.
Prompts to try in your first session
CloudThinker is conversational. You ask in plain language, and each answer cites the ruleset text and release history behind it:
"Inventory every Firebase project we have and flag anything still on default or test-mode rules. Propose only, change nothing."
"Diff the last two Firestore rules releases in the production project and tell me exactly what an unauthenticated or anonymous user can now read."
"Which apps have App Check registered but not enforced for Firestore or Storage, and what would enforcing it break?"
These are the same artifacts you pulled from the Rules management REST API in part two, so you verify rather than trust.
What CloudThinker won't do
- No publishing, editing or rolling back rules on its own. Rules changes need explicitly granted write access, and they still go through Auto Mode.
- No disabling auth providers or flipping App Check enforcement unilaterally. Both can break live clients, and both stay approval-gated.
- No actions outside the audit trail. Every finding, proposal and executed change is logged with evidence and approver.
- No deciding your data model's access semantics. It can prove any signed-in user can read
orders. Whether that's a bug or a business rule is a call that stays with your team.
From audit to standing watch
You know which Firebase misconfigurations matter, and you know how to find them with native tools. The remaining gap is the one the Friday release exploits: rules change continuously, and audits don't. Putting a read-only agent on the release stream turns a silent regression into a same-hour alert, and turns remediation into a reviewed, logged, approval-gated release instead of another untracked deploy.
Try CloudThinker free with 100 premium credits and no card required, connect Firebase read-only via the connection guide, and get your first project inventory and rules findings within the hour.
