# No Cross-Tenant Data Leakage in List Endpoints

- **Pattern:** `ab-002266` (`saas-authorization.admin-privilege.no-cross-tenant-leakage`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002266
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002266)

## Why it matters

List endpoints without mandatory tenant scope filters are the highest-impact cross-tenant leakage vector: a single `GET /api/projects` call returns every project in the database if the tenant filter is missing or overridable. CWE-284 (Improper Access Control) and CWE-639 (Authorization Bypass Through User-Controlled Key) apply when the tenant filter can be bypassed by request parameters. OWASP A01 (Broken Access Control) lists this as a primary multi-tenant failure. NIST 800-53 AC-3 and SC-4 (Information in Shared Resources) require that tenant boundaries be enforced at the data layer. Search and autocomplete endpoints are a particularly common blind spot — they are often added quickly and the tenant filter is forgotten.

## Severity rationale

High because a missing tenant filter on a single list endpoint exposes every record in the affected table to any authenticated user, across all organizations in the system.

## Remediation

Hard-code the tenant filter from the session in every list query. No query parameter — including search terms — may override or remove it.

```typescript
// app/api/projects/route.ts
export async function GET(req: NextRequest) {
  const session = await auth();
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  const { searchParams } = new URL(req.url);
  const search = searchParams.get('q') ?? '';

  const projects = await db.project.findMany({
    where: {
      orgId: session.user.activeOrgId,  // Non-negotiable — never from user input
      name: { contains: search, mode: 'insensitive' },
    },
  });

  return NextResponse.json(projects);
}
```

Audit search and autocomplete endpoints specifically — they are the most common location for missing tenant filters because they are often added as a fast follow-up feature.

## Detection

- **ID:** `no-cross-tenant-leakage`
- **Severity:** `high`
- **What to look for:** Examine list/index API endpoints (`GET /api/projects`, `GET /api/users`, `GET /api/invoices`) in multi-tenant routes. Check whether results are filtered by the active tenant. Also look for search or autocomplete endpoints — these are a common vector for cross-tenant leakage because filtering is sometimes omitted for performance or simplicity.
- **Pass criteria:** All list endpoints in multi-tenant contexts always include the tenant ID filter. Search and autocomplete endpoints are scoped to the current tenant. No query parameter can override or remove the tenant scope filter.
- **Fail criteria:** Any list endpoint returns data from multiple tenants, or a query parameter (e.g., `?orgId=all`) can override the tenant scope filter.
- **Skip (N/A) when:** Multi-tenant concept not detected.
- **Detail on fail:** `"List endpoint at [route] may return cross-tenant data. Tenant scope filter is absent or overridable."` (Note the specific endpoint and how the filter can be bypassed.)
- **Remediation:** The tenant filter must be non-negotiable — it cannot be omitted or overridden by any user-supplied query parameter. Hard-code the tenant filter from the session in every list query.

  ```typescript
  // app/api/projects/route.ts
  export async function GET(req: NextRequest) {
    const session = await auth();
    if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

    const { searchParams } = new URL(req.url);
    const search = searchParams.get('q') ?? '';

    const projects = await db.project.findMany({
      where: {
        orgId: session.user.activeOrgId,   // Non-negotiable — never expose this to user input
        name: { contains: search, mode: 'insensitive' },
      },
    });

    return NextResponse.json(projects);
  }
  ```

  For a deeper analysis of multi-tenant isolation patterns, the Multi-Tenancy Audit in the SaaS Readiness Pack covers cross-tenant leakage in detail.

---

## External references

- cwe CWE-284
- cwe CWE-639
- owasp A01
- nist AC-3
- nist SC-4

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-002266
