Product

Automating Firebase Security Rules Audits with an AI Agent

How CloudThinker watches every Firebase rules release with viewer-only access, diffs it against the last ruleset, and judges a widening change against the data model and sign-in providers. Covers Auto Mode, an illustrative first scan and prompts to try.

8 min read
Cover Image for Automating Firebase Security Rules Audits with an AI Agent

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

CloudThinkerCloudThinker

  • 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

  • SlackSlack or TeamsRelease alerts and approvals in the thread
  • PagerDutyPagerDutyCritical exposures page the security on-call
  • JiraOwned follow-ups for data-model decisions

Nothing new to watch

The agent reads the same Rules API and console state you used in part two, and can't publish anything with the default grants.

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": true at 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 that request.auth.uid matches 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.

Three readings of one widening release. Each one predicts something different in the diff and the history.

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 Friday release from the opening becomes a message at 16:48, not an incident review on Monday.

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.

FindingDetailSeverity
Open Realtime Database1 legacy project with ".read": true at root; 2.3 GB of user dataCritical
Expired test-mode rules3 projects on console-default rules; one deadline hand-set to 2030Critical
Any-authenticated-user readsFirestore orders and profiles readable by any signed-in accountHigh
Missing write ownership checks2 collections allow cross-user document overwritesHigh
Storage broader than FirestoreAttachments bucket readable by any authenticated userHigh
App Check unenforcedRegistered on 4 apps; enforcement off for Firestore in prodMedium
Anonymous auth in prodEnabled on 2 production projects, no rate limiting downstreamMedium
Unreviewed rules releases14 releases in 90 days, zero with a second reviewerMedium

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 testsWith CloudThinker on top
A console edit on FridayFound at the next audit, if thenDiffed and alerted within the hour
Projects coveredThe ones someone budgets time forEvery project the credentials can see
Judging a hitAn engineer samples, simulates, checks providersDone with the evidence attached
FixesTickets competing with feature workProposed ruleset and test, waiting for approval
RecordAn audit doc per passEvery 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.

firebasesecurityfirestoresecurityrulesappcheckaiagentcloudthinker