From 8209fb2a2fbfd7e7910bf977dc3afda0dbb88ee2 Mon Sep 17 00:00:00 2001 From: andrew <272368723+ajbirdd@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:47:12 -0600 Subject: [PATCH] security: scope SSE stream to job owner, add security headers --- next.config.ts | 16 ++++++++++++++++ src/app/api/blocks/status/route.ts | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/next.config.ts b/next.config.ts index 1b3e47e..d8570f0 100644 --- a/next.config.ts +++ b/next.config.ts @@ -12,7 +12,23 @@ const nextConfig: NextConfig = { ], async headers() { + const securityHeaders = [ + // Prevent clickjacking + { key: 'X-Frame-Options', value: 'DENY' }, + // Stop browsers from MIME-sniffing the content type + { key: 'X-Content-Type-Options', value: 'nosniff' }, + // Referrer policy — don't leak full URL to third parties + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, + // Disable browser features we don't use + { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }, + ] + return [ + // Apply security headers to all routes + { + source: '/(.*)', + headers: securityHeaders, + }, // The Bluesky PDS fetches these endpoints cross-origin during OAuth flows { source: '/oauth-client-metadata.json', diff --git a/src/app/api/blocks/status/route.ts b/src/app/api/blocks/status/route.ts index e765e12..afb8e01 100644 --- a/src/app/api/blocks/status/route.ts +++ b/src/app/api/blocks/status/route.ts @@ -12,8 +12,9 @@ export async function GET(request: Request) { return Response.json({ error: 'jobId is required' }, { status: 400 }) } + let session: { did: string } try { - await requireSession() + session = await requireSession() } catch { return Response.json({ error: 'Not authenticated' }, { status: 401 }) } @@ -23,6 +24,11 @@ export async function GET(request: Request) { return Response.json({ error: 'Job not found' }, { status: 404 }) } + // Verify the job belongs to the requesting user + if (job.did !== session.did) { + return Response.json({ error: 'Forbidden' }, { status: 403 }) + } + const encoder = new TextEncoder() const stream = new ReadableStream({ -- 2.54.0