Block enforcement applied only at the profile level but not the feed query means a blocked user's posts continue appearing in the blocker's feed — the block has the appearance of working while delivering no actual protection. This is OWASP A01 (Broken Access Control) and CWE-284: a declared access restriction not enforced at the data layer. GDPR Art. 25 requires that data minimization be built into the processing itself, not applied as a post-fetch client-side filter. Client-side filtering also leaks the count and shape of blocked content to the browser before suppression.
Critical because feed-level block bypass means harassing content reaches victims regardless of their explicit block action, creating direct user safety exposure and a broken-access-control defect.
Add block and mute exclusions to the feed WHERE clause at query time in app/api/feed/route.ts. Load blocked and muted IDs once per request, then exclude them from the post filter — never rely on client-side filtering.
// app/api/feed/route.ts
export async function GET(req: Request) {
const userId = getCurrentUserId()
const [following, blocked, muted] = await Promise.all([
db.follow.findMany({ where: { followerId: userId }, select: { followedId: true } }),
db.block.findMany({ where: { blockerId: userId }, select: { blockedId: true } }),
db.mute.findMany({ where: { muterId: userId }, select: { mutedId: true } })
])
const excludedIds = [
...blocked.map(b => b.blockedId),
...muted.map(m => m.mutedId)
]
const posts = await db.post.findMany({
where: {
userId: { in: following.map(f => f.followedId), notIn: excludedIds }
},
orderBy: { createdAt: 'desc' },
take: 20
})
return Response.json(posts)
}
ID: community-social-engagement.activity-feeds.block-mute-enforcement
Severity: critical
What to look for: Enumerate all relevant files and Examine the feed query for exclusions. Check whether the WHERE clause filters out posts from users the current user has blocked or muted. Look for subqueries or JOIN logic that checks the Block and Mute tables. Verify this filtering happens at the database level (not client-side post-fetch filtering, which could leak data).
Pass criteria: No more than 0 violations are acceptable. Feed query includes NOT EXISTS (SELECT 1 FROM blocks WHERE ...) or similar logic to exclude blocked/muted users' posts. The filtering is done at query time, before data is returned to the client.
Fail criteria: No block/mute filtering in feed queries, or filtering is done on the client-side (posts are fetched and then filtered in application code).
Skip (N/A) when: No blocking or muting feature exists.
Detail on fail: "Feed query has no WHERE clause excluding blocked users — their posts appear in the feed" or "Block filtering happens in JavaScript after fetch — leaks post count data to client"
Remediation: Filter blocks and mutes at the database level:
// app/api/feed/route.ts
export async function GET(req: Request) {
const userId = getCurrentUserId()
const posts = await db.post.findMany({
where: {
AND: [
{ userId: { in: followedIds } },
{
NOT: {
userId: {
in: await db.block.findMany({
where: { blockerId: userId },
select: { blockedId: true }
}).then(bs => bs.map(b => b.blockedId))
}
}
},
{
NOT: {
userId: {
in: await db.mute.findMany({
where: { muterId: userId },
select: { mutedId: true }
}).then(ms => ms.map(m => m.mutedId))
}
}
}
]
},
orderBy: { createdAt: 'desc' },
take: 20
})
return Response.json(posts)
}