# User Blocking & Safety Mechanisms

- **Pattern:** `ab-000758` (`community-social-engagement.profiles-identity.user-blocking-reporting`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000758
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000758)

## Why it matters

Missing user blocking is a critical safety gap under OWASP A01 (Broken Access Control) and CWE-284. Without a blocking system enforced at the query layer, a harassing user remains visible in every surface — profile, feed, search, messaging — regardless of victim intent. This is not merely a UX problem: platforms have faced regulatory scrutiny and civil liability for enabling sustained contact after victims attempted to block. Enforcement must happen at the database level; client-side filtering leaks data before it is suppressed.

## Severity rationale

Critical because the absence of query-level block enforcement means harassing users retain full visibility into and interaction capability with victims, creating direct personal safety exposure.

## Remediation

Create a `Block` table with a composite unique index on `(blockerId, blockedId)` and enforce the block check in every query that returns user content — profile fetches, feed queries, and search results.

```ts
// lib/db.ts
export async function getProfile(userId: string, viewerId: string) {
  const isBlocked = await db.block.findFirst({
    where: {
      OR: [
        { blockerId: viewerId, blockedId: userId },
        { blockerId: userId, blockedId: viewerId }
      ]
    }
  })
  if (isBlocked) return null
  return db.user.findUnique({ where: { id: userId } })
}
```

Apply the same `NOT IN (blocked_ids)` filter to feed queries and search results. A block check that only covers profiles but not feeds gives a false sense of safety.

## Detection

- **ID:** `user-blocking-reporting`
- **Severity:** `critical`
- **What to look for:** Enumerate all relevant files and Look for a Block or UserBlock relationship/table in the database schema. Check API endpoints for blocking users (e.g., `POST /api/users/[id]/block`). Verify that blocked users are globally excluded from: profile views, search results, feed visibility, and interaction capability. Look for a "Report User" feature or endpoint as well.
- **Pass criteria:** At least 1 conforming pattern must exist. A database relationship or table for blocks exists. Blocked users are excluded from profile queries, search, and feed queries. Users cannot interact with blocked accounts (cannot see their posts, cannot follow, cannot message). Report the count of conforming instances found even on pass.
- **Fail criteria:** No blocking mechanism exists, or blocked users are visible in search/feed/profile views, or the block status is not checked before allowing interactions.
- **Skip (N/A) when:** The platform is internal or has no user safety concerns.
- **Detail on fail:** `"No user blocking table found in schema"` or `"Block relationship exists but profile queries do not check block status — blocked users are still visible"`
- **Remediation:** Implement a blocks system and enforce it across all queries:

  ```ts
  // Prisma schema
  model Block {
    id String @id @default(cuid())
    blockerId String
    blockedId String
    blocker User @relation(name: "blocker", fields: [blockerId], references: [id], onDelete: Cascade)
    blocked User @relation(name: "blocked", fields: [blockedId], references: [id], onDelete: Cascade)
    createdAt DateTime @default(now())
    @@unique([blockerId, blockedId])
  }

  // lib/db.ts - exclude blocks from all queries
  export async function getProfile(userId: string, viewerId: string) {
    const isBlocked = await db.block.findFirst({
      where: {
        OR: [
          { blockerId: viewerId, blockedId: userId },
          { blockerId: userId, blockedId: viewerId }
        ]
      }
    })
    if (isBlocked) return null

    return db.user.findUnique({ where: { id: userId } })
  }

  // API route for blocking
  export async function POST(req: Request, { params }) {
    const { blockUserId } = await req.json()
    const userId = getCurrentUserId() // From auth

    await db.block.create({
      data: { blockerId: userId, blockedId: blockUserId }
    })
    return Response.json({ success: true })
  }
  ```

## External references

- cwe CWE-284
- owasp A01

Taxons: access-control

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