From bdd89579095b83fc04134d2de740f8864388a63d Mon Sep 17 00:00:00 2001 From: Florian <45694132+flo-bit@users.noreply.github.com> Date: Wed, 26 Aug 2026 00:59:00 +0200 Subject: [PATCH] moderation quick fix --- apps/web/src/hooks.server.ts | 8 ++++++++ apps/web/src/lib/helpers/actor.ts | 5 ++++- apps/web/src/lib/helpers/moderation.test.ts | 14 ++++++++++++++ apps/web/src/lib/helpers/moderation.ts | 6 ++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/lib/helpers/moderation.test.ts create mode 100644 apps/web/src/lib/helpers/moderation.ts diff --git a/apps/web/src/hooks.server.ts b/apps/web/src/hooks.server.ts index d86f244..7d864ea 100644 --- a/apps/web/src/hooks.server.ts +++ b/apps/web/src/hooks.server.ts @@ -1,5 +1,6 @@ import type { Handle } from '@sveltejs/kit'; import { restoreSession } from '$lib/atproto/server/session'; +import { isDidBlocked } from '$lib/helpers/moderation'; export const handle: Handle = async ({ event, resolve }) => { const customDomain = event.request.headers.get('X-Custom-Domain')?.toLowerCase() || undefined; @@ -14,5 +15,12 @@ export const handle: Handle = async ({ event, resolve }) => { event.locals.client = client; event.locals.did = did; + if (isDidBlocked(did)) { + return new Response('Forbidden', { + status: 403, + headers: { 'Cache-Control': 'private, no-store' } + }); + } + return resolve(event); }; diff --git a/apps/web/src/lib/helpers/actor.ts b/apps/web/src/lib/helpers/actor.ts index b56a3cd..e831529 100644 --- a/apps/web/src/lib/helpers/actor.ts +++ b/apps/web/src/lib/helpers/actor.ts @@ -3,6 +3,7 @@ import type { CacheService } from './cache'; import { env as publicEnv } from '$env/dynamic/public'; import { resolveHandle } from '../atproto'; import { isHandle } from '@atcute/lexicons/syntax'; +import { isDidBlocked } from './moderation'; export async function getActor({ request, @@ -36,5 +37,7 @@ export async function getActor({ actor = undefined; } - return isHandle(actor) ? await resolveHandle({ handle: actor }) : actor; + const did = isHandle(actor) ? await resolveHandle({ handle: actor }) : actor; + + return isDidBlocked(did) ? undefined : did; } diff --git a/apps/web/src/lib/helpers/moderation.test.ts b/apps/web/src/lib/helpers/moderation.test.ts new file mode 100644 index 0000000..4dc0da6 --- /dev/null +++ b/apps/web/src/lib/helpers/moderation.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from 'vitest'; +import { isDidBlocked } from './moderation'; + +describe('isDidBlocked', () => { + it('blocks denylisted DIDs', () => { + expect(isDidBlocked('did:plc:2mbicyoe7bckq5rutzedgnks')).toBe(true); + }); + + it('allows other and missing DIDs', () => { + expect(isDidBlocked('did:plc:allowed')).toBe(false); + expect(isDidBlocked(undefined)).toBe(false); + expect(isDidBlocked(null)).toBe(false); + }); +}); diff --git a/apps/web/src/lib/helpers/moderation.ts b/apps/web/src/lib/helpers/moderation.ts new file mode 100644 index 0000000..30edf57 --- /dev/null +++ b/apps/web/src/lib/helpers/moderation.ts @@ -0,0 +1,6 @@ +// Emergency application-level denylist. DIDs remain stable when users change handles. +const BLOCKED_DIDS: ReadonlySet = new Set(['did:plc:2mbicyoe7bckq5rutzedgnks']); + +export function isDidBlocked(did: string | null | undefined): boolean { + return did != null && BLOCKED_DIDS.has(did); +} -- 2.51.2