diff --git a/next.config.mjs b/next.config.mjs index 84ce3b00f..0e8999631 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -16,5 +16,28 @@ export default withBundleAnalyzer({ } ] }, - output: 'standalone' + output: 'standalone', + async headers() { + return [ + { + source: '/(.*)', + headers: [ + { key: 'X-Frame-Options', value: 'DENY' }, + { key: 'X-Content-Type-Options', value: 'nosniff' }, + { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, + { + key: 'Content-Security-Policy', + value: [ + "default-src 'self'", + "script-src 'self' 'unsafe-inline'", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: https://lh3.googleusercontent.com https://avatars.githubusercontent.com", + "connect-src 'self'", + "frame-ancestors 'none'" + ].join('; ') + } + ] + } + ] + } }) diff --git a/src/app/api/assessments/route.ts b/src/app/api/assessments/route.ts index 34904985e..53a07b26b 100644 --- a/src/app/api/assessments/route.ts +++ b/src/app/api/assessments/route.ts @@ -3,7 +3,7 @@ import { NextRequest } from 'next/server' import { CreateAssessmentSchema } from '@/lib/api/schema/assessment' import prisma from '@/lib/prisma' import { getServerUser } from '@/lib/session' -import { badRequest, json, unauthorized } from '@/utils/apiResponse' +import { badRequest, forbidden, json, unauthorized } from '@/utils/apiResponse' import removeArrDup from '@/utils/removeArrDup' export async function GET() { @@ -35,6 +35,7 @@ export async function POST(req: NextRequest) { if (!user) return unauthorized() if (!user.id) return unauthorized('User ID not found') + if (!user.admin) return forbidden() const parsedBody = CreateAssessmentSchema.safeParse(await req.json()) diff --git a/src/app/api/render/route.ts b/src/app/api/render/route.ts index 72e439550..4166da294 100644 --- a/src/app/api/render/route.ts +++ b/src/app/api/render/route.ts @@ -1,9 +1,13 @@ import { NextRequest } from 'next/server' import { mdxToHtml } from '@/lib/renderMarkdown' -import { badRequest, json } from '@/utils/apiResponse' +import { getServerUser } from '@/lib/session' +import { badRequest, json, unauthorized } from '@/utils/apiResponse' export async function POST(req: NextRequest) { + const user = await getServerUser() + if (!user) return unauthorized() + const body = await req.json() const { content } = body diff --git a/src/app/api/submissions/[id]/realtime/route.ts b/src/app/api/submissions/[id]/realtime/route.ts index 94137b182..cda7a5dfe 100644 --- a/src/app/api/submissions/[id]/realtime/route.ts +++ b/src/app/api/submissions/[id]/realtime/route.ts @@ -20,6 +20,7 @@ export async function GET( status: true, score: true, groups: true, + userId: true, task: { select: { @@ -43,11 +44,16 @@ export async function GET( if (!(await checkUserPermissionOnTask(user, submission.task.id))) { return forbidden() } + + if (!user.admin && submission.userId !== user.id) { + return forbidden() + } } const payload: DeepPartial = submission delete payload.task + delete payload.userId return json(payload) } diff --git a/src/app/api/tasks/[id]/route.ts b/src/app/api/tasks/[id]/route.ts index b4fbe3e30..f7b45bf15 100644 --- a/src/app/api/tasks/[id]/route.ts +++ b/src/app/api/tasks/[id]/route.ts @@ -129,8 +129,6 @@ export async function DELETE( const params = await props.params const id = params.id - console.log(id) - const user = await getServerUser() if (!user) { diff --git a/src/app/api/users/route.ts b/src/app/api/users/route.ts index d7a4bfc4f..dbc7dd2ea 100644 --- a/src/app/api/users/route.ts +++ b/src/app/api/users/route.ts @@ -1,4 +1,3 @@ -import { checkOwnerPermission } from '@/lib/api/queries/checkOwnerPermissionOnAssessment' import prisma from '@/lib/prisma' import { getServerUser } from '@/lib/session' import { forbidden, json, unauthorized } from '@/utils/apiResponse' @@ -10,7 +9,7 @@ export async function GET() { return unauthorized() } - if (!user.admin && !(user.id && (await checkOwnerPermission(user.id)))) { + if (!user.admin) { return forbidden() } diff --git a/src/app/render/page.tsx b/src/app/render/page.tsx index 764d00c7c..5be5fee9d 100644 --- a/src/app/render/page.tsx +++ b/src/app/render/page.tsx @@ -20,6 +20,10 @@ export default function Render() { method: 'POST', body: JSON.stringify({ content: md }) }) + if (!res.ok) { + setLoading(false) + return + } setRendered(await res.json()) setLoading(false) } diff --git a/src/lib/api/schema/tasks.ts b/src/lib/api/schema/tasks.ts index 0487ab643..f4866c5ee 100644 --- a/src/lib/api/schema/tasks.ts +++ b/src/lib/api/schema/tasks.ts @@ -7,7 +7,13 @@ export const IndividualTaskSchema = z.object({ id: z.string().min(1) }) export type IndividualTaskSchema = z.infer -const FilePath = z.object({ path: z.string().min(1), type: z.string() }) +const FilePath = z.object({ + path: z + .string() + .min(1) + .regex(/^[a-zA-Z0-9._-]+$/, 'Invalid file path'), + type: z.string() +}) export const TaskSchema = z.object({ id: z.string().min(1),