Security Rules

How to write Cloud Storage Security Rules Tests

Keeping your users’ data safe and secure

su-
Firebase Developers

--

We all know that, in order to keep your users’ data safe and secure in Firebase products like Cloud Storage or Cloud Firestore, we need to write Security Rules. But how do we test them and make sure they don’t break when we make changes to our application or the way we structure our data?

In this article, I am going to walk you through how to write tests for Cloud Storage, making use of the Local Emulator Suite and the Firebase Security Rules unit testing library. The latest version of the library has some cool new features that we will use as well.

For a Japanese version of the article, visit my blog.

Install necessary packages

To start writing test code for Firebase Cloud Storage rules, you need to install some packages. Open package.json and add the below packages.

You should install Firebase JS SDK v9.0 or higher and firebase/rules-unit-testing v2.0 or higher to follow this post.

About the testing framework, you can use mocha or something else instead.

Set up the environment

storage.rules

Put storage.rules in your repository and write security rules for Firebase Cloud Storage like this:

This rule makes sure that users can upload PNG images to the users/{user_id} folder by checking the correct file extension and Content-Type. The rule also makes sure that images don’t exceed a size of 50KiB. And finally, it makes sure that users can only download their own images.

To simplify the explanation, this doesn't allow list, update, delete operation for /users/{user_id}/{allPaths=**.

To learn more about Firebase Security Rules, check out the official documentation:

firebase.json

Add the following code to configure the Storage Emulator for your project.

Alternatively, you can run $ firebase init emulator to let Firebase create this file for you.

When you are ready, create a test directory.

Initialize/finalize the environment for testing

Test for Security Rules are written in JavaScript / TypeScript. Here is a skeleton you can use to write your tests. It makes sure to connect to your project (replace demo-users-storage-rules-tests with your project ID), and loads your local storage.rules file:

We can access the Cloud Storage instance through the RulesTestEnvironment instance.

There are some key points to set up/clean up the environment:

  • (1) Before all tests begin, you should create RulesTestEnvironment instance with a unique project ID for each test file.
  • (2) Before each test case begins, (or after finishing each test case) you need to clear the storage data. Don’t worry — this is happening locally, so no data will be lost in your production project.
  • (3) After finishing all test cases, you need to finalize theRulesTestEnvironment instance.

[Q] How do I set Project ID to testEnv?

  • You need to set a unique project ID for each test file.
  • I recommended using the prefix demo-

For more details about connecting to the Cloud Storage Emulator, check out the official docs:

Uploading mock files

When you write test cases, you may need to upload mock files to the Storage Emulator in advance while ignoring the security rule you wrote.

To achieve this, stage your mock files using the RulesTestEnvironment.withSecurityRulesDisabled function. This allows you to temporarily bypass any rules for Cloud Storage.

Please note that If you want to access context.storage() more than twice inside withSecurityRulesDisabled , you should assign it to a variable once to avoid an error.

Obtaining a CloudStorage instance

To obtain an unauthenticated CloudStorage instance, use the following code snippet:

testEnv.unauthenticatedContext().storage()

If you want to write a test with an authenticated user, you can use the following snippet instead:

testEnv.authenticatedContext('user_id').storage()

It’s also possible to specify additional authentication information like this:

Validating Security Rules for each operation

get/list operations

You can use getDownloadURL function with assertion like this:

You can use list/listAll functions with assertion like this:

*To get resources from Storage Emulator with the correct security rules, you need to upload any test files in advance. If a test file doesn’t exist at a specified path, Null value error will be returned.

create operation

You can use StorageReference.put function with assertion.

By the way, as the put doesn't return Promise<void>, you should add .then() behind put()to get the return value of Promise<void> if you want to wait for asynchronous processing with async/await .

● Validating metadata
If you want to validate a file’s metadata on a create operation, for example Content-Type or some custom fields, you can set metadata information in put :

In storage.rules, you can use this custom function for checking the Content-Type

● Validating file extensions
You can validate file extensions with the following function in your tests:

In storage.rules, you can use the following custom function for checking a file’s extension:

● Validating the file size
To validate the size of a file, load a file and pass it to put

Inside storage.rules, you can use this custom function for checking a file’s size:

update operation

You can use StorageReference.updateMetadata for checking update operation.

await assertFails(ref.updateMetadata({})

delete operation

Simply use StorageReference.delete

await assertFails(ref.delete())

💡 Tips: Uploading large files to Storage Emulator in your tests

Typically, most services deal with files of several MB or even several GB. However, it’s not a good idea to keep huge files for testing in your repository.

Therefore, if you want to verify the upload of a large file, you can use the Bufferclass to create a larger file of the required size in memory and upload it to the Storage Emulator:

Run tests

To run your tests, against the Storage Emulator, you first need to instal the Firebase Local Emulator Suite using the firebase command.

$ firebase setup:emulators:storage

After installing it, you can run your tests using one of the following commands:

$ yarn firebase emulators:exec --only storage 'jest'
$ yarn firebase emulators:exec --only storage 'jest --watch'

Conclusion

In this post, you’ve learned how to write tests for Cloud Storage Security Rules. This additional safety net will give you confidence you’re not breaking anything when adding new features to your app.

If you’re interested in the code I showed in this post, check out my GitHub profile:

--

--

su-
Firebase Developers

iOS & Firebase & Flutter Developer / Engineering Manager