Demo rescue
A marketplace where every seller could see every seller
Two-sided marketplaces make the multi-tenant failure expensive: the data that leaks is commercial, and the people it leaks to are each other’s competitors.
Built with Bolt on Firebase
A demonstration build, not a client engagement. We built this app, broke it, and repaired it to show the method. No client is named or implied, and the figures below are measurements from this build alone.
- Stack
-
- Bolt
- React
- Firebase
- Firestore
- Cloud Functions
- Duration
- [n] days
01Before
What was broken
- S1
Firestore rules allowed any authenticated read
The security rules granted read access to any signed-in user rather than scoping by seller. Any seller who created an account could query the full orders collection, including other sellers’ volumes, customers and pricing.
- S2
Tenant identity came from the client
The seller ID used to filter queries was read from a value the client sent. Changing it in the request returned another seller’s data, because the server never checked it against the authenticated session.
- S3
Deployments failed unpredictably
Cloud Functions were deployed from a local machine with environment variables that existed nowhere else. A deploy from any other machine produced a working build and a broken runtime, with no error tracking to explain it.
- S4
No backups and no tested restore
Firestore was running with default settings and no export schedule. There was no point at which data loss could have been recovered from.
02After
What we changed
- C1
Security rules rewritten against the auth token
Every rule now scopes to the seller claim on the authenticated token rather than to a client-supplied value. The rules have a test suite that runs against the Firestore emulator in CI, asserting cross-tenant reads fail.
- C2
Tenant identity taken from the session only
Seller ID is resolved server-side from custom claims. The client can no longer influence which tenant a query resolves to, and the parameter it used to send is rejected outright.
- C3
Reproducible deploys and real error tracking
Configuration moved into managed secrets, deployment moved into CI on merge, and separate projects created for staging and production. Error tracking and structured logging added, so a runtime failure now names itself.
- C4
Scheduled backups and a restore we watched work
Daily exports with retention, plus a documented restore procedure that was executed into a scratch project to prove it works. A backup nobody has restored from is not a backup.
03Measured
What it measured
| Measure | Before | After |
|---|---|---|
| Collections readable across tenants | [n] | 0 |
| Cross-tenant access tests in CI | 0 | [n] |
| Deploy success rate from a clean checkout | [n]% | 100% |
| Recovery point objective | None | [n] hours |
| Time to rescue | — | [n] days |
Figures are measurements from this demonstration build. All demo rescues
Your app probably fails in one of these ways.
The audit tells you which, with evidence and a fix estimate, in two business days. If it finds nothing material you do not pay for it.
Prefer to write? Send your requirements