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
90%
Does it follow best practices?
Impact
94%
1.05xAverage score across 3 eval scenarios
High
Do not use without reviewing
{
"context": "Cross-tenant access coverage for a repository whose existing suite runs as one tenant. Predicted baseline failure: the agent adds a second tenant but tests only the read path, leaving update and delete - the operations that cause damage rather than disclosure - covered from inside one tenant only. A subtler failure is proving the negative without proving the positive: asserting tenant B cannot read a document, without a control showing tenant A can, so a repository that returns not_found for everyone would pass. The skill's value is applying the same cross-boundary probe to every operation, with a paired control.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Test file created",
"description": "A file src/documentAccess.test.js exists and contains at least one test.",
"max_score": 4
},
{
"name": "Two disjoint tenants in the fixture",
"description": "MUST NOT test the boundary from one side. The fixture seeds documents belonging to at least two different tenants and constructs a session for each. A suite with a single tenantId anywhere in it cannot exercise a boundary and scores zero on this criterion.",
"max_score": 16
},
{
"name": "Cross-tenant probe on every operation",
"description": "Read, list, update and delete are each attempted by a session from the wrong tenant. Score proportionally: four operations probed is full marks, three is three quarters, two is half, one or none scores zero. Update and delete count as probed only if the attempt targets a document owned by the other tenant.",
"max_score": 30
},
{
"name": "Write attempts asserted not to have mutated anything",
"description": "The cross-tenant update and delete tests assert the target document is unchanged and still present afterwards - by reading it back as its rightful owner, or through rawGet and countAll. Asserting only that the call returned not_found scores at most half: the refusal status and the absence of a mutation are separate claims.",
"max_score": 20
},
{
"name": "Positive control alongside each negative",
"description": "For the operations probed, the suite also shows the rightful tenant succeeds. Without a control, a repository broken so that everything returns not_found would satisfy every cross-tenant assertion. Missing controls entirely scores zero here.",
"max_score": 14
},
{
"name": "List leakage asserted by content, not count",
"description": "The list test asserts the returned documents all belong to the calling tenant - checking ids or tenantIds - rather than only that the length matches an expected number. A count-only assertion passes if the wrong documents are returned in the right quantity.",
"max_score": 10
},
{
"name": "Identifiers do not collide across tenants",
"description": "MUST NOT give both tenants documents with the same id values. Distinct ids per tenant mean a leak cannot be masked by a coincidental match, and a filter dropped entirely shows up immediately.",
"max_score": 6
}
]
}