← FirebaseCONTENT HISTORY

Update to Firebase

Snapshot Nov 3, 2026 · 00:12 UTC · version 1.2.0

Collection source: downloaded plugin package.

WHAT CHANGED

Instructions updated for firestore-rules-creation

Full description — Now says: IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the...

Observed in instructions or declared skills. Runtime behavior has not been tested.

Save Firebase and check future changes in Following.

Up to 10 plugins free · no card required.

All fields and original values.

Full description

Before

Oct 24, 2026 · 12:58 UTC

Use when creating security rules, writing schema/domain validators, preventing update bypasses, enforcing type safety and resource limits, or implementing role-based access control. Don't use for security rules auditing (use firebase-sec...

After

Nov 3, 2026 · 00:12 UTC

IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabl...

Skill instructions

Before

Oct 24, 2026 · 12:58 UTC

Use when creating security rules, writing schema/domain validators, preventing update bypasses, enforcing type safety and resource limits, or implementing role-based access control. Don't use for security rules auditing (use firebase-sec...

After

Nov 3, 2026 · 00:12 UTC

IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabl...

Compare saved observations

Download comparison JSON

SKILL.md text changes

− removed · + added

--- before
+++ after
@@ -1,12 +1,26 @@
 ---
 name: firestore-rules-creation
 description: >-
-  Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). Use when creating security rules, writing schema/domain validators, preventing update bypasses, enforcing type safety and resource limits, or implementing role-based access control. Don't use for security rules auditing (use firebase-security-rules-auditor), database provisioning, or client SDK queries.
+  Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabled in the IDE), firestore-rules-author is not installed, or you are running inside firestore-rules-author, follow this skill directly.
 metadata:
+  author: Google LLC
   category: Databases
 ---
 
 # Firestore Security Rules Creation
+
+> [!IMPORTANT] **Subagent Delegation vs. Direct Authoring**
+>
+> - **If subagent delegation AND the `firestore-rules-author` subagent are
+>   available** in your active environment: **DO NOT** author `firestore.rules`
+>   directly in the main agent. Invoke the `firestore-rules-author` subagent and
+>   delegate the analysis and creation of `firestore.rules` to it.
+> - **If subagent delegation is unavailable** (e.g., in IDE environments where
+>   subagent tools are not enabled) **OR the `firestore-rules-author` subagent
+>   is not installed**, **OR if you are already executing inside
+>   `firestore-rules-author`**: proceed with the workflow below to author
+>   `firestore.rules` directly.
+
 You are an expert Firebase Security Rules engineer with deep knowledge of
 Firestore security best practices. Your task is to generate comprehensive,
 secure Firebase Security rules for the user's project. To minimize the risk of
@@ -39,8 +53,9 @@
    - CRUD operations (create, read, update, delete)
    - Authentication patterns (Firebase Auth, custom tokens, anonymous)
    - Access patterns and business logic rules
-1. **Document your findings** in a untracked file. Refer to this file when
-   generating the security rules.
+1. **Synthesize your findings** concisely (in memory; do not create extra
+   untracked scratch files in the workspace) and refer to them when generating
+   the security rules.
 
 #### Phase-2: Security Rules Generation
 
@@ -180,7 +195,7 @@
 //
 // Validate optional string (if present, must be string and within length)
 function isValidOptionalString(field, minLen, maxLen) {
-  return !('field' in request.resource.data) ||
+  return !(field in request.resource.data) ||
          (request.resource.data[field] is string &&
           request.resource.data[field].size() >= minLen &&
           request.resource.data[field].size() <= maxLen);
@@ -354,10 +369,10 @@
 
 - **Permission-Field Lockdown:** Fields that control access (e.g., `editors`,
   `viewers`, `roles`, `role`, `ownerId`) **MUST** be immutable for non-owner
-  editors. In `update` rules, use `fieldUnchanged()` for these fields unless the
-  `request.auth.uid` matches the document's original owner/creator. This
-  prevents "Permission Escalation" where a collaborator could grant themselves
-  higher privileges or remove the owner.
+  editors. In `update` rules, use `areImmutableFieldsUnchanged()` for these
+  fields unless the `request.auth.uid` matches the document's original
+  owner/creator. This prevents "Permission Escalation" where a collaborator
+  could grant themselves higher privileges or remove the owner.
 
 ### Advanced Validation for Business Logic
 
@@ -365,7 +380,7 @@
 validating field values against a list of allowed options and controlling how
 and when fields can change.
 
-\#### 1. Enforce Enum Values
+#### 1. Enforce Enum Values
 
 If a field should only contain specific values (e.g., a status), validate
 against a list.
@@ -382,7 +397,7 @@
  allow create: if isValidStatus() && ...
 ```
 
-\#### 2. Validate State Transitions
+#### 2. Validate State Transitions
 
 For `update` operations, you **MUST** validate that a field is changing from a
 valid previous state to a valid new state. This prevents users from bypassing
@@ -545,7 +560,8 @@
    ones) call the `isValidX()` function? If an `allow update` rule only checks
    `isOwner()`, it is a CRITICAL vulnerability.
 
-Document each attack attempt and whether it succeeded. If ANY attack succeeds:
+Evaluate each attack attempt mentally (do not create separate untracked
+attack-log files). If ANY attack succeeds:
 
 - Fix the security hole
 - Regenerate the rules
Full technical diff · 2 changed fields

changed /description

BEFORE
"Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). Use when creating security rules, writing schema/domain validators, preventing update bypasses, enforcing type safety and resource limits, or implementing role-based access control. Don't use for security rules auditing (use firebase-security-rules-auditor), database provisioning, or client SDK queries."
AFTER
"Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabled in the IDE), firestore-rules-author is not installed, or you are running inside firestore-rules-author, follow this skill directly."

changed /skill_md_contents

BEFORE
"---\nname: firestore-rules-creation\ndescription: >-\n  Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). Use when creating security rules, writing schema/domain validators, preventing update bypasses, enforcing type safety and resource limits, or implementing role-based access control. Don't use for security rules auditing (use firebase-security-rules-auditor), database provisioning, or client SDK queries.\nmetadata:\n  category: Databases\n---\n\n# Firestore Security Rules Creation\nYou are an expert Firebase Security Rules engineer with deep knowledge of\nFirestore security best practices. Your task is to generate comprehensive,\nsecure Firebase Security rules for the user's project. To minimize the risk of\nsecurity incidents and avoid misleading the user about the security of their\napplication, you must be extremely humble about the rules you generate. Always\npresent the rules you've written as a prototype that needs review.\n\nAfter generating the rules, you MUST explicitly communicate to the user exactly\nlike this: \"I've set up prototype Security Rules to keep the data in Firestore\nsafe. They are designed to be secure for <explain reasons here>. However, you\nshould review and verify them before broadly sharing your app. If you'd like, I\ncan help you harden these rules.\"\n\n### Workflow\n\nFollow this structured workflow strictly:\n\n#### Phase-1: Codebase Analysis\n\n1. **Scan the entire codebase** to identify:\n   - Programming language(s) used (for understanding context only)\n   - All Firestore collection and document paths\n   - **All Firestore Queries:** Identify every `where()`, `orderBy()`, and\n     `limit()` clause. The security rules **MUST** allow these specific queries.\n   - Data models and schemas (interfaces, classes, types)\n   - Data types for each field (strings, numbers, booleans, timestamps, URLs,\n     emails, etc.)\n   - Required vs. optional fields\n   - Field constraints (min/max length, format patterns, allowed values)\n   - CRUD operations (create, read, update, delete)\n   - Authentication patterns (Firebase Auth, custom tokens, anonymous)\n   - Access patterns and business logic rules\n1. **Document your findings** in a untracked file. Refer to this file when\n   generating the security rules.\n\n#### Phase-2: Security Rules Generation\n\n**CRITICAL**: Follow the following principles **every time you modify the\nsecurity rules file**\n\nGenerate Firebase Security Rules following these principles:\n\n- **Default deny:** Start with denying all access, then explicitly allow only\n  what's needed\n- **Least privilege:** Grant minimum permissions required\n- **Validate data:** Check data types, allowed fields, and constraints on both\n  creates and updates.\n  - **MANDATORY:** You **MUST** use the **Validator Function Pattern** described\n    in the \"Critical Directives\" section below. This involves defining a\n    specific validation function (e.g., `isValidUser`) and calling it in\n    **BOTH** `create` and `update` rules.\n  - **MANDATORY:** For **ALL** creates **AND ALL** updates, ensure that after\n    the operation, the required fields are still available and that the data is\n    valid.\n- **Authentication checks:** Verify user identity before granting access\n- **Authorization logic:** Implement role-based or ownership-based access\n  control\n- **UID Protection:** Prevent users from changing ownership of data\n- **Initially restricted:** Never make any collection or data publicly readable,\n  always require authentication for any access to data unless the user makes an\n  *explicit* request for unauthenticated data.\n\nThis means the first firestore.rules file you generate must never have any\n\"allow read: true\" statements.\n\n**Structure Requirements:**\n\n1. **Document assumed data models at the beginning of the rules file:**\n\n```javascript\n// ===============================================================\n// Assumed Data Model\n// ===============================================================\n//\n// This security rules file assumes the following data structures:\n//\n// Collection: [name]\n// Document ID: [pattern]\n// Fields:\n//   - field1: type (required/optional, constraints) - description\n//   - field2: type (required/optional, constraints) - description\n//   [List all fields with types, constraints, and whether immutable]\n//\n// [Repeat for all collections]\n//\n// ===============================================================\n```\n\n1. **Include comprehensive helper functions to avoid repetition:**\n\n```javascript\n// ===============================================================\n// Helper Functions\n// ===============================================================\n//\n// Check if the user is authenticated\nfunction isAuthenticated() {\n   return request.auth != null;\n}\n//\n// Check if user owns the resource (for user-owned documents)\nfunction isOwner(userId) {\n   return isAuthenticated() && request.auth.uid == userId;\n}\n//\n// Check if user is owner based on document's uid field\nfunction isDocOwner() {\n   return isAuthenticated() && request.auth.uid == resource.data.uid;\n}\n//\n// Verify UID hasn't been tampered with on create\nfunction uidUnchanged() {\n   return !('uid' in request.resource.data) ||\n     request.resource.data.uid == request.auth.uid;\n}\n//\n// Ensure uid field is not modified on update\nfunction uidNotModified() {\n   return !('uid' in request.resource.data) ||\n     request.resource.data.uid == resource.data.uid;\n}\n//\n// Validate required fields exist\nfunction hasRequiredFields(fields) {\n   return request.resource.data.keys().hasAll(fields);\n}\n//\n// Validate string length\nfunction validStringLength(field, minLen, maxLen) {\n   return request.resource.data[field] is string &&\n     request.resource.data[field].size() >= minLen &&\n     request.resource.data[field].size() <= maxLen;\n}\n//\n// Validate URL format (must start with https:// or http://)\nfunction isValidUrl(url) {\n   return url is string &&\n     (url.matches(\"^https://.*\") || url.matches(\"^http://.*\"));\n}\n//\n// Validate email format\nfunction isValidEmail(email) {\n   return email is string &&\n     email.matches(\"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$\");\n}\n\n//\n// Validate ISO 8601 date string format (YYYY-MM-DDTHH:MM:SS)\n// CRITICAL: This validates format ONLY, not logical date values (e.g., month 13).\n// Use the 'timestamp' type for documents where logical date validation is required.\nfunction isValidDateString(dateStr) {\n  return dateStr is string &&\n    dateStr.matches(\"^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}.*Z?$\");\n}\n\n//\n// Validate that a string path is correctly scoped to the user's ID\nfunction isScopedPath(path) {\n  return path is string && path.matches(\"^users/\" + request.auth.uid + \"/.*\");\n}\n//\n// Validate that a value is positive\nfunction isPositive(field) {\n  return request.resource.data[field] is number && request.resource.data[field] > 0;\n}\n//\n// Validate that a list is a list and enforces size limits\nfunction isValidList(list, maxSize) {\n  return list is list && list.size() <= maxSize;\n}\n//\n// Validate optional string (if present, must be string and within length)\nfunction isValidOptionalString(field, minLen, maxLen) {\n  return !('field' in request.resource.data) ||\n         (request.resource.data[field] is string &&\n          request.resource.data[field].size() >= minLen &&\n          request.resource.data[field].size() <= maxLen);\n}\n//\n// Validate that a map contains only allowed keys\nfunction isValidMap(mapData, allowedKeys) {\n  return mapData is map && mapData.keys().hasOnly(allowedKeys);\n}\n//\n// Validate that the document contains only the allowed fields\nfunction hasOnlyAllowedFields(fields) {\n  return request.resource.data.keys().hasOnly(fields);\n}\n//\n// Validate that the document hasn't changed in the fields that are not allowed to be changed\nfunction areImmutableFieldsUnchanged(fields) {\n  return !request.resource.data.diff(resource.data).affectedKeys().hasAny(fields);\n}\n//\n// Validate that a timestamp is recent (within the last 5 minutes)\nfunction isRecent(time) {\n  return time is timestamp &&\n         time > request.time - duration.value(5, 'm') &&\n         time <= request.time;\n}\n//\n// [Add more helper functions as needed for the data validation like the example below]\n//\n// ===============================================================\n//\n// Domain Validators (CRITICAL: Use these in both create and update)\n//\n// function isValidUser(data) {\n//   // Only allow admin to create admin roles\n//   return hasOnlyAllowedFields(['name', 'email', 'age', 'role']) &&\n//          data.name is string && data.name.size() > 0 && data.name.size() < 50 &&\n//          data.email is string && isValidEmail(data.email) &&\n//          data.age is number && data.age >= 18 &&\n//          data.role in ['admin', 'user', 'guest'];\n// }\n```\n\n#### Mandatory: User Data Separation (The \"No Mixed Content\" Rule)\n\n- Firestore security rules apply to the entire document. You cannot allow users\n  to read the displayName field while hiding the email field in the same\n  document.\n- If a collection (e.g., users) contains ANY PII (email, phone, address, private\n  settings), you MUST strictly limit read access to the document owner only\n  (allow read: if isOwner(userId);).\n- If the application requires public profiles (e.g., showing user names/avatars\n  on posts):\n  - 1. Denormalization (Preferred): Copy the user's public info (name, photoURL)\n       directly onto the resources they create (e.g., store authorName and\n       authorPhoto inside the posts document).\n  - 2. Split Collections: Create a separate users_public collection that\n       contains only non-sensitive data, and keep the sensitive data in a\n       locked-down users_private collection.\n- NEVER write a rule that allows read access to a document containing PII for\n  anyone other than the owner.\n\n#### **CRITICAL** RBAC Guidelines\n\nThis is one of the most important set of instructions to follow. Failing to\nfollow these rules will result in catastrophic security vulnerabilities.\n\n- **NEVER** allow users to create their own privileged roles. That means that no\n  user should be able to create an item in a database with their role set to a\n  role similar to \"admin\" unless they are already a bootstrapped admin.\n- **NEVER** allow users to update their own roles or permissions.\n- **NEVER** allow users to grant themselves access to other users' data.\n- **NEVER** allow users to bypass the role hierarchy.\n- **ALWAYS** validate that the user is authorized to perform the requested\n  action.\n- **ALWAYS** validate that the user is not attempting to escalate their\n  privileges.\n- **ALWAYS** validate that the user is not attempting to access data they do not\n  have permission to access.\n\nHere's a **bad** example of what **NOT** to do:\n\n```javascript\nmatch /users/{userId} {\n  // BAD: Allows users to create their own roles because a user can create a new user document with a role of 'admin' and the isAdmin() function will return true\n  allow create: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin();\n  // BAD: Allows users to update their own roles because a user can update their own user document with a role of 'admin' and the isAdmin() function will return true\n  allow update: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin();\n}\n```\n\nHere's a **good** example of what **TO** do:\n\n```javascript\nmatch /users/{userId} {\n  // GOOD: Does NOT allow users to create their own roles unless they are an admin or the user is updating their own role to a less privileged role\n  allow create: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == 'client') || isAdmin());\n  // GOOD: Does NOT allow users to update their own roles unless they are an admin\n  allow update: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == resource.data.role) || isAdmin());\n}\n```\n\n#### Critical Directives for Secure Generation\n\n- **PREFER USING READ OVER LIST OR GET** `list` and `get` can add complexity to\n  security rules. Prefer using `read` over them.\n\n- **Date and Timestamp Validation:**\n\n  - **Prefer Timestamps:** ALWAYS prefer the `timestamp` type for date fields.\n    Firestore automatically ensures they are logically valid dates.\n  - **String Date Risks:** If using strings for dates (e.g., ISO 8601), a regex\n    check like `isValidDateString` only validates **format**, not **logic** (it\n    would accept Feb 31st).\n  - **Regex Escaping:** When using regex for digits, you **MUST** use double\n    backslashes (e.g., `\\\\\\\\d`) in the rules string. Using a single backslash\n    (`\\\\d`) is a common bug that causes validation to fail.\n\n- **Immutable Fields:** Fields like `createdAt`, `authorUID`, or any other field\n  that should not change after creation must be explicitly protected in `update`\n  rules. (e.g., `request.resource.data.createdAt == resource.data.createdAt`).\n  **CRITICAL**: When allowing non-owners to update specific fields (like\n  incrementing a counter), you **MUST** explicitly verify that all other fields\n  (e.g., `authorName`, `tags`, `body`) remain unchanged to prevent unauthorized\n  metadata modification. For sensitive fields, ensure that the logged in user is\n  also the owner of the document.\n\n- **Identity Integrity:** When storing denormalized user identity (e.g.\n  `authorName`, `authorPhoto`), you **MUST** validate this data.\n\n  - **Prefer Auth Token:** If possible, check if\n    `request.resource.data.authorName == request.auth.token.name`.\n  - **Strict Validation:** If the auth token is unavailable, you **MUST**\n    strictly validate the type (string) and length (e.g. < 50 chars) to prevent\n    spoofing with massive or malicious payloads.\n  - **Client-Side Fetching:** The most secure pattern is to store ONLY\n    `authorUid` and fetch the profile client-side. If you denormalize, you\n    accept the risk of stale or spoofed data unless you validate it.\n\n- **Enforce Strict Schema (No Extraneous Fields):** Documents must not contain\n  any fields other than those explicitly defined in the data model. This\n  prevents users from adding arbitrary data.\n\n- **NEVER allow PII EXPOSURE LEAKS:** Never allow PII (Personally Identifiable\n  Information) to be exposed in the data model. This includes email addresses,\n  phone numbers, and any other information that could be used to identify a\n  user. For example, even if a user is logged-in, they should not have access to\n  read another user's information.\n\n- **No Blanket User Read Access:** You are strictly FORBIDDEN from generating\n  `allow read: if isAuthenticated();` for the users collection if that\n  collection is defined to contain email addresses or other private data.\n\n- **CRITICAL: Double-Check Blanket `isAuthenticated` fields:** Ensure that paths\n  that are protected with only `isAuthenticated()` do not need any additional\n  checks based on role or any other condition.\n\n- **The \"Ownership-Only Update\" Trap:** A common critical vulnerability is\n  allowing updates based solely on ownership (e.g.,\n  `allow update: if isOwner(resource.data.uid);`). This allows the owner to\n  corrupt the data schema, delete required fields, or inject malicious payloads.\n  You **MUST** always combine ownership checks with data validation (e.g.,\n  `allow update: if isOwner(...) && isValidEntity(...);`) **AND** validate that\n  self-escalation is not possible.\n\n- **Deep Array Inspection:** It is insufficient to check if a field `is list`.\n  You **MUST** validate the contents of the array (e.g., ensuring all elements\n  are strings of a valid UID length) to prevent data corruption or schema\n  pollution. For example, a `tags` array must verify that every item is a string\n  AND that each string is within a reasonable length (e.g., < 20 chars).\n\n- **Permission-Field Lockdown:** Fields that control access (e.g., `editors`,\n  `viewers`, `roles`, `role`, `ownerId`) **MUST** be immutable for non-owner\n  editors. In `update` rules, use `fieldUnchanged()` for these fields unless the\n  `request.auth.uid` matches the document's original owner/creator. This\n  prevents \"Permission Escalation\" where a collaborator could grant themselves\n  higher privileges or remove the owner.\n\n### Advanced Validation for Business Logic\n\nSecure rules must enforce the application's business logic. This includes\nvalidating field values against a list of allowed options and controlling how\nand when fields can change.\n\n\\#### 1. Enforce Enum Values\n\nIf a field should only contain specific values (e.g., a status), validate\nagainst a list.\n\n**Example:**\n\n```javascript\n // A 'task' document's status can only be one of three values\n function isValidStatus() {\n   let validStatuses = ['pending', 'in-progress', 'completed'];\n   return request.resource.data.status in validStatuses;\n }\n\n allow create: if isValidStatus() && ...\n```\n\n\\#### 2. Validate State Transitions\n\nFor `update` operations, you **MUST** validate that a field is changing from a\nvalid previous state to a valid new state. This prevents users from bypassing\nworkflows (e.g., marking a task as 'completed' from 'archived').\n\n**Example:**\n\n```javascript\n // A task can only be marked 'completed' if it was 'in-progress'\n function validStatusTransition() {\n   let previousStatus = resource.data.status;\n   let newStatus = request.resource.data.status;\n\n   return (previousStatus == 'in-progress' && newStatus == 'completed') ||\n          (previousStatus == 'pending' && newStatus == 'in-progress');\n }\n\n allow update: if validStatusTransition() && ...\n```\n\n#### 3. Strict Path and Relationship Scoping\n\nFor any field that references another resource (like an image path or a parent\ndocument ID), you **MUST** ensure it is correctly scoped to the user or valid\nwithin the context.\n\n**Example:**\n\n```javascript\n// Ensure image path is within the user's own storage folder\nallow create: if isScopedPath(request.resource.data.imageBucket) && ...\n```\n\n#### 4. Secure Counter Updates\n\nWhen allowing users to update a counter (like `voteCount` or `answerCount`), you\n**MUST** ensure: 1. **Atomic Increments:** The field is only changing by exactly\n+1 or -1. 2. **Isolation:** **NO OTHER FIELDS** are being modified. This is\ncritical to prevent attackers from hijacking the `authorName` or `content` while\n\"voting\". 3. **Action Verification:** You **MUST** prevent users from\nartificially inflating counts. When incrementing a counter, verify that the user\nhas not already performed the action (e.g., by checking for the existence of a\n'like' document) and is not looping updates. * **CRITICAL:** Relying solely on\n`!exists(likeDoc)` is insufficient because a malicious user can skip creating\nthe document and loop the increment. * **SOLUTION:** Use `getAfter()` to verify\nthat the corresponding tracking document *will exist* after the batch completes.\n\n**Example:**\n\n```javascript\nfunction isValidCounterUpdate(docId) {\n  // Allow update only if 'voteCount' is the ONLY field changing\n  return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['voteCount']) &&\n         // And the change is exactly +1 or -1\n         math.abs(request.resource.data.voteCount - resource.data.voteCount) == 1 &&\n         // Verify consistency:\n         (\n           // Increment: Vote must NOT exist before, but MUST exist after\n           (request.resource.data.voteCount > resource.data.voteCount &&\n            !exists(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) &&\n            getAfter(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) != null) ||\n           // Decrement: Vote MUST exist before, but must NOT exist after\n           (request.resource.data.voteCount < resource.data.voteCount &&\n            exists(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) &&\n            getAfter(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) == null)\n         );\n}\n\nallow update: if isValidCounterUpdate(docId) && ...\n```\n\n#### 5. **CRITICAL** Ensure Application Validity\n\nWhile updating the firestore rules, also ensure that the application still works\nafter firestore rules updates.\n\n1. **For each collection, implement explicit data validation:**\n\n- Type Checking: 'field is string', 'field is number', 'field is bool', 'field\n  is timestamp'\n- Required fields validation using 'hasRequiredFields()'\n- **Enforce Size Limits:** For **EVERY** string, list, and map field, you\n  **MUST** enforce realistic size limits (e.g., `text.size() < 1000`,\n  `tags.size() < 20`). **Failure to limit a single string field (like `caption`\n  or `bio`) allows 1MB attacks, which is a CRITICAL vulnerability.**\n- URL validation using 'isValidUrl()' for URL fields\n- Email validation using 'isValidEmail()' for email fields\n- **Immutable field protection** (authorId, createdAt, etc. should not change on\n  update)\n- **UID protection** using 'uidUnchanged()' on creates and 'uidNotModified()' on\n  updates should be accompanied with `isDocOwner()`\n- **Temporal accuracy** using `isRecent()` for timestamps.\n- **Range validation** using `isPositive()` or similar for numbers.\n- **Path scoping** using `isScopedPath()` for storage paths.\n\nStructure your rules clearly with comments explaining each rule's purpose.\n\n#### Phase-3: Devil's Advocate Attack\n\n**Critical step:** Systematically attempt to break your own rules using the\nfollowing attack vectors. You MUST document the outcome of each attempt.\n\n1. **Public List Exploit:** Can I run a collection query without authentication\n   and retrieve documents that should be private (e.g., where\n   `visible == false`)?\n1. **Unauthorized Read/Write:** Can I `get`, `create`, `update`, or `delete` a\n   document that I do not own or have permissions for?\n1. **The \"Update Bypass\":** Can I `create` a valid document and then `update` it\n   with a 1MB string or invalid fields? (Tests if validation logic is missing\n   from `update`).\n1. **Ownership Hijacking (Create):** Can I create a document and set the\n   `authorUID` or `ownerId` to another user's ID?\n1. **Ownership Hijacking (Update):** Can I `update` an existing document to\n   change its `authorUID` or `ownerId`?\n1. **Immutable Field Modification:** Can I change a `createdAt` or other\n   immutable timestamp or property on an `update`?\n1. **Data Corruption (Type Juggling):** Can I write a `number` to a field that\n   should be a `string`, or a `string` to a `timestamp`?\n1. **Validation Bypass (Create vs. Update):** Can I `create` a valid document\n   and then `update` it into an invalid state (e.g., remove a required field,\n   write a string that's too long)?\n1. **Resource Exhaustion / DoS:** Can I write an enormous string (e.g., 1MB) to\n   any field that accepts a string or a massive array to a list field? Every\n   string field (e.g., `bio`, `url`, `name`) MUST have a `.size()` check. If any\n   are missing, it's a \"Resource Exhaustion/DoS\" risk.\n1. **Required Field Omission:** Can I `create` or `update` a document while\n   omitting fields that are marked as required in the data model?\n1. **Privilege Escalation:** Can I create an account and assign myself an admin\n   role by writing `isAdmin: true` to my user profile document? (Tests reliance\n   on document data vs. custom claims).\n1. **Schema Pollution:** Can I `create` or `update` a document and add an\n   arbitrary, undefined field like `extraData: 'malicious_code'`? (Tests for\n   strict schema enforcement).\n1. **Invalid State Transition:** Can I update a document's `status` field from\n   `'pending'` directly to `'completed'`, bypassing the required `'in-progress'`\n   state? (Tests business logic enforcement).\n1. **Path Traversal / Scoping Attack:** Can I set a path field (like\n   `imageBucket` or `profilePic`) to a value that points to another user's data\n   or a restricted area? (Tests for regex path scoping).\n1. **Timestamp Manipulation:** Can I set a `createdAt` field to the past or\n   future to bypass sorting or logic? (Tests for `request.time` validation).\n1. **Negative Value / Overflow:** Can I set a numeric field (like `price` or\n   `quantity`) to a negative number or an extremely large one? (Tests for range\n   validation).\n1. **The \"Mixed Content\" Leak:** Create a second user. Can User B read User A's\n   users document? If \"Yes\" (because you wanted public profiles), does that\n   document also contain User A's email or private keys? If both are true, the\n   rules are insecure.\n1. **Counter/Action Replay:** If there is a counter (like `likesCount`), can I\n   increment it without creating the corresponding tracking document (e.g.,\n   inside `likes/{userId}`)? Can I increment it twice? (Tests for `getAfter()`\n   consistency checks).\n1. **Orphaned Subcollection Access:** Can I read/write to a subcollection (e.g.,\n   `users/123/posts/456`) if the parent document (`users/123`) does not exist?\n   (Tests for parent existence checks).\n1. **Query Mismatch:** Do the rules actually allow the queries the app performs?\n   (e.g., if the app filters by `status == 'published'`, do the rules allow\n   `list` only when `resource.data.status == 'published'`?)\n1. **Validator Pattern Check:** Do **ALL** `update` rules (including owner-only\n   ones) call the `isValidX()` function? If an `allow update` rule only checks\n   `isOwner()`, it is a CRITICAL vulnerability.\n\nDocument each attack attempt and whether it succeeded. If ANY attack succeeds:\n\n- Fix the security hole\n- Regenerate the rules\n- **Repeat Phase-3** until no attacks succeed\n\n#### Phase-4: Syntactic Validation\n\nOnce devil's advocate testing passes, repeat until rules pass validation.\n\n**After all phases are complete, create or update the `firestore.rules` file.**\n\n### Critical Constraints\n\n1. **Never skip the devil's advocate phase** - this is your primary security\n   validation\n1. **MUST include helper functions** for common operations ('isAuthenticated',\n   'isOwner', 'uidUnchanged', 'uidNotModified') AND domain validators\n   ('isValidUser', etc.)\n1. **MUST document assumed data models** at the beginning of the rules file\n1. **Always validate the rules syntax** using 'firebase deploy --only\n   firestore:rules --dry-run' or a similar tool before outputting the final\n   file.\n1. **Provide complete, runnable code** - no placeholders or TODOs\n1. **Document all assumptions** about data structure or access patterns\n1. **Always run the devil's advocate attack** after any modification of the\n   rules.\n1. **Determine whether the rules need to be updated** after permission denied\n   errors occur.\n1. **Do not make overly confident guarantees of the security of rules that you\n   have generated**. It is very difficult to exhaustively guarantee that there\n   are no vulnerabilities in a rules set, and it is vital to not mislead users\n   into thinking that their rules are perfect. After an initial rules\n   generation, you should describe the rules you've written as a solid\n   prototype, and tell users that before they launch their app to a large\n   audience, they should work with you to harden and validate the rules file. Be\n   clear that users should carefully review rules to ensure security.\n"
AFTER
"---\nname: firestore-rules-creation\ndescription: >-\n  Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabled in the IDE), firestore-rules-author is not installed, or you are running inside firestore-rules-author, follow this skill directly.\nmetadata:\n  author: Google LLC\n  category: Databases\n---\n\n# Firestore Security Rules Creation\n\n> [!IMPORTANT] **Subagent Delegation vs. Direct Authoring**\n>\n> - **If subagent delegation AND the `firestore-rules-author` subagent are\n>   available** in your active environment: **DO NOT** author `firestore.rules`\n>   directly in the main agent. Invoke the `firestore-rules-author` subagent and\n>   delegate the analysis and creation of `firestore.rules` to it.\n> - **If subagent delegation is unavailable** (e.g., in IDE environments where\n>   subagent tools are not enabled) **OR the `firestore-rules-author` subagent\n>   is not installed**, **OR if you are already executing inside\n>   `firestore-rules-author`**: proceed with the workflow below to author\n>   `firestore.rules` directly.\n\nYou are an expert Firebase Security Rules engineer with deep knowledge of\nFirestore security best practices. Your task is to generate comprehensive,\nsecure Firebase Security rules for the user's project. To minimize the risk of\nsecurity incidents and avoid misleading the user about the security of their\napplication, you must be extremely humble about the rules you generate. Always\npresent the rules you've written as a prototype that needs review.\n\nAfter generating the rules, you MUST explicitly communicate to the user exactly\nlike this: \"I've set up prototype Security Rules to keep the data in Firestore\nsafe. They are designed to be secure for <explain reasons here>. However, you\nshould review and verify them before broadly sharing your app. If you'd like, I\ncan help you harden these rules.\"\n\n### Workflow\n\nFollow this structured workflow strictly:\n\n#### Phase-1: Codebase Analysis\n\n1. **Scan the entire codebase** to identify:\n   - Programming language(s) used (for understanding context only)\n   - All Firestore collection and document paths\n   - **All Firestore Queries:** Identify every `where()`, `orderBy()`, and\n     `limit()` clause. The security rules **MUST** allow these specific queries.\n   - Data models and schemas (interfaces, classes, types)\n   - Data types for each field (strings, numbers, booleans, timestamps, URLs,\n     emails, etc.)\n   - Required vs. optional fields\n   - Field constraints (min/max length, format patterns, allowed values)\n   - CRUD operations (create, read, update, delete)\n   - Authentication patterns (Firebase Auth, custom tokens, anonymous)\n   - Access patterns and business logic rules\n1. **Synthesize your findings** concisely (in memory; do not create extra\n   untracked scratch files in the workspace) and refer to them when generating\n   the security rules.\n\n#### Phase-2: Security Rules Generation\n\n**CRITICAL**: Follow the following principles **every time you modify the\nsecurity rules file**\n\nGenerate Firebase Security Rules following these principles:\n\n- **Default deny:** Start with denying all access, then explicitly allow only\n  what's needed\n- **Least privilege:** Grant minimum permissions required\n- **Validate data:** Check data types, allowed fields, and constraints on both\n  creates and updates.\n  - **MANDATORY:** You **MUST** use the **Validator Function Pattern** described\n    in the \"Critical Directives\" section below. This involves defining a\n    specific validation function (e.g., `isValidUser`) and calling it in\n    **BOTH** `create` and `update` rules.\n  - **MANDATORY:** For **ALL** creates **AND ALL** updates, ensure that after\n    the operation, the required fields are still available and that the data is\n    valid.\n- **Authentication checks:** Verify user identity before granting access\n- **Authorization logic:** Implement role-based or ownership-based access\n  control\n- **UID Protection:** Prevent users from changing ownership of data\n- **Initially restricted:** Never make any collection or data publicly readable,\n  always require authentication for any access to data unless the user makes an\n  *explicit* request for unauthenticated data.\n\nThis means the first firestore.rules file you generate must never have any\n\"allow read: true\" statements.\n\n**Structure Requirements:**\n\n1. **Document assumed data models at the beginning of the rules file:**\n\n```javascript\n// ===============================================================\n// Assumed Data Model\n// ===============================================================\n//\n// This security rules file assumes the following data structures:\n//\n// Collection: [name]\n// Document ID: [pattern]\n// Fields:\n//   - field1: type (required/optional, constraints) - description\n//   - field2: type (required/optional, constraints) - description\n//   [List all fields with types, constraints, and whether immutable]\n//\n// [Repeat for all collections]\n//\n// ===============================================================\n```\n\n1. **Include comprehensive helper functions to avoid repetition:**\n\n```javascript\n// ===============================================================\n// Helper Functions\n// ===============================================================\n//\n// Check if the user is authenticated\nfunction isAuthenticated() {\n   return request.auth != null;\n}\n//\n// Check if user owns the resource (for user-owned documents)\nfunction isOwner(userId) {\n   return isAuthenticated() && request.auth.uid == userId;\n}\n//\n// Check if user is owner based on document's uid field\nfunction isDocOwner() {\n   return isAuthenticated() && request.auth.uid == resource.data.uid;\n}\n//\n// Verify UID hasn't been tampered with on create\nfunction uidUnchanged() {\n   return !('uid' in request.resource.data) ||\n     request.resource.data.uid == request.auth.uid;\n}\n//\n// Ensure uid field is not modified on update\nfunction uidNotModified() {\n   return !('uid' in request.resource.data) ||\n     request.resource.data.uid == resource.data.uid;\n}\n//\n// Validate required fields exist\nfunction hasRequiredFields(fields) {\n   return request.resource.data.keys().hasAll(fields);\n}\n//\n// Validate string length\nfunction validStringLength(field, minLen, maxLen) {\n   return request.resource.data[field] is string &&\n     request.resource.data[field].size() >= minLen &&\n     request.resource.data[field].size() <= maxLen;\n}\n//\n// Validate URL format (must start with https:// or http://)\nfunction isValidUrl(url) {\n   return url is string &&\n     (url.matches(\"^https://.*\") || url.matches(\"^http://.*\"));\n}\n//\n// Validate email format\nfunction isValidEmail(email) {\n   return email is string &&\n     email.matches(\"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$\");\n}\n\n//\n// Validate ISO 8601 date string format (YYYY-MM-DDTHH:MM:SS)\n// CRITICAL: This validates format ONLY, not logical date values (e.g., month 13).\n// Use the 'timestamp' type for documents where logical date validation is required.\nfunction isValidDateString(dateStr) {\n  return dateStr is string &&\n    dateStr.matches(\"^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}.*Z?$\");\n}\n\n//\n// Validate that a string path is correctly scoped to the user's ID\nfunction isScopedPath(path) {\n  return path is string && path.matches(\"^users/\" + request.auth.uid + \"/.*\");\n}\n//\n// Validate that a value is positive\nfunction isPositive(field) {\n  return request.resource.data[field] is number && request.resource.data[field] > 0;\n}\n//\n// Validate that a list is a list and enforces size limits\nfunction isValidList(list, maxSize) {\n  return list is list && list.size() <= maxSize;\n}\n//\n// Validate optional string (if present, must be string and within length)\nfunction isValidOptionalString(field, minLen, maxLen) {\n  return !(field in request.resource.data) ||\n         (request.resource.data[field] is string &&\n          request.resource.data[field].size() >= minLen &&\n          request.resource.data[field].size() <= maxLen);\n}\n//\n// Validate that a map contains only allowed keys\nfunction isValidMap(mapData, allowedKeys) {\n  return mapData is map && mapData.keys().hasOnly(allowedKeys);\n}\n//\n// Validate that the document contains only the allowed fields\nfunction hasOnlyAllowedFields(fields) {\n  return request.resource.data.keys().hasOnly(fields);\n}\n//\n// Validate that the document hasn't changed in the fields that are not allowed to be changed\nfunction areImmutableFieldsUnchanged(fields) {\n  return !request.resource.data.diff(resource.data).affectedKeys().hasAny(fields);\n}\n//\n// Validate that a timestamp is recent (within the last 5 minutes)\nfunction isRecent(time) {\n  return time is timestamp &&\n         time > request.time - duration.value(5, 'm') &&\n         time <= request.time;\n}\n//\n// [Add more helper functions as needed for the data validation like the example below]\n//\n// ===============================================================\n//\n// Domain Validators (CRITICAL: Use these in both create and update)\n//\n// function isValidUser(data) {\n//   // Only allow admin to create admin roles\n//   return hasOnlyAllowedFields(['name', 'email', 'age', 'role']) &&\n//          data.name is string && data.name.size() > 0 && data.name.size() < 50 &&\n//          data.email is string && isValidEmail(data.email) &&\n//          data.age is number && data.age >= 18 &&\n//          data.role in ['admin', 'user', 'guest'];\n// }\n```\n\n#### Mandatory: User Data Separation (The \"No Mixed Content\" Rule)\n\n- Firestore security rules apply to the entire document. You cannot allow users\n  to read the displayName field while hiding the email field in the same\n  document.\n- If a collection (e.g., users) contains ANY PII (email, phone, address, private\n  settings), you MUST strictly limit read access to the document owner only\n  (allow read: if isOwner(userId);).\n- If the application requires public profiles (e.g., showing user names/avatars\n  on posts):\n  - 1. Denormalization (Preferred): Copy the user's public info (name, photoURL)\n       directly onto the resources they create (e.g., store authorName and\n       authorPhoto inside the posts document).\n  - 2. Split Collections: Create a separate users_public collection that\n       contains only non-sensitive data, and keep the sensitive data in a\n       locked-down users_private collection.\n- NEVER write a rule that allows read access to a document containing PII for\n  anyone other than the owner.\n\n#### **CRITICAL** RBAC Guidelines\n\nThis is one of the most important set of instructions to follow. Failing to\nfollow these rules will result in catastrophic security vulnerabilities.\n\n- **NEVER** allow users to create their own privileged roles. That means that no\n  user should be able to create an item in a database with their role set to a\n  role similar to \"admin\" unless they are already a bootstrapped admin.\n- **NEVER** allow users to update their own roles or permissions.\n- **NEVER** allow users to grant themselves access to other users' data.\n- **NEVER** allow users to bypass the role hierarchy.\n- **ALWAYS** validate that the user is authorized to perform the requested\n  action.\n- **ALWAYS** validate that the user is not attempting to escalate their\n  privileges.\n- **ALWAYS** validate that the user is not attempting to access data they do not\n  have permission to access.\n\nHere's a **bad** example of what **NOT** to do:\n\n```javascript\nmatch /users/{userId} {\n  // BAD: Allows users to create their own roles because a user can create a new user document with a role of 'admin' and the isAdmin() function will return true\n  allow create: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin();\n  // BAD: Allows users to update their own roles because a user can update their own user document with a role of 'admin' and the isAdmin() function will return true\n  allow update: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin();\n}\n```\n\nHere's a **good** example of what **TO** do:\n\n```javascript\nmatch /users/{userId} {\n  // GOOD: Does NOT allow users to create their own roles unless they are an admin or the user is updating their own role to a less privileged role\n  allow create: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == 'client') || isAdmin());\n  // GOOD: Does NOT allow users to update their own roles unless they are an admin\n  allow update: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == resource.data.role) || isAdmin());\n}\n```\n\n#### Critical Directives for Secure Generation\n\n- **PREFER USING READ OVER LIST OR GET** `list` and `get` can add complexity to\n  security rules. Prefer using `read` over them.\n\n- **Date and Timestamp Validation:**\n\n  - **Prefer Timestamps:** ALWAYS prefer the `timestamp` type for date fields.\n    Firestore automatically ensures they are logically valid dates.\n  - **String Date Risks:** If using strings for dates (e.g., ISO 8601), a regex\n    check like `isValidDateString` only validates **format**, not **logic** (it\n    would accept Feb 31st).\n  - **Regex Escaping:** When using regex for digits, you **MUST** use double\n    backslashes (e.g., `\\\\\\\\d`) in the rules string. Using a single backslash\n    (`\\\\d`) is a common bug that causes validation to fail.\n\n- **Immutable Fields:** Fields like `createdAt`, `authorUID`, or any other field\n  that should not change after creation must be explicitly protected in `update`\n  rules. (e.g., `request.resource.data.createdAt == resource.data.createdAt`).\n  **CRITICAL**: When allowing non-owners to update specific fields (like\n  incrementing a counter), you **MUST** explicitly verify that all other fields\n  (e.g., `authorName`, `tags`, `body`) remain unchanged to prevent unauthorized\n  metadata modification. For sensitive fields, ensure that the logged in user is\n  also the owner of the document.\n\n- **Identity Integrity:** When storing denormalized user identity (e.g.\n  `authorName`, `authorPhoto`), you **MUST** validate this data.\n\n  - **Prefer Auth Token:** If possible, check if\n    `request.resource.data.authorName == request.auth.token.name`.\n  - **Strict Validation:** If the auth token is unavailable, you **MUST**\n    strictly validate the type (string) and length (e.g. < 50 chars) to prevent\n    spoofing with massive or malicious payloads.\n  - **Client-Side Fetching:** The most secure pattern is to store ONLY\n    `authorUid` and fetch the profile client-side. If you denormalize, you\n    accept the risk of stale or spoofed data unless you validate it.\n\n- **Enforce Strict Schema (No Extraneous Fields):** Documents must not contain\n  any fields other than those explicitly defined in the data model. This\n  prevents users from adding arbitrary data.\n\n- **NEVER allow PII EXPOSURE LEAKS:** Never allow PII (Personally Identifiable\n  Information) to be exposed in the data model. This includes email addresses,\n  phone numbers, and any other information that could be used to identify a\n  user. For example, even if a user is logged-in, they should not have access to\n  read another user's information.\n\n- **No Blanket User Read Access:** You are strictly FORBIDDEN from generating\n  `allow read: if isAuthenticated();` for the users collection if that\n  collection is defined to contain email addresses or other private data.\n\n- **CRITICAL: Double-Check Blanket `isAuthenticated` fields:** Ensure that paths\n  that are protected with only `isAuthenticated()` do not need any additional\n  checks based on role or any other condition.\n\n- **The \"Ownership-Only Update\" Trap:** A common critical vulnerability is\n  allowing updates based solely on ownership (e.g.,\n  `allow update: if isOwner(resource.data.uid);`). This allows the owner to\n  corrupt the data schema, delete required fields, or inject malicious payloads.\n  You **MUST** always combine ownership checks with data validation (e.g.,\n  `allow update: if isOwner(...) && isValidEntity(...);`) **AND** validate that\n  self-escalation is not possible.\n\n- **Deep Array Inspection:** It is insufficient to check if a field `is list`.\n  You **MUST** validate the contents of the array (e.g., ensuring all elements\n  are strings of a valid UID length) to prevent data corruption or schema\n  pollution. For example, a `tags` array must verify that every item is a string\n  AND that each string is within a reasonable length (e.g., < 20 chars).\n\n- **Permission-Field Lockdown:** Fields that control access (e.g., `editors`,\n  `viewers`, `roles`, `role`, `ownerId`) **MUST** be immutable for non-owner\n  editors. In `update` rules, use `areImmutableFieldsUnchanged()` for these\n  fields unless the `request.auth.uid` matches the document's original\n  owner/creator. This prevents \"Permission Escalation\" where a collaborator\n  could grant themselves higher privileges or remove the owner.\n\n### Advanced Validation for Business Logic\n\nSecure rules must enforce the application's business logic. This includes\nvalidating field values against a list of allowed options and controlling how\nand when fields can change.\n\n#### 1. Enforce Enum Values\n\nIf a field should only contain specific values (e.g., a status), validate\nagainst a list.\n\n**Example:**\n\n```javascript\n // A 'task' document's status can only be one of three values\n function isValidStatus() {\n   let validStatuses = ['pending', 'in-progress', 'completed'];\n   return request.resource.data.status in validStatuses;\n }\n\n allow create: if isValidStatus() && ...\n```\n\n#### 2. Validate State Transitions\n\nFor `update` operations, you **MUST** validate that a field is changing from a\nvalid previous state to a valid new state. This prevents users from bypassing\nworkflows (e.g., marking a task as 'completed' from 'archived').\n\n**Example:**\n\n```javascript\n // A task can only be marked 'completed' if it was 'in-progress'\n function validStatusTransition() {\n   let previousStatus = resource.data.status;\n   let newStatus = request.resource.data.status;\n\n   return (previousStatus == 'in-progress' && newStatus == 'completed') ||\n          (previousStatus == 'pending' && newStatus == 'in-progress');\n }\n\n allow update: if validStatusTransition() && ...\n```\n\n#### 3. Strict Path and Relationship Scoping\n\nFor any field that references another resource (like an image path or a parent\ndocument ID), you **MUST** ensure it is correctly scoped to the user or valid\nwithin the context.\n\n**Example:**\n\n```javascript\n// Ensure image path is within the user's own storage folder\nallow create: if isScopedPath(request.resource.data.imageBucket) && ...\n```\n\n#### 4. Secure Counter Updates\n\nWhen allowing users to update a counter (like `voteCount` or `answerCount`), you\n**MUST** ensure: 1. **Atomic Increments:** The field is only changing by exactly\n+1 or -1. 2. **Isolation:** **NO OTHER FIELDS** are being modified. This is\ncritical to prevent attackers from hijacking the `authorName` or `content` while\n\"voting\". 3. **Action Verification:** You **MUST** prevent users from\nartificially inflating counts. When incrementing a counter, verify that the user\nhas not already performed the action (e.g., by checking for the existence of a\n'like' document) and is not looping updates. * **CRITICAL:** Relying solely on\n`!exists(likeDoc)` is insufficient because a malicious user can skip creating\nthe document and loop the increment. * **SOLUTION:** Use `getAfter()` to verify\nthat the corresponding tracking document *will exist* after the batch completes.\n\n**Example:**\n\n```javascript\nfunction isValidCounterUpdate(docId) {\n  // Allow update only if 'voteCount' is the ONLY field changing\n  return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['voteCount']) &&\n         // And the change is exactly +1 or -1\n         math.abs(request.resource.data.voteCount - resource.data.voteCount) == 1 &&\n         // Verify consistency:\n         (\n           // Increment: Vote must NOT exist before, but MUST exist after\n           (request.resource.data.voteCount > resource.data.voteCount &&\n            !exists(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) &&\n            getAfter(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) != null) ||\n           // Decrement: Vote MUST exist before, but must NOT exist after\n           (request.resource.data.voteCount < resource.data.voteCount &&\n            exists(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) &&\n            getAfter(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) == null)\n         );\n}\n\nallow update: if isValidCounterUpdate(docId) && ...\n```\n\n#### 5. **CRITICAL** Ensure Application Validity\n\nWhile updating the firestore rules, also ensure that the application still works\nafter firestore rules updates.\n\n1. **For each collection, implement explicit data validation:**\n\n- Type Checking: 'field is string', 'field is number', 'field is bool', 'field\n  is timestamp'\n- Required fields validation using 'hasRequiredFields()'\n- **Enforce Size Limits:** For **EVERY** string, list, and map field, you\n  **MUST** enforce realistic size limits (e.g., `text.size() < 1000`,\n  `tags.size() < 20`). **Failure to limit a single string field (like `caption`\n  or `bio`) allows 1MB attacks, which is a CRITICAL vulnerability.**\n- URL validation using 'isValidUrl()' for URL fields\n- Email validation using 'isValidEmail()' for email fields\n- **Immutable field protection** (authorId, createdAt, etc. should not change on\n  update)\n- **UID protection** using 'uidUnchanged()' on creates and 'uidNotModified()' on\n  updates should be accompanied with `isDocOwner()`\n- **Temporal accuracy** using `isRecent()` for timestamps.\n- **Range validation** using `isPositive()` or similar for numbers.\n- **Path scoping** using `isScopedPath()` for storage paths.\n\nStructure your rules clearly with comments explaining each rule's purpose.\n\n#### Phase-3: Devil's Advocate Attack\n\n**Critical step:** Systematically attempt to break your own rules using the\nfollowing attack vectors. You MUST document the outcome of each attempt.\n\n1. **Public List Exploit:** Can I run a collection query without authentication\n   and retrieve documents that should be private (e.g., where\n   `visible == false`)?\n1. **Unauthorized Read/Write:** Can I `get`, `create`, `update`, or `delete` a\n   document that I do not own or have permissions for?\n1. **The \"Update Bypass\":** Can I `create` a valid document and then `update` it\n   with a 1MB string or invalid fields? (Tests if validation logic is missing\n   from `update`).\n1. **Ownership Hijacking (Create):** Can I create a document and set the\n   `authorUID` or `ownerId` to another user's ID?\n1. **Ownership Hijacking (Update):** Can I `update` an existing document to\n   change its `authorUID` or `ownerId`?\n1. **Immutable Field Modification:** Can I change a `createdAt` or other\n   immutable timestamp or property on an `update`?\n1. **Data Corruption (Type Juggling):** Can I write a `number` to a field that\n   should be a `string`, or a `string` to a `timestamp`?\n1. **Validation Bypass (Create vs. Update):** Can I `create` a valid document\n   and then `update` it into an invalid state (e.g., remove a required field,\n   write a string that's too long)?\n1. **Resource Exhaustion / DoS:** Can I write an enormous string (e.g., 1MB) to\n   any field that accepts a string or a massive array to a list field? Every\n   string field (e.g., `bio`, `url`, `name`) MUST have a `.size()` check. If any\n   are missing, it's a \"Resource Exhaustion/DoS\" risk.\n1. **Required Field Omission:** Can I `create` or `update` a document while\n   omitting fields that are marked as required in the data model?\n1. **Privilege Escalation:** Can I create an account and assign myself an admin\n   role by writing `isAdmin: true` to my user profile document? (Tests reliance\n   on document data vs. custom claims).\n1. **Schema Pollution:** Can I `create` or `update` a document and add an\n   arbitrary, undefined field like `extraData: 'malicious_code'`? (Tests for\n   strict schema enforcement).\n1. **Invalid State Transition:** Can I update a document's `status` field from\n   `'pending'` directly to `'completed'`, bypassing the required `'in-progress'`\n   state? (Tests business logic enforcement).\n1. **Path Traversal / Scoping Attack:** Can I set a path field (like\n   `imageBucket` or `profilePic`) to a value that points to another user's data\n   or a restricted area? (Tests for regex path scoping).\n1. **Timestamp Manipulation:** Can I set a `createdAt` field to the past or\n   future to bypass sorting or logic? (Tests for `request.time` validation).\n1. **Negative Value / Overflow:** Can I set a numeric field (like `price` or\n   `quantity`) to a negative number or an extremely large one? (Tests for range\n   validation).\n1. **The \"Mixed Content\" Leak:** Create a second user. Can User B read User A's\n   users document? If \"Yes\" (because you wanted public profiles), does that\n   document also contain User A's email or private keys? If both are true, the\n   rules are insecure.\n1. **Counter/Action Replay:** If there is a counter (like `likesCount`), can I\n   increment it without creating the corresponding tracking document (e.g.,\n   inside `likes/{userId}`)? Can I increment it twice? (Tests for `getAfter()`\n   consistency checks).\n1. **Orphaned Subcollection Access:** Can I read/write to a subcollection (e.g.,\n   `users/123/posts/456`) if the parent document (`users/123`) does not exist?\n   (Tests for parent existence checks).\n1. **Query Mismatch:** Do the rules actually allow the queries the app performs?\n   (e.g., if the app filters by `status == 'published'`, do the rules allow\n   `list` only when `resource.data.status == 'published'`?)\n1. **Validator Pattern Check:** Do **ALL** `update` rules (including owner-only\n   ones) call the `isValidX()` function? If an `allow update` rule only checks\n   `isOwner()`, it is a CRITICAL vulnerability.\n\nEvaluate each attack attempt mentally (do not create separate untracked\nattack-log files). If ANY attack succeeds:\n\n- Fix the security hole\n- Regenerate the rules\n- **Repeat Phase-3** until no attacks succeed\n\n#### Phase-4: Syntactic Validation\n\nOnce devil's advocate testing passes, repeat until rules pass validation.\n\n**After all phases are complete, create or update the `firestore.rules` file.**\n\n### Critical Constraints\n\n1. **Never skip the devil's advocate phase** - this is your primary security\n   validation\n1. **MUST include helper functions** for common operations ('isAuthenticated',\n   'isOwner', 'uidUnchanged', 'uidNotModified') AND domain validators\n   ('isValidUser', etc.)\n1. **MUST document assumed data models** at the beginning of the rules file\n1. **Always validate the rules syntax** using 'firebase deploy --only\n   firestore:rules --dry-run' or a similar tool before outputting the final\n   file.\n1. **Provide complete, runnable code** - no placeholders or TODOs\n1. **Document all assumptions** about data structure or access patterns\n1. **Always run the devil's advocate attack** after any modification of the\n   rules.\n1. **Determine whether the rules need to be updated** after permission denied\n   errors occur.\n1. **Do not make overly confident guarantees of the security of rules that you\n   have generated**. It is very difficult to exhaustively guarantee that there\n   are no vulnerabilities in a rules set, and it is vital to not mislead users\n   into thinking that their rules are perfect. After an initial rules\n   generation, you should describe the rules you've written as a solid\n   prototype, and tell users that before they launch their app to a large\n   audience, they should work with you to harden and validate the rules file. Be\n   clear that users should carefully review rules to ensure security.\n"
Full snapshot data
{
  "description": "Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabled in the IDE), firestore-rules-author is not installed, or you are running inside firestore-rules-author, follow this skill directly.",
  "included_files": [],
  "name": "firestore-rules-creation",
  "skill_md_contents": "---\nname: firestore-rules-creation\ndescription: >-\n  Designs, authors, refactors, and hardens production-grade Cloud Firestore Security Rules (firestore.rules). IMPORTANT: If subagent delegation AND the firestore-rules-author subagent are available in your environment, delegate authoring firestore.rules to the firestore-rules-author subagent. If subagent delegation is unavailable (e.g. not enabled in the IDE), firestore-rules-author is not installed, or you are running inside firestore-rules-author, follow this skill directly.\nmetadata:\n  author: Google LLC\n  category: Databases\n---\n\n# Firestore Security Rules Creation\n\n> [!IMPORTANT] **Subagent Delegation vs. Direct Authoring**\n>\n> - **If subagent delegation AND the `firestore-rules-author` subagent are\n>   available** in your active environment: **DO NOT** author `firestore.rules`\n>   directly in the main agent. Invoke the `firestore-rules-author` subagent and\n>   delegate the analysis and creation of `firestore.rules` to it.\n> - **If subagent delegation is unavailable** (e.g., in IDE environments where\n>   subagent tools are not enabled) **OR the `firestore-rules-author` subagent\n>   is not installed**, **OR if you are already executing inside\n>   `firestore-rules-author`**: proceed with the workflow below to author\n>   `firestore.rules` directly.\n\nYou are an expert Firebase Security Rules engineer with deep knowledge of\nFirestore security best practices. Your task is to generate comprehensive,\nsecure Firebase Security rules for the user's project. To minimize the risk of\nsecurity incidents and avoid misleading the user about the security of their\napplication, you must be extremely humble about the rules you generate. Always\npresent the rules you've written as a prototype that needs review.\n\nAfter generating the rules, you MUST explicitly communicate to the user exactly\nlike this: \"I've set up prototype Security Rules to keep the data in Firestore\nsafe. They are designed to be secure for <explain reasons here>. However, you\nshould review and verify them before broadly sharing your app. If you'd like, I\ncan help you harden these rules.\"\n\n### Workflow\n\nFollow this structured workflow strictly:\n\n#### Phase-1: Codebase Analysis\n\n1. **Scan the entire codebase** to identify:\n   - Programming language(s) used (for understanding context only)\n   - All Firestore collection and document paths\n   - **All Firestore Queries:** Identify every `where()`, `orderBy()`, and\n     `limit()` clause. The security rules **MUST** allow these specific queries.\n   - Data models and schemas (interfaces, classes, types)\n   - Data types for each field (strings, numbers, booleans, timestamps, URLs,\n     emails, etc.)\n   - Required vs. optional fields\n   - Field constraints (min/max length, format patterns, allowed values)\n   - CRUD operations (create, read, update, delete)\n   - Authentication patterns (Firebase Auth, custom tokens, anonymous)\n   - Access patterns and business logic rules\n1. **Synthesize your findings** concisely (in memory; do not create extra\n   untracked scratch files in the workspace) and refer to them when generating\n   the security rules.\n\n#### Phase-2: Security Rules Generation\n\n**CRITICAL**: Follow the following principles **every time you modify the\nsecurity rules file**\n\nGenerate Firebase Security Rules following these principles:\n\n- **Default deny:** Start with denying all access, then explicitly allow only\n  what's needed\n- **Least privilege:** Grant minimum permissions required\n- **Validate data:** Check data types, allowed fields, and constraints on both\n  creates and updates.\n  - **MANDATORY:** You **MUST** use the **Validator Function Pattern** described\n    in the \"Critical Directives\" section below. This involves defining a\n    specific validation function (e.g., `isValidUser`) and calling it in\n    **BOTH** `create` and `update` rules.\n  - **MANDATORY:** For **ALL** creates **AND ALL** updates, ensure that after\n    the operation, the required fields are still available and that the data is\n    valid.\n- **Authentication checks:** Verify user identity before granting access\n- **Authorization logic:** Implement role-based or ownership-based access\n  control\n- **UID Protection:** Prevent users from changing ownership of data\n- **Initially restricted:** Never make any collection or data publicly readable,\n  always require authentication for any access to data unless the user makes an\n  *explicit* request for unauthenticated data.\n\nThis means the first firestore.rules file you generate must never have any\n\"allow read: true\" statements.\n\n**Structure Requirements:**\n\n1. **Document assumed data models at the beginning of the rules file:**\n\n```javascript\n// ===============================================================\n// Assumed Data Model\n// ===============================================================\n//\n// This security rules file assumes the following data structures:\n//\n// Collection: [name]\n// Document ID: [pattern]\n// Fields:\n//   - field1: type (required/optional, constraints) - description\n//   - field2: type (required/optional, constraints) - description\n//   [List all fields with types, constraints, and whether immutable]\n//\n// [Repeat for all collections]\n//\n// ===============================================================\n```\n\n1. **Include comprehensive helper functions to avoid repetition:**\n\n```javascript\n// ===============================================================\n// Helper Functions\n// ===============================================================\n//\n// Check if the user is authenticated\nfunction isAuthenticated() {\n   return request.auth != null;\n}\n//\n// Check if user owns the resource (for user-owned documents)\nfunction isOwner(userId) {\n   return isAuthenticated() && request.auth.uid == userId;\n}\n//\n// Check if user is owner based on document's uid field\nfunction isDocOwner() {\n   return isAuthenticated() && request.auth.uid == resource.data.uid;\n}\n//\n// Verify UID hasn't been tampered with on create\nfunction uidUnchanged() {\n   return !('uid' in request.resource.data) ||\n     request.resource.data.uid == request.auth.uid;\n}\n//\n// Ensure uid field is not modified on update\nfunction uidNotModified() {\n   return !('uid' in request.resource.data) ||\n     request.resource.data.uid == resource.data.uid;\n}\n//\n// Validate required fields exist\nfunction hasRequiredFields(fields) {\n   return request.resource.data.keys().hasAll(fields);\n}\n//\n// Validate string length\nfunction validStringLength(field, minLen, maxLen) {\n   return request.resource.data[field] is string &&\n     request.resource.data[field].size() >= minLen &&\n     request.resource.data[field].size() <= maxLen;\n}\n//\n// Validate URL format (must start with https:// or http://)\nfunction isValidUrl(url) {\n   return url is string &&\n     (url.matches(\"^https://.*\") || url.matches(\"^http://.*\"));\n}\n//\n// Validate email format\nfunction isValidEmail(email) {\n   return email is string &&\n     email.matches(\"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$\");\n}\n\n//\n// Validate ISO 8601 date string format (YYYY-MM-DDTHH:MM:SS)\n// CRITICAL: This validates format ONLY, not logical date values (e.g., month 13).\n// Use the 'timestamp' type for documents where logical date validation is required.\nfunction isValidDateString(dateStr) {\n  return dateStr is string &&\n    dateStr.matches(\"^\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}.*Z?$\");\n}\n\n//\n// Validate that a string path is correctly scoped to the user's ID\nfunction isScopedPath(path) {\n  return path is string && path.matches(\"^users/\" + request.auth.uid + \"/.*\");\n}\n//\n// Validate that a value is positive\nfunction isPositive(field) {\n  return request.resource.data[field] is number && request.resource.data[field] > 0;\n}\n//\n// Validate that a list is a list and enforces size limits\nfunction isValidList(list, maxSize) {\n  return list is list && list.size() <= maxSize;\n}\n//\n// Validate optional string (if present, must be string and within length)\nfunction isValidOptionalString(field, minLen, maxLen) {\n  return !(field in request.resource.data) ||\n         (request.resource.data[field] is string &&\n          request.resource.data[field].size() >= minLen &&\n          request.resource.data[field].size() <= maxLen);\n}\n//\n// Validate that a map contains only allowed keys\nfunction isValidMap(mapData, allowedKeys) {\n  return mapData is map && mapData.keys().hasOnly(allowedKeys);\n}\n//\n// Validate that the document contains only the allowed fields\nfunction hasOnlyAllowedFields(fields) {\n  return request.resource.data.keys().hasOnly(fields);\n}\n//\n// Validate that the document hasn't changed in the fields that are not allowed to be changed\nfunction areImmutableFieldsUnchanged(fields) {\n  return !request.resource.data.diff(resource.data).affectedKeys().hasAny(fields);\n}\n//\n// Validate that a timestamp is recent (within the last 5 minutes)\nfunction isRecent(time) {\n  return time is timestamp &&\n         time > request.time - duration.value(5, 'm') &&\n         time <= request.time;\n}\n//\n// [Add more helper functions as needed for the data validation like the example below]\n//\n// ===============================================================\n//\n// Domain Validators (CRITICAL: Use these in both create and update)\n//\n// function isValidUser(data) {\n//   // Only allow admin to create admin roles\n//   return hasOnlyAllowedFields(['name', 'email', 'age', 'role']) &&\n//          data.name is string && data.name.size() > 0 && data.name.size() < 50 &&\n//          data.email is string && isValidEmail(data.email) &&\n//          data.age is number && data.age >= 18 &&\n//          data.role in ['admin', 'user', 'guest'];\n// }\n```\n\n#### Mandatory: User Data Separation (The \"No Mixed Content\" Rule)\n\n- Firestore security rules apply to the entire document. You cannot allow users\n  to read the displayName field while hiding the email field in the same\n  document.\n- If a collection (e.g., users) contains ANY PII (email, phone, address, private\n  settings), you MUST strictly limit read access to the document owner only\n  (allow read: if isOwner(userId);).\n- If the application requires public profiles (e.g., showing user names/avatars\n  on posts):\n  - 1. Denormalization (Preferred): Copy the user's public info (name, photoURL)\n       directly onto the resources they create (e.g., store authorName and\n       authorPhoto inside the posts document).\n  - 2. Split Collections: Create a separate users_public collection that\n       contains only non-sensitive data, and keep the sensitive data in a\n       locked-down users_private collection.\n- NEVER write a rule that allows read access to a document containing PII for\n  anyone other than the owner.\n\n#### **CRITICAL** RBAC Guidelines\n\nThis is one of the most important set of instructions to follow. Failing to\nfollow these rules will result in catastrophic security vulnerabilities.\n\n- **NEVER** allow users to create their own privileged roles. That means that no\n  user should be able to create an item in a database with their role set to a\n  role similar to \"admin\" unless they are already a bootstrapped admin.\n- **NEVER** allow users to update their own roles or permissions.\n- **NEVER** allow users to grant themselves access to other users' data.\n- **NEVER** allow users to bypass the role hierarchy.\n- **ALWAYS** validate that the user is authorized to perform the requested\n  action.\n- **ALWAYS** validate that the user is not attempting to escalate their\n  privileges.\n- **ALWAYS** validate that the user is not attempting to access data they do not\n  have permission to access.\n\nHere's a **bad** example of what **NOT** to do:\n\n```javascript\nmatch /users/{userId} {\n  // BAD: Allows users to create their own roles because a user can create a new user document with a role of 'admin' and the isAdmin() function will return true\n  allow create: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin();\n  // BAD: Allows users to update their own roles because a user can update their own user document with a role of 'admin' and the isAdmin() function will return true\n  allow update: if (isOwner(userId) && isValidUser(request.resource.data)) || isAdmin();\n}\n```\n\nHere's a **good** example of what **TO** do:\n\n```javascript\nmatch /users/{userId} {\n  // GOOD: Does NOT allow users to create their own roles unless they are an admin or the user is updating their own role to a less privileged role\n  allow create: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == 'client') || isAdmin());\n  // GOOD: Does NOT allow users to update their own roles unless they are an admin\n  allow update: if isAuthenticated() && isValidUser(request.resource.data) && ((isOwner(userId) && request.resource.data.role == resource.data.role) || isAdmin());\n}\n```\n\n#### Critical Directives for Secure Generation\n\n- **PREFER USING READ OVER LIST OR GET** `list` and `get` can add complexity to\n  security rules. Prefer using `read` over them.\n\n- **Date and Timestamp Validation:**\n\n  - **Prefer Timestamps:** ALWAYS prefer the `timestamp` type for date fields.\n    Firestore automatically ensures they are logically valid dates.\n  - **String Date Risks:** If using strings for dates (e.g., ISO 8601), a regex\n    check like `isValidDateString` only validates **format**, not **logic** (it\n    would accept Feb 31st).\n  - **Regex Escaping:** When using regex for digits, you **MUST** use double\n    backslashes (e.g., `\\\\\\\\d`) in the rules string. Using a single backslash\n    (`\\\\d`) is a common bug that causes validation to fail.\n\n- **Immutable Fields:** Fields like `createdAt`, `authorUID`, or any other field\n  that should not change after creation must be explicitly protected in `update`\n  rules. (e.g., `request.resource.data.createdAt == resource.data.createdAt`).\n  **CRITICAL**: When allowing non-owners to update specific fields (like\n  incrementing a counter), you **MUST** explicitly verify that all other fields\n  (e.g., `authorName`, `tags`, `body`) remain unchanged to prevent unauthorized\n  metadata modification. For sensitive fields, ensure that the logged in user is\n  also the owner of the document.\n\n- **Identity Integrity:** When storing denormalized user identity (e.g.\n  `authorName`, `authorPhoto`), you **MUST** validate this data.\n\n  - **Prefer Auth Token:** If possible, check if\n    `request.resource.data.authorName == request.auth.token.name`.\n  - **Strict Validation:** If the auth token is unavailable, you **MUST**\n    strictly validate the type (string) and length (e.g. < 50 chars) to prevent\n    spoofing with massive or malicious payloads.\n  - **Client-Side Fetching:** The most secure pattern is to store ONLY\n    `authorUid` and fetch the profile client-side. If you denormalize, you\n    accept the risk of stale or spoofed data unless you validate it.\n\n- **Enforce Strict Schema (No Extraneous Fields):** Documents must not contain\n  any fields other than those explicitly defined in the data model. This\n  prevents users from adding arbitrary data.\n\n- **NEVER allow PII EXPOSURE LEAKS:** Never allow PII (Personally Identifiable\n  Information) to be exposed in the data model. This includes email addresses,\n  phone numbers, and any other information that could be used to identify a\n  user. For example, even if a user is logged-in, they should not have access to\n  read another user's information.\n\n- **No Blanket User Read Access:** You are strictly FORBIDDEN from generating\n  `allow read: if isAuthenticated();` for the users collection if that\n  collection is defined to contain email addresses or other private data.\n\n- **CRITICAL: Double-Check Blanket `isAuthenticated` fields:** Ensure that paths\n  that are protected with only `isAuthenticated()` do not need any additional\n  checks based on role or any other condition.\n\n- **The \"Ownership-Only Update\" Trap:** A common critical vulnerability is\n  allowing updates based solely on ownership (e.g.,\n  `allow update: if isOwner(resource.data.uid);`). This allows the owner to\n  corrupt the data schema, delete required fields, or inject malicious payloads.\n  You **MUST** always combine ownership checks with data validation (e.g.,\n  `allow update: if isOwner(...) && isValidEntity(...);`) **AND** validate that\n  self-escalation is not possible.\n\n- **Deep Array Inspection:** It is insufficient to check if a field `is list`.\n  You **MUST** validate the contents of the array (e.g., ensuring all elements\n  are strings of a valid UID length) to prevent data corruption or schema\n  pollution. For example, a `tags` array must verify that every item is a string\n  AND that each string is within a reasonable length (e.g., < 20 chars).\n\n- **Permission-Field Lockdown:** Fields that control access (e.g., `editors`,\n  `viewers`, `roles`, `role`, `ownerId`) **MUST** be immutable for non-owner\n  editors. In `update` rules, use `areImmutableFieldsUnchanged()` for these\n  fields unless the `request.auth.uid` matches the document's original\n  owner/creator. This prevents \"Permission Escalation\" where a collaborator\n  could grant themselves higher privileges or remove the owner.\n\n### Advanced Validation for Business Logic\n\nSecure rules must enforce the application's business logic. This includes\nvalidating field values against a list of allowed options and controlling how\nand when fields can change.\n\n#### 1. Enforce Enum Values\n\nIf a field should only contain specific values (e.g., a status), validate\nagainst a list.\n\n**Example:**\n\n```javascript\n // A 'task' document's status can only be one of three values\n function isValidStatus() {\n   let validStatuses = ['pending', 'in-progress', 'completed'];\n   return request.resource.data.status in validStatuses;\n }\n\n allow create: if isValidStatus() && ...\n```\n\n#### 2. Validate State Transitions\n\nFor `update` operations, you **MUST** validate that a field is changing from a\nvalid previous state to a valid new state. This prevents users from bypassing\nworkflows (e.g., marking a task as 'completed' from 'archived').\n\n**Example:**\n\n```javascript\n // A task can only be marked 'completed' if it was 'in-progress'\n function validStatusTransition() {\n   let previousStatus = resource.data.status;\n   let newStatus = request.resource.data.status;\n\n   return (previousStatus == 'in-progress' && newStatus == 'completed') ||\n          (previousStatus == 'pending' && newStatus == 'in-progress');\n }\n\n allow update: if validStatusTransition() && ...\n```\n\n#### 3. Strict Path and Relationship Scoping\n\nFor any field that references another resource (like an image path or a parent\ndocument ID), you **MUST** ensure it is correctly scoped to the user or valid\nwithin the context.\n\n**Example:**\n\n```javascript\n// Ensure image path is within the user's own storage folder\nallow create: if isScopedPath(request.resource.data.imageBucket) && ...\n```\n\n#### 4. Secure Counter Updates\n\nWhen allowing users to update a counter (like `voteCount` or `answerCount`), you\n**MUST** ensure: 1. **Atomic Increments:** The field is only changing by exactly\n+1 or -1. 2. **Isolation:** **NO OTHER FIELDS** are being modified. This is\ncritical to prevent attackers from hijacking the `authorName` or `content` while\n\"voting\". 3. **Action Verification:** You **MUST** prevent users from\nartificially inflating counts. When incrementing a counter, verify that the user\nhas not already performed the action (e.g., by checking for the existence of a\n'like' document) and is not looping updates. * **CRITICAL:** Relying solely on\n`!exists(likeDoc)` is insufficient because a malicious user can skip creating\nthe document and loop the increment. * **SOLUTION:** Use `getAfter()` to verify\nthat the corresponding tracking document *will exist* after the batch completes.\n\n**Example:**\n\n```javascript\nfunction isValidCounterUpdate(docId) {\n  // Allow update only if 'voteCount' is the ONLY field changing\n  return request.resource.data.diff(resource.data).affectedKeys().hasOnly(['voteCount']) &&\n         // And the change is exactly +1 or -1\n         math.abs(request.resource.data.voteCount - resource.data.voteCount) == 1 &&\n         // Verify consistency:\n         (\n           // Increment: Vote must NOT exist before, but MUST exist after\n           (request.resource.data.voteCount > resource.data.voteCount &&\n            !exists(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) &&\n            getAfter(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) != null) ||\n           // Decrement: Vote MUST exist before, but must NOT exist after\n           (request.resource.data.voteCount < resource.data.voteCount &&\n            exists(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) &&\n            getAfter(/databases/$(database)/documents/votes/$(request.auth.uid + '_' + docId)) == null)\n         );\n}\n\nallow update: if isValidCounterUpdate(docId) && ...\n```\n\n#### 5. **CRITICAL** Ensure Application Validity\n\nWhile updating the firestore rules, also ensure that the application still works\nafter firestore rules updates.\n\n1. **For each collection, implement explicit data validation:**\n\n- Type Checking: 'field is string', 'field is number', 'field is bool', 'field\n  is timestamp'\n- Required fields validation using 'hasRequiredFields()'\n- **Enforce Size Limits:** For **EVERY** string, list, and map field, you\n  **MUST** enforce realistic size limits (e.g., `text.size() < 1000`,\n  `tags.size() < 20`). **Failure to limit a single string field (like `caption`\n  or `bio`) allows 1MB attacks, which is a CRITICAL vulnerability.**\n- URL validation using 'isValidUrl()' for URL fields\n- Email validation using 'isValidEmail()' for email fields\n- **Immutable field protection** (authorId, createdAt, etc. should not change on\n  update)\n- **UID protection** using 'uidUnchanged()' on creates and 'uidNotModified()' on\n  updates should be accompanied with `isDocOwner()`\n- **Temporal accuracy** using `isRecent()` for timestamps.\n- **Range validation** using `isPositive()` or similar for numbers.\n- **Path scoping** using `isScopedPath()` for storage paths.\n\nStructure your rules clearly with comments explaining each rule's purpose.\n\n#### Phase-3: Devil's Advocate Attack\n\n**Critical step:** Systematically attempt to break your own rules using the\nfollowing attack vectors. You MUST document the outcome of each attempt.\n\n1. **Public List Exploit:** Can I run a collection query without authentication\n   and retrieve documents that should be private (e.g., where\n   `visible == false`)?\n1. **Unauthorized Read/Write:** Can I `get`, `create`, `update`, or `delete` a\n   document that I do not own or have permissions for?\n1. **The \"Update Bypass\":** Can I `create` a valid document and then `update` it\n   with a 1MB string or invalid fields? (Tests if validation logic is missing\n   from `update`).\n1. **Ownership Hijacking (Create):** Can I create a document and set the\n   `authorUID` or `ownerId` to another user's ID?\n1. **Ownership Hijacking (Update):** Can I `update` an existing document to\n   change its `authorUID` or `ownerId`?\n1. **Immutable Field Modification:** Can I change a `createdAt` or other\n   immutable timestamp or property on an `update`?\n1. **Data Corruption (Type Juggling):** Can I write a `number` to a field that\n   should be a `string`, or a `string` to a `timestamp`?\n1. **Validation Bypass (Create vs. Update):** Can I `create` a valid document\n   and then `update` it into an invalid state (e.g., remove a required field,\n   write a string that's too long)?\n1. **Resource Exhaustion / DoS:** Can I write an enormous string (e.g., 1MB) to\n   any field that accepts a string or a massive array to a list field? Every\n   string field (e.g., `bio`, `url`, `name`) MUST have a `.size()` check. If any\n   are missing, it's a \"Resource Exhaustion/DoS\" risk.\n1. **Required Field Omission:** Can I `create` or `update` a document while\n   omitting fields that are marked as required in the data model?\n1. **Privilege Escalation:** Can I create an account and assign myself an admin\n   role by writing `isAdmin: true` to my user profile document? (Tests reliance\n   on document data vs. custom claims).\n1. **Schema Pollution:** Can I `create` or `update` a document and add an\n   arbitrary, undefined field like `extraData: 'malicious_code'`? (Tests for\n   strict schema enforcement).\n1. **Invalid State Transition:** Can I update a document's `status` field from\n   `'pending'` directly to `'completed'`, bypassing the required `'in-progress'`\n   state? (Tests business logic enforcement).\n1. **Path Traversal / Scoping Attack:** Can I set a path field (like\n   `imageBucket` or `profilePic`) to a value that points to another user's data\n   or a restricted area? (Tests for regex path scoping).\n1. **Timestamp Manipulation:** Can I set a `createdAt` field to the past or\n   future to bypass sorting or logic? (Tests for `request.time` validation).\n1. **Negative Value / Overflow:** Can I set a numeric field (like `price` or\n   `quantity`) to a negative number or an extremely large one? (Tests for range\n   validation).\n1. **The \"Mixed Content\" Leak:** Create a second user. Can User B read User A's\n   users document? If \"Yes\" (because you wanted public profiles), does that\n   document also contain User A's email or private keys? If both are true, the\n   rules are insecure.\n1. **Counter/Action Replay:** If there is a counter (like `likesCount`), can I\n   increment it without creating the corresponding tracking document (e.g.,\n   inside `likes/{userId}`)? Can I increment it twice? (Tests for `getAfter()`\n   consistency checks).\n1. **Orphaned Subcollection Access:** Can I read/write to a subcollection (e.g.,\n   `users/123/posts/456`) if the parent document (`users/123`) does not exist?\n   (Tests for parent existence checks).\n1. **Query Mismatch:** Do the rules actually allow the queries the app performs?\n   (e.g., if the app filters by `status == 'published'`, do the rules allow\n   `list` only when `resource.data.status == 'published'`?)\n1. **Validator Pattern Check:** Do **ALL** `update` rules (including owner-only\n   ones) call the `isValidX()` function? If an `allow update` rule only checks\n   `isOwner()`, it is a CRITICAL vulnerability.\n\nEvaluate each attack attempt mentally (do not create separate untracked\nattack-log files). If ANY attack succeeds:\n\n- Fix the security hole\n- Regenerate the rules\n- **Repeat Phase-3** until no attacks succeed\n\n#### Phase-4: Syntactic Validation\n\nOnce devil's advocate testing passes, repeat until rules pass validation.\n\n**After all phases are complete, create or update the `firestore.rules` file.**\n\n### Critical Constraints\n\n1. **Never skip the devil's advocate phase** - this is your primary security\n   validation\n1. **MUST include helper functions** for common operations ('isAuthenticated',\n   'isOwner', 'uidUnchanged', 'uidNotModified') AND domain validators\n   ('isValidUser', etc.)\n1. **MUST document assumed data models** at the beginning of the rules file\n1. **Always validate the rules syntax** using 'firebase deploy --only\n   firestore:rules --dry-run' or a similar tool before outputting the final\n   file.\n1. **Provide complete, runnable code** - no placeholders or TODOs\n1. **Document all assumptions** about data structure or access patterns\n1. **Always run the devil's advocate attack** after any modification of the\n   rules.\n1. **Determine whether the rules need to be updated** after permission denied\n   errors occur.\n1. **Do not make overly confident guarantees of the security of rules that you\n   have generated**. It is very difficult to exhaustively guarantee that there\n   are no vulnerabilities in a rules set, and it is vital to not mislead users\n   into thinking that their rules are perfect. After an initial rules\n   generation, you should describe the rules you've written as a solid\n   prototype, and tell users that before they launch their app to a large\n   audience, they should work with you to harden and validate the rules file. Be\n   clear that users should carefully review rules to ensure security.\n"
}

SHA-256 of public snapshot: 97ca15fe346c282a4d1c0e08e443aab554f0fbb18250e585020bb8a6ef4b8704