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

task.mdevals/scenario-1/

Document access is only tested from inside one tenant

Problem Description

src/documentAccess.js scopes every document operation to the caller's tenant. The whole suite runs as a single tenant, so every test passes whether or not the scoping works - remove the tenant filter and nothing fails.

We need coverage that actually attempts access across the boundary.

Output Specification

Add src/documentAccess.test.js covering cross-tenant access attempts against each operation this module exposes: read, list, update and delete.

Run npm test before you finish; it must pass.

Leave src/documentAccess.single.test.js in place.

Input Files

Extract the following files before beginning.

=============== FILE: package.json =============== { "name": "workspace-docs", "version": "1.3.0", "private": true, "scripts": { "test": "node --test" } }

=============== FILE: src/documentAccess.js =============== 'use strict';

function createRepository(seed) { const documents = new Map(seed.map((doc) => [doc.id, { ...doc }]));

function visible(session, id) { const doc = documents.get(id); if (!doc || doc.tenantId !== session.tenantId) { return null; } return doc; }

return { list(session) { return [...documents.values()].filter((doc) => doc.tenantId === session.tenantId); }, read(session, id) { const doc = visible(session, id); return doc ? { status: 'ok', document: doc } : { status: 'not_found', document: null }; }, update(session, id, patch) { const doc = visible(session, id); if (!doc) { return { status: 'not_found' }; } Object.assign(doc, patch); return { status: 'ok', document: doc }; }, remove(session, id) { const doc = visible(session, id); if (!doc) { return { status: 'not_found' }; } documents.delete(id); return { status: 'ok' }; }, countAll() { return documents.size; }, rawGet(id) { return documents.get(id) || null; }, }; }

module.exports = { createRepository };

=============== FILE: src/documentAccess.single.test.js =============== 'use strict';

const test = require('node:test'); const assert = require('node:assert/strict'); const { createRepository } = require('./documentAccess');

const SESSION = { userId: 'u_1', tenantId: 't_acme' };

test('a tenant reads its own document', () => { const repo = createRepository([ { id: 'doc_1', tenantId: 't_acme', title: 'Roadmap' }, ]);

assert.equal(repo.read(SESSION, 'doc_1').status, 'ok'); assert.equal(repo.list(SESSION).length, 1); });

SKILL.md

tile.json