# Tips for writing Security Rules

Tip 01

If the type is defined in the schema (bolt_validation_type: "ChildType[]"), then it can and should not be defined via path /path/to/child

Tip 02

read/write rules can also be defined in schema.bolt. In some cases, this might even be necessary! (see sequence)

Tip 03

Path in /path/xx as MyType[] should never end with an {id}!

Tip 04

Be careful when using white spaces. Tabs in .bolt files will cause errors and are very hard to find.

Tip 05

Nested read/write rules are connected via "OR" You cannot grant access to data, then revoke it later!

Tip 06

No read()/write() = false

Tip 07

read() { false } = read() {} = no read (same for write)

Tip 08

write = create + update + delete

Tip 09

Create overwrite write when write is false and create is true (?)

Tip 10

Multiple update() are not allowed, use || instead.

Tip 11

Validation rules do not cascade, so all relevant validation rules must evaluate to true in order for the write to be allowed.

Tip 12

Object types are required to have at least one property when present.

Tip 13

Map types can be empty collections (they don't have to contain any child keys).

Tip 14

First letters of type definitions must be uppercase and should be singular.

Tip 15

index() is the index for props of EVERY child, not the node itself!

path /x { index() ['prop'] }

means /x/1/prop will get indexed

Tip 16

When using functions in: this is not available in functions and must be passed to the function as a parameter

Tip 17

If queries are used, the child-rules won't work! You have to define query.* rules.

Tip 18

If the bolt compiler complains about duplicate keys ("$taskId" and "$key18"), the reason is probably that 'path /path/task_meta is TaskMeta[]' is used in combination with 'path /path/task_meta/{idTaskMeta}/sub is SubItem[]', which is currently not possible. TaskMeta type needs to be converted to something like 'sub: { bolt_validation_type: "SubItem[]" }'.

Tip 19

path /a/b/c is Type[] { write, update, delete, create }

is most likely wrong, you probably want to do

path /a/b/c { path /{id} is Type { write, update, ... } }

Tip 20

This is most likely wrong:

  path /user/{uid}/notifications is Notification[] {
    read() { isUser(uid) }
    create() { false }
    update() { false }
    delete() { isUser(uid) }
  }

correct:

  path /user/{uid}/notifications {
    read() { isUser(uid) }
    path /{notificationId} is Notification {
      create() { false }
      update() { false }
      delete() { isUser(uid) }
    }
  }
"outgoing_invitations": {
  "$invitationId": {
    ".validate": "newData.hasChildren()",
    ...
    ".write": "newData.val() == null"
  }
}

vs

"outgoing_invitations": {
  "$key12": {
    ".validate": "newData.hasChildren()",
    ...
  },
  ".validate": "newData.hasChildren()",
  ".write": "newData.val() == null"
}

but this works:

"incoming_invitations": {
  "$key12": {
    ...
  },
  ".validate": "newData.hasChildren()",
  ".write": "((data.val() == null && auth != null) || ((data.val() != null && newData.val() == null) && ((auth != null && auth.uid == $uid) || data.child('senderUserId').val() == auth.uid)))"
},

Tip 21

If you only get PERMISSION_DENIED without any other information, the reason can be found by looking at the socket comminucation with Chrome DevTools (See corresponding request with same r-value as error frame).

Tip 22

Shallower security rules override rules at deeper paths. Child rules can only grant additional privileges to what parent nodes have already declared. They cannot revoke a read or write privilege.

Tip 23

Bolt function definitions can not contain spaces:

  • WRONG:
function isMeGroupMember (groupId) {}
function isMeGroupMember( groupId ) {}
  • RIGHT:
function isMeGroupMember(groupId) {}

For more information see: https://github.com/FirebaseExtended/bolt/blob/master/docs/language.md.