I'm working hard on putting up a Firebase backend. In the beginning it is very much straight forward but as the rules grow, it is harder to spot a security flaw. What are the options to actually test the rules? I've looked into Targaryen, which is a 3rd-party library, but can't get it up and running on OSX. Is there a more common approach to test the rules? What is the most common approach to do Firebase security tests?
If you haven't seen it yet, in the Firebase control panel for your app, there's a number of options down the left side; Data, Security and Rules, Simulator; Simulator is the one you want.
Once there, you can authenticate as a user and then test read and write ability on different child nodes.
We crafted our own small app to read/write to different nodes: as our app grew, so did the complexity of the rules and it just made it easier to bang through testing 20 nodes via the app then one at a time in the simulator. Our testing app is all of about 100 lines of code.
I've just set up Bolt and it looks like a much better option than using the standard rules and simulator through the Firebase UI.
You do need to actually use the Bolt syntax but I find it's much easier than the standard rules anyway, especially if they are getting large and complex, since Bolt allows you to create functions to re-use common code for read/write/validate logic. The testing was just a bonus for me.
Introduction to Bolt:
https://www.firebase.com/blog/2015-11-12-security-rules-bolt-user-data.html
Testing instructions: https://github.com/firebase/bolt/issues/80
A few things to note:
The instructions say to install firebase-bolt globally but node can find it unless it's installed locally or you link to the global install.
See this answer to get mocha running. If you add --ui tdd to "test": "mocha --ui tdd" in the scripts section of your package.json file and you keep your tests in test/test.js then you just need to run npm test to run all your tests.
To develop your rules you can use Targaryen.
To test your rules against a live db, you can use the REST api using a token with the debug flag set to true (database secret used to create those token are deprecated but I don't think you create such token with the new Firebase Admin SDK); the response header will include debug info about rule evaluation.
To debug your rules and production data use the simulator in the firebase console (note that it doesn't allow to simulate update operation AFAIK)
Related
Context:
I'm building a recipe website with Angular frontend and Node.js express backend.
I'm using Heroku for the backend and trying to use Firebase for the frontend. The backend is not ready for use, and is unstable. The frontend is almost ready for v1. For now, all I have is the ability to retrieve a list of recipes from the GET /api/recipes endpoint, and display them (and search through the loaded recipes). Eventually, I plan to add the ability to add/edit/delete/search through the recipes, as well as other endpoints. For now, it works equally well with a static .json file or a dynamic GET endpoint.
In the process of adding this to the backend, the backend will naturally go down and may become temporarily unusable at times, so I'm of course going to have two deployments of the API, and two deployments of the frontend. Prod and Dev, Live and Preview. This should be easy with Heroku, but Firebase is giving me some problems.
I want to have different configs for different deployments. A way for the front-end to ask Firebase "Where's the API I'm supposed to use". This shouldn't go in the repository, code once deploy many, yada: https://12factor.net/codebase . But I haven't figured out how to do this with Firebase.
Goal:
Firebase Remote Config should use a condition such as App Channel to send a different api_url parameter based on what channel the deployment is. This should not be stored in the repository. It should not give away the other deployments etc.
Whatever config I use should be build-once deploy-many. I do not wish to pass a config parameter such as configs:{"recipes.web.app":{apiUrl:"..."}, "recipes-preview-1239j20.web.app":{apiUrl:"..."}}. The frontend must not be aware of other deployments. It shouldn't even really have to be aware of where it's being hosted.
Current Progress:
I successfully deployed different versions to the live channel and the preview channel. I successfully made a RemoteConfig config using GUI (https://console.firebase.google.com/project/<project_name>/config). I did see how to add conditions. I successfully accessed RemoteConfig inside my custom service in Angular. I did find out that init.json on the preview and live channels are identical. I did successfully limit it to "If it's the recipe app", but it's useless because everything's the recipe app. There is a way to make multiple sites, but that's not really useful either, because I'd have to set up the entire app again.
I did not manage to figure out how to deploy remoteconfig.template.json from command line. I did not manage to figure out what conditions are useful. I did not manage to get the "Version" condition to be enabled in the condition selector. I did not manage to find any condition about "channels". I did not manage to find anything with "remote config" and "channels" even on the same page! I did not manage to find anything in firebase docs about "deployments".
Alternative and Unrelated Sidenote:
I'm extremely frustrated with Firebase, and if I can't figure this out, I'm going to abandon it in favor of a Heroku thing I just found. An "unsupported" buildpack for nginx (https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-nginx). "Unsupported" as in it's unofficial. I'm familiar with nginx, and using that will make everything about this personal project (except the server location and build/deploy process) match up with what I'm doing at work. And also both halves of my personal project (static web and api) will be on the same host, which might make things nice.
When configuring CI for a Firebase project, I often see references to either a FIREBASE_TOKEN generated with firebase login:ci or a service account key that (I think) is generated by default for each project.
For my particular use case, I want to do the following:
run online tests (with Firestore) against my test project when running npm run test during my CI build
deploy that code to a different prod project if tests pass
Which one should I use?
I would recommend you to use the FIREBASE_TOKEN. As usually, tokens are better to use, as you can quickly cancel or renew in any issue that you might have it, as well it's easier to manage them a service account and to insert them in the code.
Besides that, the official Firebase documentation - Use the CLI with CI systems - indicates and teaches how to use it with the token, so it seems that indeed, using the token is the best and easier option for you to implement.
I've been thinking about the best way to write automated tests to validate the bazel rules that we wrote. The bazel documentation on testing indicates we can use Skylib has a test framework called unittest.bzl, however that check the analysis-time behaviour of the rule.
In my case, we have rules that spawn a processes, and if the process dies, we expect a certain output. As such, I have created bazel targets that I expect to fails as I want to test some negative path (such as manually killing a process).
I'm looking to test happy path, but really want to test the negative paths, things that can go boom, etc...
I'm just wondering:
What worked for you and what didn't?
What techniques/tools/things you used to test the custom rules you wrote in bazel?
Did you write a bazel target that runs a sh_test, which the shell scripts runs bazel and expect a test failure?
Did you use a new tools? If so, how did you ensure the same version of the tools is used across the organization?
Did you focus on happy paths, negative paths, or both?
Looking forward to hearing your responses
I have an open-source project that uses two separate Firebase projects for a test environment and the production one.
Ultimately, I want to have other developers be able to pull down the project and actually be able to run it WITHOUT me needing to give each individual developer access.
I see a number of solutions in this question: How to add collaborators to a Firebase app?. Those all seem to require each person's email.
I understand why it maybe isn't a thing, but I am wondering if there is a way to just give access to everyone for only the test project so that contributing is super low-friction. Something similar to Firestore database rules that allow read/write in a public fashion to get started for testing.
I've tried making a new IAM account in the Google Cloud Console, and I think that partially worked for the Firebase Cloud Functions access to Admin SDK, but my collaborators get hung up trying to run firebase use <test-firebase-project> saying that they don't have access.
I see a lot of other config options for IAM, but nothing sticking out to me for this public access scenario.
Can anyone confirm this either is or isn't a thing?
Thanks!
EDIT
To add some more detail to my project...
I am using Authentication, Firestore, and Cloud Functions. The only js package I will use is the Auth one, which will be loaded from a CDN (so I believe that doesn't apply to my question).
I want to give access to people to run the Cloud Functions locally. There is a pre-build step that I have made in vanilla Node that calls a Cloud Function (running locally), which uses the Firebase Admin SDK to call my Firestore database. I then write that response to a JSON file that my front end uses.
When my collaborators pull down the project, and install the Firebase CLI, when they try to serve the Cloud Functions locally, they get hit with a "no access" type of error. I was trying to resolve this by adding a service account into the /functions directory, but apparently that didn't help.
My collaborators don't need access to the console at all. I basically just need them to be able to run the Cloud Function locally (and make sure they can't access my production Firebase project).
I am happy to add more detail, but I kind of feel like I am just rambling. Thanks again.
There is no way to grant everyone contributor access to your Firebase console. You will either have add each individual user, or create your own dashboard that uses the API to show the relevant data.
We're using google Cloud Build to deploy pull-request specific versions of our app to GAE so we can share dev versions with stakeholders before launching them into the wild. On GAE, a url looks like http://[VERSION_ID]-dot-[YOUR_PROJECT_ID].appspot.com or https://my-pr-name-dot-projectname.appspot.com
We're want to allow for stakeholders to preview and to run E2E tests (including Firebase login) but because of what's essentially a wildcard subdomain, we'd have to manually whitelist each subdomain in the the Firebase control panel under "Authorized domains" after a deploy. Unfortunately, Firebase doesn't allow for wildcard style whitelisting (eg. *-dot-projectname.appspot.com).
We've reached out to Google support but they've confirmed that whitelisting can only be done manually.
One possibility would be to use a separate, staging project for PR testing.
You'd use whitelisting for http://[YOUR_STAGING_PROJECT_ID].appspot.com or https://staging_projectname.appspot.com. And you'd manage your mapping from a specific PR to the staging project via traffic migrations, which can be done programmatically from your PR automation scripts.
The drawback would be that you'd effectively be verifying only one PR at a time. But that's not necessarily all bad: serializing PR verifications eliminates the risk of breakages due to conflicting changes that each pass in isolation.
There are also advantages of using a separate project for testing purposes you might find of interest, see Advantages of implementing CI/CD environments at GAE project/app level vs service/module level?
We encountered the same issue and decided on authorizing a constant number (say N) of "preview environments". We use GCP cloud run and our CI/CD is ran by github actions. The idea is to iterate through the number of environments such that every N PRs we'll use the same environment, this is done by calculating the modulo of the PR number to N.
Here's the main bash command in the github actions yaml:
run: echo "PREVIEW_ENV=$((${{github.event.number}} % ${{ env.N_PREVIEW_ENVS }}))" >> $GITHUB_ENV