Security Rules
How to write Cloud Storage Security Rules Tests
Keeping your users’ data safe and secure
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 the
RulesTestEnvironment
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 insidewithSecurityRulesDisabled
, 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 Buffer
class 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: