← Back to Desktop application starters · project planning pack

Skill · Version 1.0.0

Tenant isolation verification

Tests tenant boundaries adversarially using comparable data in two workspaces, including indirect relationships and shared infrastructure.

tenant-isolation-verification

Agents using this skill

See how it helps

When is it useful?

Use it before accepting a multi-tenant feature whose ordinary single-workspace tests cannot expose data leakage.

What does it help you do?

  • Uses two workspaces with comparable resources and overlapping names.
  • Attempts cross-tenant reads, writes and foreign-key injection.
  • Checks bulk exports, cache reuse, files and concurrent context handling.

Try asking this

Prove workspace A cannot attach workspace B’s file by guessing its identifier. Use valid A credentials with B's attachment ID in a normal-looking request. Reject the relationship before saving and confirm no copied metadata reveals B's file.

Detailed documentation

Read the skill’s original instructions, examples and checks in their authored language.

Details…Show less

Tenant Isolation Verification

When to Use

Use during implementation, review, diagnosis, and testing of any multi-tenant feature, endpoint, query, background worker, file handler, or AI tool.

Why Single-Tenant Tests Never Prove Isolation

A test suite that creates Tenant A and asserts Tenant A can read and update its own records proves zero tenant isolation—a query completely missing WHERE tenant_id = ? passes every single-tenant test!

  • Mandatory Two-Tenant Rule: Every tenant-scoped feature must be tested with at least two distinct workspaces (Tenant A and Tenant B) seeded with identically shaped resources (and overlapping slugs/names where permitted).

The Adversarial Cross-Tenant Test Matrix

Verify and assert all six isolation vectors in automated tests:

1. Direct IDOR / BOLA Read & Mutation Attempts
  • Cross-Tenant Read: Authenticate as a valid member/owner of Tenant A (with active context tenantId = A) and request GET /projects/<projectId_of_Tenant_B> $\rightarrow$ must return 404 Not Found (or 403), never Tenant B's data.
  • Cross-Tenant Update / Delete: Authenticate in Tenant A and send PATCH or DELETE targeting <resourceId_of_Tenant_B> $\rightarrow$ must reject and leave Tenant B's row untouched in the database.
  • Tenant Header / URL Spoofing: Authenticate as User 1 (member of Tenant A, not Tenant B) and pass X-Workspace-Id: <Tenant_B> or /w/<Tenant_B>/... $\rightarrow$ must fail membership verification (403/404) before reaching the controller.
2. Cross-Tenant Relational Injection (Confused Deputy Joins)
  • Authenticate in Tenant A and create or update a record in Tenant A while passing a foreign key ID (parent_id, category_id, assignee_id, file_id, webhook_id) belonging to Tenant B in the request payload:
    • Example: POST /w/tenant-a/tasks { title: "x", projectId: "<project_in_tenant_b>" }.
    • Verify the server validates that every referenced foreign key belongs to Tenant A (backed by composite FKs in the DB) and rejects the request.
3. List, Search, Aggregation & Export Leakage
  • Seed matching records in both Tenant A and Tenant B.
  • Call list endpoints, search/autocomplete queries, dashboard count/sum aggregations, and CSV/PDF exports as Tenant A $\rightarrow$ assert the response contains exclusively Tenant A records and zero Tenant B records.
4. Missing / Undefined Tenant Context Fail-Closed Check
  • Call repository/service methods with tenantId: undefined or empty context $\rightarrow$ assert they throw an explicit error rather than falling back to an unfiltered query.
5. Concurrent Interleaved Request Isolation
  • Fire concurrent requests (Promise.all) for Tenant A and Tenant B simultaneously against the same server/worker instance $\rightarrow$ verify zero context bleed across pooled DB connections, AsyncLocalStorage, or shared caches.
6. Revocation & Workspace Switch Verification
  • Verify that after removing a user from Tenant A (or switching active workspace to Tenant B), subsequent requests or cached queries for Tenant A are rejected and no stale Tenant A data remains visible.

Included files 1

  • SKILL.md