Platform security

Firebase security rules testing: Firestore, Storage and the emulator

Firebase security rules testing for Firestore, Storage and Realtime Database: working unit tests for the Local Emulator Suite, safe example rules, and a checklist.

Short answer

Firebase Security Rules are the access control for Firestore, Cloud Storage and Realtime Database when clients connect directly. Test them with the Local Emulator Suite and the @firebase/rules-unit-testing library. Write one allowed and one denied case per path, test as signed-out, owner and other-user callers, and never ship allow read, write: if true.

Firebase Security Rules decide who can read and write your data when a mobile or web client talks to Firebase directly. Firebase's own docs say: "Firebase Security Rules stand between your data and malicious users." They cover Cloud Firestore, Realtime Database and Cloud Storage.

If a rule is too open, anyone with your project's public config can use it. This page shows how to test rules with the Local Emulator Suite, gives safe example rules, and ends with a checklist. Test only projects you own or have written permission to test.

What do Firebase Security Rules protect, and what do they not?#

Rules protect direct client access. They do not protect server access. Firebase states that the server client libraries "bypass all Cloud Firestore Security Rules". Code that runs with the Admin SDK or a service account needs its own checks.

Three facts apply to every test:

  • The Firebase config is public. The API key and project ID in a web app are not secrets. Rules are the control.
  • Rules are not filters. A Firestore query succeeds only if the rules allow every document it could return. Test the exact queries your app runs.
  • Rules do not combine across products. Firestore, Storage and Realtime Database each have their own rules file. A good Firestore ruleset does not protect your Storage bucket.

What do unsafe rules look like?#

Two rule patterns are unsafe. Firebase's Firestore docs warn against the first one: "NEVER use this rule set in production; it allows anyone to overwrite your entire database."

// Unsafe: open to everyone
match /{document=**} {
  allow read, write: if true;
}

// Unsafe: any signed-in user, including self-registered ones
match /{document=**} {
  allow read, write: if request.auth != null;
}

Firebase notes that basic rules like these "are not recommended for production applications". The second pattern checks only that a user is signed in. It does not check who owns the data.

What do safe example rules look like?#

The rules below use an example app with users and projects collections, and a Storage path per user. Adapt the names.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Default: deny everything not listed below
    match /{document=**} {
      allow read, write: if false;
    }

    // A user reads and edits only their own profile
    match /users/{userId} {
      allow read: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null && request.auth.uid == userId
                    && request.resource.data.keys().hasOnly(['name', 'bio']);
      allow update: if request.auth != null && request.auth.uid == userId
                    && request.resource.data.diff(resource.data)
                         .affectedKeys().hasOnly(['name', 'bio']);
      allow delete: if false;
    }

    // Project members can read; only the owner can write
    match /projects/{projectId} {
      allow read: if request.auth != null
                  && request.auth.uid in resource.data.memberIds;
      allow write: if request.auth != null
                   && request.auth.uid == resource.data.ownerId;
      allow create: if request.auth != null
                    && request.resource.data.ownerId == request.auth.uid;
    }
  }
}
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    // Each user uploads images to their own folder, up to 5 MB
    match /users/{userId}/{fileName} {
      allow read: if request.auth != null && request.auth.uid == userId;
      allow write: if request.auth != null && request.auth.uid == userId
                   && request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

The hasOnly checks stop users from writing fields like role or isAdmin on their own profile. The default deny rule keeps new collections closed until you open them on purpose.

How do you unit test rules with the emulator?#

Run Firestore (and Storage) in the Local Emulator Suite and test with @firebase/rules-unit-testing. Firebase recommends the v9 library because it needs less setup and "never touches your production resources." The main calls are:

  • initializeTestEnvironment() starts the test environment. It works only while the emulators run.
  • authenticatedContext(uid) acts as a signed-in user. unauthenticatedContext() acts as a client with no token.
  • assertSucceeds() passes when the operation is allowed. assertFails() passes when rules deny it.
  • withSecurityRulesDisabled() seeds data without rules. clearFirestore() resets data between tests.

This test follows Firebase's example pattern. It uses a demo project ID.

import { readFileSync } from "fs";
import { doc, getDoc, setDoc } from "firebase/firestore";
import { assertFails, assertSucceeds, initializeTestEnvironment } from "@firebase/rules-unit-testing";

let testEnv;

before(async () => {
  testEnv = await initializeTestEnvironment({
    projectId: "demo-rules-test",
    firestore: { rules: readFileSync("firestore.rules", "utf8") },
  });
});
afterEach(() => testEnv.clearFirestore());
after(() => testEnv.cleanup());

it("alice writes her own profile, not bob's", async () => {
  await testEnv.withSecurityRulesDisabled(async (ctx) => {
    await setDoc(doc(ctx.firestore(), "users/bob"), { name: "Bob" });
  });
  const db = testEnv.authenticatedContext("alice").firestore();
  await assertSucceeds(setDoc(doc(db, "users/alice"), { name: "Alice" }));
  await assertFails(setDoc(doc(db, "users/bob"), { name: "Changed" }));
});

it("alice cannot add an isAdmin field to her profile", async () => {
  const db = testEnv.authenticatedContext("alice").firestore();
  await assertFails(setDoc(doc(db, "users/alice"), { name: "Alice", isAdmin: true }));
});

it("signed-out users cannot read profiles", async () => {
  const db = testEnv.unauthenticatedContext().firestore();
  await assertFails(getDoc(doc(db, "users/alice")));
});

Run the suite from the CLI with the emulator started for the test run, for example firebase emulators:exec --only firestore "npm test". Check the Emulator Suite docs for the flags your CLI version supports. If tests time out or show ECONNREFUSED, the emulator is probably not running.

What is the test checklist?#

Test every path in every rules file. Use four callers: signed out, owner, other signed-in user, and a user with a role claim, if you use roles.

Firestore#

  • Search for if true, and for request.auth != null used alone.
  • Each collection has a read case and a write case for each caller. Include create, update and delete apart.
  • Owner checks compare request.auth.uid to a stored owner field, not a client-sent value.
  • Updates cannot change protected fields such as role, ownerId, plan or isAdmin.
  • Queries (list) work only with the filters your rules need, and fail without them.
  • Subcollections and collection-group queries have their own rules.

Cloud Storage#

  • Paths are scoped to the owner or the tenant.
  • Size and content type limits exist on uploads.
  • Read access is not wider than the app needs. Test a read of another user's path.
  • Public download URLs for private files are reviewed. A shared download token works outside the rules.

Realtime Database#

  • The root has no ".read": true or ".write": true for private data.
  • Child paths use auth.uid checks and .validate rules.

Auth and server side#

  • Anonymous sign-in is off, or its rules are as strict as for other users.
  • Role claims are set only by trusted server code.
  • Cloud Functions and Admin SDK code check authorization, because rules do not apply there. See the authentication testing checklist.

For the two-account method on object access, see IDOR testing and multi-tenant isolation testing.

What are the common Firebase mistakes?#

Look for these flaws in a Firebase review:

  • Test-mode rules left in place. Rules written for development stay live, sometimes with an expiry date nobody tracks.
  • Owner checks that trust the client. A rule compares a field in request.resource.data to the user, but a different path lets the user change the owner field.
  • Role fields the user can write. A user sets role: "admin" on their own document.
  • Open reads for lists. A rule that allows read on a collection also lets clients list the whole collection.
  • Storage rules that differ from Firestore rules. The database is locked, and the bucket is open.
  • Unvalidated writes. No type, size or field checks, so a client can store anything.

How do you run the tests in CI?#

Add the unit tests to your build. Start the emulator, run the suite, and fail the build on any failed assertion. For each change to a rules file, write the denied-case test first, then the allowed-case test. Keep the rules in version control and review them like code. Test before you deploy, and deploy rules only from the main branch.

What should a good finding say?#

Name the path, the caller, the operation and the proof. For example: "A signed-out client reads /orders/{id} from Firestore. Cause: rule allow read: if true on orders. Fix: replace with an owner check and add a denied-case unit test. Retest: assertFails passes in CI." Add the failing unit test to your suite so the flaw cannot return unseen.

For risks across Bubble, Supabase and Firebase, see no-code app security testing. The database-first sibling guide is Supabase security testing.

Questions

Do Security Rules apply to the Admin SDK?

No. Firebase documents that the server client libraries bypass all Cloud Firestore Security Rules. Rules protect access from mobile and web clients. Protect server code with its own authorization checks and least-privilege service accounts.

Are Firestore rules filters?

No. Firestore rules do not filter query results. A query succeeds only if the rules allow every document it could return. Write queries with the same constraints as your rules, and test the exact queries your app sends.

Is request.auth != null enough for a rule?

Rarely. It only checks that someone is signed in, and anyone can often create an account. Firebase marks such basic rules as not recommended for production. Compare request.auth.uid to the owner of the document, or check a role claim.

Can I test rules without touching production data?

Yes. The Local Emulator Suite runs Firestore, Storage and Realtime Database locally. Firebase says the v9 rules-unit-testing library never touches your production resources. Use a demo project ID for tests.

Sources

  1. Firebase Docs: Firebase Security Rules (checked 9 November 2026)
  2. Firebase Docs: Unit-test your Security Rules with the Local Emulator Suite (checked 9 November 2026)
  3. Firebase Docs: Cloud Firestore Security Rules basics (checked 9 November 2026)
  4. Firebase Docs: Security Rules and queries (rules are not filters) (checked 9 November 2026)
  5. Firebase Docs: Cloud Storage Security Rules basics (checked 9 November 2026)
  6. Firebase Docs: Firebase Local Emulator Suite (checked 9 November 2026)