Files
petfeed/src/lib/assert-not-blocked.ts
T
admin d9930cba60 fix(security): enforce blocks on interaction, close remaining review findings
- New assertNotBlocked() helper (src/lib/assert-not-blocked.ts), mirroring
  assertPetOwnership. comments.create, reactions.toggle (create path), and
  reposts.create now reject direct interaction between blocked pets, not
  just feed visibility (product decision: enforce, matching messages.ts's
  existing DM block check).
- videos.updateCaption and stories.getViewers now route through the shared
  assertPetOwnership helper instead of duplicating the inline ownership
  check.
- ads.ts mutations (recordView, toggleReaction, addComment, toggleRepost)
  gain the same rate limiting their post-side equivalents already have.
- follows.listFollowers/listFollowing/getPetsWithActiveStories now apply
  getSpiderExclusionId(), matching the established per-query pattern in
  feed.ts/explore.ts/search.ts.

Remaining findings from the review are addressed:
- timingSafeStringEqual's length short-circuit needs no fix (matches
  Node's own crypto.timingSafeEqual behavior).
2026-08-23 14:50:20 +02:00

31 lines
815 B
TypeScript

import "server-only";
import { TRPCError } from "@trpc/server";
import type { PrismaClient } from "@prisma/client";
/**
* Blocks apply in both directions and must gate direct interaction
* (comments, reactions, reposts), not just feed visibility — see
* blocks.ts's own doc comment on why feed hydration alone isn't enough.
*/
export async function assertNotBlocked(
prisma: PrismaClient,
petIdA: string,
petIdB: string
) {
if (petIdA === petIdB) return;
const block = await prisma.block.findFirst({
where: {
OR: [
{ blockerPetId: petIdA, blockedPetId: petIdB },
{ blockerPetId: petIdB, blockedPetId: petIdA },
],
},
});
if (block) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Cannot interact with this pet's content.",
});
}
}