v1.0.0 to v1.0.1

75 added, 318 removed. Audit A to A.

---
name: firebase-security-rules-auditor
- description: 'Audit Firebase Security Rules for Firestore and Cloud Storage. Use when reviewing rules diffs, hardening Firebase apps, validating generated rules, checking emulator tests, or investigating authorization, data validation, privilege escalation, and resource abuse risks in Firebase projects.'
+ description: A skill to evaluate how secure Firestore security rules are. Use this when Firestore security rules are updated to ensure that the generated rules are extremely secure and robust.
zh_description: "审计 Firestore 与 Cloud Storage 安全规则,检查授权、字段校验、权限提升、资源滥用和模拟器测试覆盖。"
- version: "1.0.0"
+ version: "1.0.1"
author: seaworld008
source: github:firebase/agent-skills
source_url: "https://github.com/firebase/agent-skills/tree/main/skills/firebase-security-rules-auditor"
license: Apache-2.0
tags: '[firebase, firestore, cloud-storage, security-rules, authorization, appsec]'
created_at: "2026-07-06"
- updated_at: "2026-07-06"
+ updated_at: "2026-07-13"
quality: 4
complexity: advanced
---
- # Firebase Security Rules Auditor
-
- Review Firebase Security Rules as an application security boundary, not as a
- formatting exercise. The goal is to prove whether an attacker can read data,
- write invalid state, escalate privileges, corrupt another user's records, or
- abuse storage and query costs.
-
- Use this skill for Firestore rules, Cloud Storage rules, emulator test coverage,
- generated rules, and pull requests that touch Firebase schemas or client access
- patterns.
-
- ## Trigger / When to Use
-
- - A PR changes `firestore.rules`, `storage.rules`, `firebase.json`, or Firebase
- emulator tests.
- - An AI-generated app adds Firestore or Cloud Storage rules and needs a security
- pass before deployment.
- - A team reports permission errors, overly broad reads, or unexpected access
- from another account.
- - Rules depend on custom claims, user document roles, group membership, document
- owners, or tenant IDs.
- - The app stores user profiles, team data, uploads, invite links, chat messages,
- billing metadata, or admin-only fields.
- - The user asks whether Firebase rules are "secure", "production ready", or
- "strict enough".
-
- ## Core Capabilities
-
- 1. Map the protected data model and trust boundaries.
- 2. Detect authorization bypasses across users, teams, tenants, and admin roles.
- 3. Compare create, update, delete, get, list, and query behavior separately.
- 4. Review field-level validation, immutable fields, type checks, and size caps.
- 5. Identify rules that trust `request.resource.data` for authority.
- 6. Check Cloud Storage path ownership, metadata validation, content type, and
- file size controls.
- 7. Produce emulator test cases that demonstrate each high-risk access path.
- 8. Rank findings by exploitability and data impact.
-
- ## Audit Workflow
-
- ### 1. Inventory Firebase Surfaces
-
- Search for Firebase configuration and rules files:
-
- ```bash
- find . -name 'firestore.rules' -o -name 'storage.rules' -o -name 'firebase.json' -o -name '*.rules'
- rg -n "initializeApp|getFirestore|getStorage|collection\\(|doc\\(|ref\\(" .
- rg -n "request\\.auth|request\\.resource|resource\\.data|get\\(|exists\\(|allow " .
- ```
-
- Record:
-
- - Firestore collections and subcollections.
- - Cloud Storage bucket paths.
- - Client call sites that read, write, upload, or delete.
- - Server/admin code paths that bypass rules through Admin SDK.
- - Auth model: anonymous, email, custom claims, team membership, multi-tenant
- organization IDs, or public reads.
-
- ### 2. Build an Access Matrix
-
- Create a compact matrix before judging the rules:
-
- | Actor | Examples | Expected access |
- |---|---|---|
- | unauthenticated | signed-out browser, crawler | only public data, if any |
- | authenticated owner | user editing own profile | own records and own uploads |
- | authenticated non-owner | another user in same app | no private data |
- | team member | collaborator with role | scoped team data only |
- | admin/moderator | custom claim or trusted ACL | privileged actions only |
- | attacker with chosen input | malformed writes, forged owner IDs | denied |
-
- If the product requirements are unclear, infer the safest reasonable model and
- state the assumption in the report.
-
- ### 3. Review Rule Semantics
-
- Firestore operations are not interchangeable. Check them independently:
-
- - `get`: single-document reads.
- - `list`: collection queries and collection group queries.
- - `create`: new document validation.
- - `update`: mutation of existing documents.
- - `delete`: destructive access and ownership constraints.
-
- For every `allow read` or `allow write`, expand it mentally into the exact
- operations. A broad `allow read, write` often hides a missing `list` restriction
- or an update bypass.
-
- ## Firestore Security Checklist
-
- ### Authorization
-
- - Require `request.auth != null` before any private read or write.
- - Bind ownership to trusted existing state, not user-controlled new data.
- - For owner documents, compare against stable paths or immutable fields:
-
- ```javascript
- function isOwner(userId) {
- return request.auth != null && request.auth.uid == userId;
- }
-
- match /users/{userId} {
- allow get: if isOwner(userId);
- allow update: if isOwner(userId)
- && request.resource.data.uid == resource.data.uid
- && request.resource.data.role == resource.data.role;
- }
- ```
-
- - For team or tenant rules, check both identity and membership:
-
- ```javascript
- function isTeamMember(teamId) {
- return request.auth != null
- && exists(/databases/$(database)/documents/teams/$(teamId)/members/$(request.auth.uid));
- }
- ```
-
- - Treat client-supplied `ownerId`, `teamId`, `role`, `isAdmin`, `tenantId`, and
- `permissions` as untrusted unless they are checked against an existing trusted
- document or custom claim.
-
- ### Create vs Update Bypass
-
- Compare validation on `create` and `update`. A common bug is allowing a safe
- create and then permitting an update into unsafe state.
-
- High-risk examples:
-
- - User creates `{ role: "member" }`, then updates to `{ role: "admin" }`.
- - User creates a valid document, then changes `ownerId`.
- - User bypasses type checks by updating only one field.
- - User grows arrays or strings after creation to cause resource abuse.
-
- Prefer helper functions for repeated invariants:
-
- ```javascript
- function hasImmutableIdentity() {
- return request.resource.data.uid == resource.data.uid
- && request.resource.data.ownerId == resource.data.ownerId
- && request.resource.data.createdAt == resource.data.createdAt;
- }
-
- function isValidProfile(data) {
- return data.keys().hasOnly(["uid", "displayName", "photoURL", "role", "createdAt", "updatedAt"])
- && data.uid is string
- && data.displayName is string
- && data.displayName.size() <= 80
- && data.role in ["member"]
- && data.createdAt is timestamp
- && data.updatedAt is timestamp;
- }
- ```
-
- ### Field-Level Validation
-
- Check that rules validate:
-
- - Allowed fields with `keys().hasOnly(...)`.
- - Required fields with `keys().hasAll(...)`.
- - Types with `is string`, `is bool`, `is int`, `is number`, `is timestamp`,
- `is list`, or `is map`.
- - String and array sizes with `.size()`.
- - Enum values with `in [...]`.
- - Server timestamps or monotonic timestamps where the app relies on ordering.
- - Immutable fields with `request.resource.data.field == resource.data.field`.
-
- Do not accept field allowlists alone as authorization. `diff().affectedKeys()`
- and `hasOnly()` restrict what can change, not who can change it.
-
- ### Query Safety
-
- Rules are not filters. A query must be provably allowed for every possible
- result. Review each list rule against real client queries:
-
- - Does the client include `where("ownerId", "==", uid)` when rules expect owner
- filtering?
- - Can a user query another tenant by changing a route param?
- - Are collection group queries covered by separate match blocks?
- - Are public listings intended, or did `allow read` accidentally include list?
-
- If list access is not needed, separate it from get:
-
- ```javascript
- allow get: if isOwner(userId);
- allow list: if false;
- ```
-
- ### Cross-Document Checks
-
- `get()` and `exists()` are useful but can become expensive or fragile:
-
- - Check that referenced paths cannot be controlled to escape tenant boundaries.
- - Confirm membership documents are created by trusted code or strict rules.
- - Avoid trusting a role document that the same user can edit.
- - Watch for deleted membership edge cases where stale client state still writes.
-
- ## Cloud Storage Rules Checklist
-
- Review Storage rules separately from Firestore rules. Storage paths and metadata
- often encode authorization.
-
- ```javascript
- match /users/{userId}/uploads/{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/.*');
- }
- ```
-
- Check:
-
- - Path ownership: user-controlled paths must include a verified `uid`, `teamId`,
- or document ID.
- - Read policy: profile images may be public, but private uploads should not be.
- - Write policy: content type, size, extension assumptions, and overwrite rules.
- - Metadata: do not trust client metadata for authorization without validation.
- - Deletion: users should not delete another user's files through guessed paths.
- - Pairing with Firestore: if metadata documents point to files, both sides must
- enforce the same ownership model.
+ # Overview
- ## Emulator Test Patterns
+ This skill acts as an auditor for Firebase Security Rules, evaluating them
+ against a rigorous set of criteria to ensure they are secure, robust, and
+ correctly implemented.
- Prefer Firebase emulator tests for every material rule. Test both allowed and
- denied paths.
+ # Scoring Criteria
- ```javascript
- import {
- assertFails,
- assertSucceeds,
- initializeTestEnvironment,
- } from "@firebase/rules-unit-testing";
- import { doc, getDoc, setDoc, updateDoc } from "firebase/firestore";
+ ## Assessment: Security Validator (Red Team Edition)
- test("user cannot update another user's profile", async () => {
- const env = await initializeTestEnvironment({
- projectId: "demo-test",
- firestore: { rules: fs.readFileSync("firestore.rules", "utf8") },
- });
+ You are a Senior Security Auditor and Penetration Tester specializing in
+ Firestore. Your goal is to find "the hole in the wall." Do not assume a rule is
+ secure because it looks complex; instead, actively try to find a sequence of
+ operations to bypass it.
- await env.withSecurityRulesDisabled(async (ctx) => {
- await setDoc(doc(ctx.firestore(), "users/alice"), {
- uid: "alice",
- displayName: "Alice",
- role: "member",
- createdAt: new Date(),
- updatedAt: new Date(),
- });
- });
+ ### Mandatory Audit Checklist:
- const bobDb = env.authenticatedContext("bob").firestore();
- await assertFails(updateDoc(doc(bobDb, "users/alice"), {
- displayName: "Owned",
- }));
- });
- ```
+ 1. **The Update Bypass:** Compare 'create' and 'update' rules. Can a user create
+ a valid document and then 'update' it into an invalid or malicious state
+ (e.g., changing their role, bypassing size limits, or corrupting data types)?
+ 1. **Authority Source:** Does the security rely on user-provided data
+ (request.resource.data) for sensitive fields like 'role', 'isAdmin', or
+ 'ownerId'? Carefully consider the source for that authority.
+ 1. **Business Logic vs. Rules:** Does the rule set actually support the app's
+ purpose? (e.g., In a collaboration app, can collaborators actually read the
+ data? If not, the rules are "broken" or will force insecure workarounds).
+ 1. **Storage Abuse:** Are there string length or array size limits? If not,
+ label it as a "Resource Exhaustion/DoS" risk.
+ 1. **Type Safety:** Are fields checked with 'is string', 'is int', or 'is
+ timestamp'?
+ 1. **Field-Level vs. Identity-Level Security:** Be careful with rules that use
+ \`hasOnly()\` or \`diff()\`. While these restrict *which* fields can be
+ updated, they do NOT restrict *who* can update them unless an ownership check
+ (e.g., \`resource.data.uid == request.auth.uid\`) is also present. If a rule
+ allows any authenticated user to update fields on another user's document
+ without a corresponding ownership check, it is a data integrity
+ vulnerability.
- Minimum test set:
+ ### Admin Bootstrapping & Privileges:
- - unauthenticated read/write denial for private paths;
- - owner allowed get/update for expected fields;
- - non-owner denied get/list/update/delete;
- - role escalation denied;
- - owner ID or tenant ID mutation denied;
- - invalid type denied;
- - oversize string/list/file denied;
- - intended public read allowed, if public access exists.
+ The admin bootstrapping process is limited in this app. If the rules use a
+ single hardcoded admin email (e.g., checking request.auth.token.email ==
+ 'admin@example.com'), this should NOT count against the score as long as:
- ## Finding Severity
+ - email_verified is also checked (request.auth.token.email_verified == true).
+ - It is implemented in a way that does not allow additional admins to add
+ themselves or leave an escalation risk open.
- Use this scale:
+ ### Scoring Criteria (1-5):
- | Severity | Meaning |
- |---|---|
- | Critical | Unauthorized private data read, privilege escalation, cross-tenant access, admin bypass |
- | High | Write access to another user's data, role or owner mutation, broad public list of sensitive data |
- | Medium | Missing type validation on important fields, incomplete create/update parity, weak Storage metadata checks |
- | Low | Minor over-permissive public metadata, missing size cap on low-impact fields, unclear tests |
+ - **1 (Critical):** Unauthorized data access (leaks), privilege escalation, or
+ total validation bypass.
+ - **2 (Major):** Broken business logic, self-assigned roles, bypass of controls.
+ - **3 (Moderate):** PII exposure (e.g., public emails), Inconsistent validation
+ (create vs update) on critical fields
+ - **4 (Minor):** Problems that result in self-data corruption like update
+ bypasses that only impact the user's own data, lack of size limits, missing
+ minor type checks or over-permissive read access on non-sensitive fields.
+ - **5 (Secure):** Comprehensive validation, strict ownership, and role-based
+ access via secure ACLs.
- Adjust severity upward when the affected collection contains PII, billing data,
- messages, invite tokens, API credentials, location data, or child/user safety
- data.
+ Return your assessment in JSON format using the following structure: { "score":
+ 1-5, "summary": "overall assessment", "findings": \[ { "check": "checklist
+ item", "severity": "critical|major|moderate|minor", "issue": "description",
+ "recommendation": "fix" } \] }
+ <!-- LOCAL-QUALITY-SUPPLEMENT:START -->
+ ## Usage Notes
- ## Report Template
+ This supplement is maintained by the repository sync pipeline. It keeps the
+ imported upstream skill usable inside this curated collection when the upstream
+ source is intentionally concise.
- Return concise, evidence-based findings:
+ ## Common Patterns
- ```json
- {
- "score": 2,
- "summary": "Rules prevent basic unauthenticated access but allow authenticated users to modify other users' profile fields.",
- "findings": [
- {
- "severity": "high",
- "rule": "match /users/{userId} allow update",
- "issue": "The update rule checks request.auth != null but does not require request.auth.uid == userId.",
- "impact": "Any signed-in user can change another user's display name and profile metadata.",
- "recommendation": "Require owner identity and preserve immutable uid, role, and createdAt fields.",
- "test": "Add assertFails(updateDoc(doc(bobDb, 'users/alice'), ...))."
- }
- ]
- }
+ ```text
+ 1. Confirm that the user's task matches the skill trigger.
+ 2. Read the relevant project files or user-provided context before acting.
+ 3. Choose the smallest reversible action that advances the task.
+ 4. Run the verification command or manual check that proves the result.
+ 5. Report the outcome, evidence, and any remaining risk.
```
- ## Common Anti-Patterns
-
- - `allow read, write: if request.auth != null` on user, team, or message data.
- - Trusting `request.resource.data.ownerId == request.auth.uid` on update without
- checking `resource.data.ownerId`.
- - Allowing all authenticated users to list a collection because each document
- also has owner checks.
- - Letting users write `role`, `permissions`, `isAdmin`, or `plan` fields.
- - Using a user profile document as an admin source when users can edit that
- profile.
- - Allowing Storage uploads without size or content-type limits.
- - Relying on file extensions for security.
- - Assuming Admin SDK behavior is constrained by Security Rules.
- - Testing only allowed paths and skipping attacker paths.
-
## Boundaries
- - Do not claim a rule is secure without checking the app's data model and real
- client queries.
- - Do not weaken rules to make a failing client query work; change the query or
- data model instead.
- - Do not treat Security Rules as a replacement for server-side validation on
- privileged backend operations.
- - Do not recommend public read access unless the product explicitly requires
- public data and the data is safe to expose.
- - If requirements are ambiguous, choose least privilege and document the
- assumption.
+ - Prefer the upstream workflow for Firebase Security Rules Auditor; this section only adds local quality
+ guardrails.
+ - Do not invent project facts when required files, vaults, services, or tools are
+ unavailable.
+ - Stop and ask for clarification when the next action could overwrite user work,
+ expose private data, or change production state.
+ <!-- LOCAL-QUALITY-SUPPLEMENT:END -->