Skip to content

Repository files navigation

HotRulez logo

HotRulez

First-class Firebase Security Rules support for JetBrains IDEs — Cloud Firestore and Cloud Storage.

Syntax highlighting, formatting, diagnostics, and symbol intelligence for .rules files —
so the IDE treats them as their own language instead of JavaScript, JSON, or plain text.

CI JetBrains Marketplace version JetBrains Marketplace downloads License: MIT Latest release Buy Me a Coffee

WhyWhat It DoesCompareInstallContributingSpec


Why HotRulez?

Firebase Security Rules — for both Cloud Firestore and Cloud Storage — are a real language with their own grammar, scoping, and built-ins, but no JetBrains IDE knows that. Open a .rules file and it is treated as JavaScript, JSON, or plain text: no highlighting that understands match and allow, no rules-aware formatting, no go-to-definition for your helper functions, and no warning when a rule can never parse.

There is currently no freely available solution that fills this gap. HotRulez exists to fill that hole: a free, open-source plugin that makes Firebase Rules a first-class language in the IDE — entirely structural and static, never evaluating authorization or connecting to Firebase.

It is released under the MIT License and created by lezli01 at lezli01.is-a.dev. Contributions are welcome — see Contributing.

What It Does

Open any .rules file and the IDE treats it as a first-class Firebase Rules language:

  • File recognition — every *.rules file is recognized as Firebase Rules (by extension) and shown with a dedicated file icon.
  • Syntax highlighting — keywords, allow operations, built-ins, helpers, types, service names, function declarations and calls, path variables, recursive wildcards, strings, numbers, comments, operators, and invalid tokens, all separately colorable.
  • Formatting — one reformat turns compact or messy rules into clean, consistently indented output while preserving your comments, blank lines, and multiline conditions.
  • Diagnostics & quick-fixes — checks flag always-wrong constructs as errors and surface suspicious-but-legal structure, unresolved/unused symbols, and unknown built-in members as configurable warnings; most come with a one-keystroke fix (Alt+Enter), and the wording stays structural (it never claims a rule is "secure").
  • Symbol intelligence — go to declaration, find usages, rename refactoring, and scope-aware code completion for functions, parameters, let bindings, and path variables, all built on a PSI resolve layer that honors Firebase Rules scoping and path-variable shadowing.
  • Authoring aids — a structure-view outline of services, matches, functions, and allow rules; code folding for block and comment regions; quick documentation (Ctrl+Q) with doc-grounded prose and an external Firebase link; and parameter info (Ctrl+P) for function and path-helper calls.
  • Editor conveniences — brace matching, quote auto-closing, and line/block comment toggling.

Everything is structural and static. The plugin never evaluates authorization or talks to Firebase; keep using Firebase's official tooling for deployment and authorization testing.

Syntax Highlighting

The highlighter exposes 24 categories, including semantic ones — service name, function declaration, and path variable — that a fast lexer alone cannot distinguish and that are resolved by a lightweight annotator. Every category is customizable with a live preview under Settings | Editor | Color Scheme | Firebase Rules.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Signed-in owners can read and write their own city
    function isOwner(uid) {
      return request.auth != null && resource.data.owner == uid;
    }

    match /cities/{city} {
      allow read, write: if isOwner(request.auth.uid);
    }

    match /scores/{score} {
      allow read: if resource.data.value is int
        ? resource.data.value >= 100
        : resource.data.tier in ['gold', 'silver'];
    }

    match /logs/{document=**} {
      allow read: if false;
    }
  }
}

Formatting

Invoke the IDE's normal Reformat Code action on a .rules file. The formatter uses IntelliJ's formatting APIs over the parsed PSI, so it makes minimal whitespace changes — it never reorders rules or rewrites your expressions.

Expanding a compact file. A whole document collapsed onto one line is expanded and indented, with a blank line inserted between block members such as a function declaration:

Before:

rules_version='2';service cloud.firestore{match /databases/{database}/documents{match /cities/{city}{allow read,write: if request.auth!=null;function ownsCity(uid){return resource.data.owner==uid;}}}}

After:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read, write: if request.auth != null;

      function ownsCity(uid) {
        return resource.data.owner == uid;
      }
    }
  }
}

Preserving multiline conditions. A condition you intentionally split across lines stays split — the formatter indents the surrounding blocks without collapsing the && continuation onto one line:

Before:

service cloud.firestore{match /databases/{database}/documents{allow read: if request.auth != null
&& request.auth.uid == resource.data.owner;}}

After:

service cloud.firestore {
  match /databases/{database}/documents {
    allow read: if request.auth != null
    && request.auth.uid == resource.data.owner;
  }
}

Keeping comments in place. Both line (//) and block (/* */) comments are preserved and reindented to align with the block they precede:

Before:

service cloud.firestore{/* service body */match /databases/{database}/documents{// city access
match /cities/{city}{allow read: if true;}}}

After:

service cloud.firestore {
  /* service body */
  match /databases/{database}/documents {
    // city access
    match /cities/{city} {
      allow read: if true;
    }
  }
}

Path variables and recursive wildcards (/cities/{city}/{document=**}) keep their tight spacing, and malformed-but-recoverable input is formatted block by block rather than left untouched.

Diagnostics

Severity decides the home. Always-wrong, grammar-inexpressible mistakes are reported as errors by an always-on annotator. Configurable warnings for file shape, suspicious usage, unresolved/unused symbols, and unknown built-in members live in four inspections you can tune under Settings | Editor | Inspections | Firebase Rules. Most diagnostics — errors and warnings alike — carry an Alt+Enter quick-fix. None of the wording asserts that a rule is secure or that a request would be authorized.

Errors (always reported)

  • Empty operation list'allow' requires at least one operation, for example 'allow read'.
  • Unknown operationallow read, fetch: …Unknown Firebase Rules operation 'fetch'. Expected one of: get, list, read, create, update, delete, write.
  • Duplicate parameter namefunction f(uid, uid)Duplicate parameter name 'uid'.
  • Function missing its returnFunction 'helper' must end with a 'return' statement.
  • Bare returnreturn;'return' requires an expression.
  • Too many recursive wildcardsA match path may contain at most one recursive wildcard '{name=**}'.
  • Misplaced recursive wildcard (v1)In rules_version '1', a recursive wildcard '{name=**}' must be the last segment of a match path.

For example, the unknown operation here is reported on the fetch token:

match /cities/{city} {
  allow read, fetch: if true;   // error: Unknown Firebase Rules operation 'fetch'.
}

File-structure warnings

  • Missing rules versionMissing 'rules_version = '2';' declaration at the top of the file.
  • Non-v2 rules versionrules_version = '1';Expected 'rules_version = '2';' declaration at the top of the file; found version '1'.
  • Version after service'rules_version' must be declared before the 'service' block.
  • Missing serviceMissing 'service cloud.firestore { ... }' or 'service firebase.storage { ... }' block.
  • Unknown service name — both cloud.firestore and firebase.storage are recognized; an unrecognized service such as service firebase.database { … } is a soft warning (Expected 'service cloud.firestore' or 'service firebase.storage'; found 'service firebase.database'.) and the service-specific checks below are skipped.
  • Service missing its block'service cloud.firestore' is missing its rule block '{ ... }'.
  • Missing root match — the conventional root for the detected service: Missing root 'match /databases/{database}/documents' block inside 'service cloud.firestore'. (Firestore) or Missing root 'match /b/{bucket}/o' block inside 'service firebase.storage'. (Cloud Storage).
  • Multiple servicesA rules file may declare only one 'service'; Firebase rejects multiple 'service' blocks per file. (Firestore and Cloud Storage rules belong in separate files.)

Usage warnings

  • Condition-less allowallow read;'allow' rule has no 'if' condition. Add ': if <condition>' to restrict when the operation is permitted. A condition-less allow is legal (it unconditionally grants the operation), so this is an opt-in nudge rather than an error.
  • Recursive wildcard without v2Recursive wildcard '{document=**}' should be used with rules_version = '2'; its match semantics differ between rules versions.
  • Helper-call arityexists()'exists()' takes exactly one argument but found 0. Applies to get, getAfter, exists, and existsAfter.

Symbol warnings

A resolver-based inspection — the same scope resolution that powers go-to-definition and rename — flags symbols that don't line up, without ever evaluating authorization:

  • Unresolved reference — a name resolving to no function, parameter, let, or path variable that also isn't a built-in → Cannot resolve symbol 'isOwner'. Members (request.auth), path variables, and built-ins/helpers are never flagged.
  • Unused function / let — a declaration nothing references → Function 'isOwner' is never used. / Variable 'tmp' is never used.
  • Unused parameter — a parameter never read in its body → Parameter 'uid' is never used. (reported only; removing it would change every call site).

Member warnings

A conservative, service-aware semantic check — the plugin's first that reasons about whether a member can exist on a receiver, not just whether a symbol resolves — reported as a weak warning (on by default):

  • Unknown member on a closed built-in — accessing a member that a dialect's fixed, doc-sourced set does not define on request, resource, request.auth, request.resource, or (Firestore) request.query'foo' is not a member of 'request'. A close typo offers a Change to 'X' rename (request.resourseresource); a member that belongs to the other service gets a dialect-aware message instead — resource.size in a Firestore file ('size' is a Cloud Storage member; …), request.method in a Storage file.
  • It never fires on an open namespace — user document data (resource.data.*), custom auth claims (request.auth.token.*, including the MFA/SAML .firebase.* members), Cloud Storage object metadata (resource.metadata.*), or request params (request.params.*) — nor on a neutral (no-service) file, nor on a variable that merely shares a built-in's name. It infers no types.

Quick-fixes

Most diagnostics offer an Alt+Enter fix that edits the file for you:

  • Insert or correct rules_version = '2';, or move it above the service block.
  • Change an unknown service to cloud.firestore or firebase.storage; scaffold a missing service block or root match.
  • Replace an unknown operation with the closest real one, remove a duplicate parameter, or add a missing return false;.
  • Add a placeholder : if <condition> to a condition-less allow.
  • Create a missing function for an unresolved call, or remove an unused function or let.
  • Rename an unknown member to a near match on its receiver (Change to 'resource').

The "no automatic fix" cases are deliberate: an empty operation list, a bare return;, multiple service blocks, and helper-call arity have no unambiguous repair, so they report without one.

Symbol Intelligence

.rules files get the symbol features you expect from a first-class language, all built on a PSI reference/resolve layer that links a use of a name to its declaration using Firestore's real scoping rules — never by evaluating authorization or connecting to Firebase.

Covered symbols: functions, function parameters, let bindings, and path / wildcard variables ({city}, {document=**}).

  • Go to declaration — Ctrl+click (or Go to Declaration) on a function call jumps to its function, on a path-variable use to its binding wildcard, and on a parameter or let use to its binding. Calls resolve across service / match scope, including forward references and a call to a function declared in an enclosing scope.
  • Find usages — from any function, parameter, let, or path variable, with scoping respected: a path variable's usages stop at a nested match that re-declares the same name.
  • Rename — renaming a function updates its declaration and every call site; renaming a parameter or let updates its in-body uses; renaming a path variable updates only the correct match subtree and leaves a shadowing inner binding (and its uses) untouched. New names are validated as legal identifiers, and reserved keywords are rejected.
  • Code completion — context aware:
    • operation names after allow (get, list, read, create, update, delete, write);
    • structural keywords (match, allow, function, return, let, if), with rules_version and service at the top level;
    • cloud.firestore and firebase.storage after service ;
    • in expression position, the symbols visible at the caret — scope-aware, so parameters are offered only inside their function, path variables only within their subtree, and a let only after its declaration — plus the built-ins request and resource, the literals true / false / null, and the detected service's path helpers (Cloud Firestore's get / exists / getAfter / existsAfter, or Cloud Storage's cross-service firestore.get / firestore.exists);
    • shallow members after request., request.auth., request.resource., and resource., drawn from a fixed, service-aware table built from the official Firebase docs — Firestore offers resource.data / id / __name__, while Cloud Storage offers the object metadata (resource.size, resource.contentType, resource.metadata, …).

Built-in variables and helpers are recognized (so they are never mistaken for undefined names) but non-navigable — there is no declaration to jump to. Member completion is a fixed, documented list, not type inference: the plugin never infers the type of an expression or invents custom request.auth.token claims.

Structure, Folding & Documentation

Beyond editing, HotRulez helps you read and navigate a .rules file the way a first-class language plugin does — all read-only projections of the parsed structure, adding no new semantics.

  • Structure view — the Structure tool window (Alt+7) and the navigation bar show a service → match → function / allow outline, labeled with the service name, each match path, fn(params), and allow read, write. It offers alphabetical sorting (source order by default) and "Select in Structure View" caret sync, and degrades to a partial tree on a half-typed file. let/return statements and path variables are intentionally not surfaced.
  • Code folding — service, match, and function braced bodies fold to {…} with the head (service name / match path / function signature) staying visible, and multi-line block comments fold to /*…*/. Nothing is collapsed by default, and an unclosed or empty block never produces a bogus fold.
  • Quick documentation — hover or press Ctrl+Q on an allow operation, a built-in (request, resource, firestore), a request./resource. member, a path helper (get/exists/getAfter/existsAfter, or Storage's firestore.get/firestore.exists), or a type/namespace to see a short, doc-grounded summary plus an "open in browser" link to the official Firebase reference. Your own functions show their signature plus the comment above them; parameters, let bindings, and path variables show what they bind. Member docs are dialect-aware (a Storage-only member shows nothing in a Firestore file) and never fabricated — an unknown member or unresolved name shows nothing.
  • Parameter info — press Ctrl+P inside a call to see the signature and the highlighted current argument for your own functions and for the fixed-arity path helpers. It is a display aid only: it never validates argument count.

Editor Conveniences

  • Brace matching for {}, (), and []. Curly braces are structural, so the IDE highlights the matching } for a service, match, or function block and the Move Caret to Matching Brace action jumps between them.
  • Quote handling — typing ' or " auto-inserts the closing quote and types over an existing one.
  • Comment togglingComment with Line Comment inserts //, and Comment with Block Comment wraps a selection in /* */.

How It Compares

HotRulez is not the only Firebase-related plugin on the JetBrains Marketplace, but it is the only free, open-source one that treats the Firebase Security Rules language as a first-class citizen. The table below compares the plugins that touch the .rules DSL (Cloud Firestore and Cloud Storage), on rules-authoring features only. For the full breakdown, sources, and caveats, see docs/comparism.md.

HotRulez Firebase Rules Firebase Pro
Price Free (MIT) Paid Paid
Open source
Handles the .rules language
Syntax highlighting & formatting
Structure view
Scope-aware completion (symbols + members)
Go to declaration / find usages 🟡
Rename refactoring
Diagnostics + quick-fixes 🟡
Quick documentation (Ctrl+Q)
Parameter info (Ctrl+P)
Cloud Storage rules 🟡

✅ supported • 🟡 partial / basic • ❔ not documented on the listing (may still be present) • ❌ not supported.

Current Limitations

  • No authorization evaluation. The plugin never decides whether a request is allowed or denied. All diagnostics are structural and syntactic.
  • No Firebase integration. It does not connect to Firebase projects, emulators, credentials, or live Firestore data, and it hard-codes no project IDs.
  • No type inference. Member completion after request./resource. is a fixed, documented list, not the result of evaluating an expression's type. The resolver links names to declarations; it never asserts that a rule is correct or secure.
  • Firestore and Cloud Storage. Both service cloud.firestore and service firebase.storage are supported; the dialect (service name, root-match shape, and request/resource members) is detected from the file's service declaration. Other services (e.g. firebase.database, whose rules are JSON) are not modeled.
  • Conservative diagnostics. The grammar is intentionally permissive so the IDE keeps working while you edit. Constructs not confirmed by official Firebase docs are parsed without being flagged.
  • Highlighting is approximate. Highlighting uses a fast, context-sensitive lexer that is separate from the parser. For example, / is always highlighted as a path separator (it shares the operator color), and an unterminated string is highlighted to the end of its line; the parser remains the source of truth for precise errors.
  • One fixed formatting style. There is no custom code-style settings UI, and the formatter applies no column alignment.

Example Cloud Firestore Rules File

A complete file that exercises most of what the plugin understands:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    function isSignedIn() {
      return request.auth != null;
    }

    match /cities/{city} {
      allow read, write: if isSignedIn() && resource.data.owner == request.auth.uid;

      match /landmarks/{landmark} {
        allow read: if isSignedIn();
        allow create: if isSignedIn()
          && request.resource.data.name is string;
      }
    }

    match /logs/{document=**} {
      allow read: if false;
    }
  }
}

Example Cloud Storage Rules File

The same features work on a service firebase.storage file — the dialect is detected from the service declaration — with the Cloud Storage root match (/b/{bucket}/o) and object-metadata members (request.resource.size, request.resource.contentType):

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    function isSignedIn() {
      return request.auth != null;
    }

    match /users/{userId}/{allPaths=**} {
      allow read: if isSignedIn() && request.auth.uid == userId;
      allow write: if isSignedIn()
        && request.auth.uid == userId
        && request.resource.size < 5 * 1024 * 1024
        && request.resource.contentType.matches('image/.*');
    }
  }
}

How It Is Built

The project is a Kotlin-based IntelliJ Platform plugin built with Gradle Kotlin DSL and the IntelliJ Platform Gradle Plugin 2.x.

Important parts of the repository:

build.gradle.kts
settings.gradle.kts
src/main/grammar/                  # FirebaseRules.bnf + FirebaseRules.flex
src/main/kotlin/dev/lezli/hotrulez/
  FirebaseRulesLanguage.kt
  FirebaseRulesFileType.kt
  FirebaseRulesIcons.kt
  lexer/
  highlighting/
  parser/
  psi/
  formatting/
  diagnostics/                      # annotator, inspections, quick-fixes (fixes/)
  editor/
  references/                       # PSI named elements, resolver, reference
  findusages/                       # FindUsagesProvider + word scanner
  refactoring/                      # names validator + rename processor
  completion/                       # CompletionContributor
  structureview/                    # Structure tool-window outline
  folding/                          # FoldingBuilder
  documentation/                    # quick-docs provider + doc-prose table
  parameterinfo/                    # ParameterInfoHandler
src/main/resources/icons/
src/main/resources/META-INF/plugin.xml
src/test/kotlin/dev/lezli/hotrulez/
src/test/testData/formatter/
docs/spec.md
docs/tasks.md

The implementation is split by responsibility:

  • lexer/ tokenizes Firebase Rules syntax for highlighting.
  • highlighting/ maps tokens to IDE text attributes and exposes the color page.
  • parser/ wires the generated parser/PSI into a recoverable ParserDefinition.
  • psi/ defines the Firebase Rules PSI file and wrapper elements.
  • formatting/ provides IntelliJ formatter blocks and spacing rules.
  • diagnostics/ provides the annotator and inspections, with quick-fixes in diagnostics/fixes/.
  • editor/ provides the brace matcher, quote handler, and commenter.
  • references/ makes declarations named elements and resolves a name use to its declaration with Firestore scoping (the layer go-to-definition, find-usages, and rename all ride on).
  • findusages/ exposes declarations to Find Usages via a word scanner.
  • refactoring/ validates rename input and scopes path-variable rename.
  • completion/ provides scope-aware, doc-sourced code completion.
  • structureview/ provides the Structure tool-window outline.
  • folding/ provides code folding for braced blocks and block comments.
  • documentation/ provides quick documentation and the shared doc-prose table.
  • parameterinfo/ provides call parameter-info hints.
  • plugin.xml registers every extension point above.

The parser, typed PSI, and parsing lexer are generated by Grammar-Kit and JFlex from src/main/grammar/ into build/generated/sources/grammarkit (not committed); generation runs automatically before compilation. The grammar is deliberately permissive and recovery-oriented, so a malformed statement is isolated to its own declaration instead of breaking the rest of the file. A second, hand-written lexer (lexer/FirebaseRulesLexer.kt) is used only for highlighting.

Requirements

  • JDK 21.
  • The included Gradle wrapper.
  • A JetBrains IDE compatible with build 252 or newer.

The Gradle project currently targets IntelliJ IDEA 2025.2.6.2 for plugin development and tests.

Build And Test

Run the test suite:

./gradlew test

Build the installable plugin ZIP:

./gradlew buildPlugin

The plugin ZIP is written under:

build/distributions/

For CI-style runs, this repository uses:

./gradlew test --no-daemon
./gradlew buildPlugin --no-daemon

How To Use

Install from the JetBrains Marketplace (recommended)

Install from JetBrains Marketplace

From inside your IDE:

  1. Open Settings / PreferencesPlugins.
  2. Select the Marketplace tab.
  3. Search for Firebase Rules (or HotRulez) and click Install.
  4. Restart the IDE if prompted.

Or open the plugin page and use its Install button.

Install from disk (specific version or offline)

Prefer to pin a version or work offline? Download the prebuilt ZIP attached to a GitHub release, or build it yourself:

./gradlew buildPlugin   # writes the ZIP to build/distributions/

Then in a JetBrains IDE:

  1. Open Settings or Preferences.
  2. Go to Plugins.
  3. Use the gear menu and choose Install Plugin from Disk....
  4. Select the ZIP from build/distributions/.
  5. Restart the IDE if prompted.

After installation:

  • Open a .rules file to get Firebase Rules file recognition and highlighting.
  • Use the IDE's normal reformat action to format Firebase Rules files.
  • Keep using Firebase's official tooling for deployment and authorization behavior checks.

Project Status

HotRulez covers file recognition, highlighting, structural parsing, automatic formatting, diagnostics, symbol intelligence (go-to-definition, find usages, rename, and code completion), authoring aids (structure view, code folding, quick documentation, and parameter info), and editor conveniences (brace, quote, and comment handling). It performs no runtime authorization evaluation by design.

See docs/spec.md for the product and implementation spec, and docs/tasks.md for the task checklist.

Contributing

Contributions of every size are welcome — bug reports, docs, new diagnostics, test cases, and features. Start here:

  • Read the Contributing guide for development setup, build and test commands, and the commit-message convention.
  • Be a good neighbor: this project follows a Code of Conduct.
  • Have a question or an idea? Open a Discussion.
  • Found a bug or want a feature? Open an issue.

Releases are automated with release-please, so pull requests use Conventional Commits titles and are squash-merged. Details are in CONTRIBUTING.md.

Security

HotRulez is purely structural and never connects to Firebase, so its attack surface is small — but security reports are taken seriously. Please report vulnerabilities privately via GitHub's security advisories rather than a public issue. See SECURITY.md for details.

License

HotRulez is released under the MIT License. © 2026 lezli01.

About

JetBrains IDE plugin that treats Firebase Security Rules (.rules) for Cloud Firestore and Cloud Storage as a first-class language: syntax highlighting, formatting, structural diagnostics, and symbol intelligence.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages