CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/cross-tenant-data-leak-tests

Workflow-driven skill that plans and implements the cross-tenant leak-test suite - from surface inventory to the runtime CI gate a multi-tenant codebase must pass on every PR. The planning section inventories tenant-bearing surfaces (tables, APIs, object storage, search, queues, caches), classifies each by isolation model (silo / pool / bridge, per references/isolation-models.md), and derives the OWASP WSTG-ATHZ-02 coverage matrix. The battery defines the canonical test patterns (read-other-tenant-by-id, list-leak, spoofed-tenant-id-in-body, JWT-replay, FK-cross-tenant, unique-collision side channel, object-storage IDOR, search-index-direct-query, async-job-context-reload, cache-key-collision), the 404-vs-403 disclosure trade-off, the Postgres-RLS-direct patterns, and the CI integration (non-superuser non-BYPASSRLS role, fail the build on any leak). Use when designing or implementing a tenant-isolation test suite, adding the CI gate to an existing project, or investigating a leak finding.

90

1.05x
Quality

90%

Does it follow best practices?

Impact

94%

1.05x

Average score across 3 eval scenarios

SecuritybySnyk

High

Do not use without reviewing

Overview
Quality
Evals
Security
Files

criteria.jsonevals/scenario-2/

{
  "context": "Isolation planning for a feature whose tenant-bearing surfaces extend well past its HTTP handlers. The source deliberately contains three genuine scoping gaps - the search query carries no tenant filter, the cache key is not tenant-prefixed, and the queue consumer looks up the record by id alone - while the object-storage key and the primary read query are correctly scoped. Predicted baseline failure: the agent produces a competent plan for the request handlers, lists 'the database' as the storage surface, and never separates search, cache and the async consumer into surfaces of their own, so it reproduces exactly the review that missed the search leak last time.",
  "type": "weighted_checklist",
  "checklist": [
    {
      "name": "Plan file created",
      "description": "A file named attachment-isolation-test-plan.md is produced.",
      "max_score": 4
    },
    {
      "name": "Inventory covers all five surface types",
      "description": "The inventory names the relational store, the object store, the search index, the message topic and its consumer, and the cache as distinct surfaces, each with how it is or is not tenant-scoped. Score proportionally by how many of the five appear as separate entries; three or fewer scores at most half. A plan organised only by HTTP handler scores zero on this criterion.",
      "max_score": 24
    },
    {
      "name": "Search index leak identified",
      "description": "The plan states that searchAttachments passes the query straight to the index with no tenant predicate, and that indexed documents carry no tenantId field to filter on. Omitting this scores zero - it is the leak the team already suffered.",
      "max_score": 18
    },
    {
      "name": "Cache key leak identified",
      "description": "The plan states the cache key `attachment:<id>` is not tenant-scoped, and that getAttachment returns a cached record before the tenant-filtered query runs, so a cached entry is served across tenants. A plan that lists the cache as a surface without naming the unscoped key or the read-before-filter ordering scores at most half.",
      "max_score": 16
    },
    {
      "name": "Async consumer leak identified",
      "description": "The plan states that onAttachmentCreated looks the record up by id with no tenant predicate, and that the message payload carries no tenant context to check against. Treating the queue only as a publish surface, without examining the consumer, scores zero here.",
      "max_score": 14
    },
    {
      "name": "Probes given per surface",
      "description": "Each inventory entry carries a concrete probe - the cross-tenant action to attempt and the expected refusal - rather than a general instruction to test isolation. A plan that lists surfaces without per-surface probes scores zero on this criterion.",
      "max_score": 12
    },
    {
      "name": "Two-tenant fixture stated as a requirement",
      "description": "The fixture section requires at least two disjoint tenants with non-colliding identifiers, and a session per tenant.",
      "max_score": 8
    },
    {
      "name": "Correctly-scoped surfaces not reported as gaps",
      "description": "MUST NOT invent findings. The tenant-prefixed object-storage key and the tenant-filtered findOne in getAttachment are not presented as scoping failures. Flagging either as a leak scores zero here.",
      "max_score": 4
    }
  ]
}

SKILL.md

tile.json