From 8f55ed9da9410e6b32fb7cfa92246371fa392936 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Thu, 22 Jan 2026 09:24:08 -0800 Subject: [PATCH] security advisory fixes opened by kolega-ai-dev --- src/app/(main)/admin/users/UserAddForm.tsx | 6 +++++- src/app/api/auth/logout/route.ts | 7 +++++++ src/app/api/auth/sso/route.ts | 14 +++++++++----- src/app/api/users/[userId]/route.ts | 8 ++++++-- src/app/api/users/route.ts | 5 +++-- src/lib/auth.ts | 5 ++--- src/lib/crypto.ts | 4 ++++ 7 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/app/(main)/admin/users/UserAddForm.tsx b/src/app/(main)/admin/users/UserAddForm.tsx index 6c365510..84b8399c 100644 --- a/src/app/(main)/admin/users/UserAddForm.tsx +++ b/src/app/(main)/admin/users/UserAddForm.tsx @@ -10,6 +10,7 @@ import { TextField, } from '@umami/react-zen'; import { useMessages, useUpdateQuery } from '@/components/hooks'; +import { messages } from '@/components/messages'; import { ROLES } from '@/lib/constants'; export function UserAddForm({ onSave, onClose }) { @@ -37,7 +38,10 @@ export function UserAddForm({ onSave, onClose }) { diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index 7bf0a813..153f1f52 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -1,7 +1,14 @@ import redis from '@/lib/redis'; +import { parseRequest } from '@/lib/request'; import { ok } from '@/lib/response'; export async function POST(request: Request) { + const { error } = await parseRequest(request); + + if (error) { + return error(); + } + if (redis.enabled) { const token = request.headers.get('authorization')?.split(' ')?.[1]; diff --git a/src/app/api/auth/sso/route.ts b/src/app/api/auth/sso/route.ts index bba3dde3..f8222869 100644 --- a/src/app/api/auth/sso/route.ts +++ b/src/app/api/auth/sso/route.ts @@ -1,7 +1,7 @@ import { saveAuth } from '@/lib/auth'; import redis from '@/lib/redis'; import { parseRequest } from '@/lib/request'; -import { json } from '@/lib/response'; +import { json, serverError } from '@/lib/response'; export async function POST(request: Request) { const { auth, error } = await parseRequest(request); @@ -10,9 +10,13 @@ export async function POST(request: Request) { return error(); } - if (redis.enabled) { - const token = await saveAuth({ userId: auth.user.id }, 86400); - - return json({ user: auth.user, token }); + if (!redis.enabled) { + return serverError({ + message: 'Redis is disabled', + }); } + + const token = await saveAuth({ userId: auth.user.id }, 86400); + + return json({ user: auth.user, token }); } diff --git a/src/app/api/users/[userId]/route.ts b/src/app/api/users/[userId]/route.ts index aade8aa8..e642fe3c 100644 --- a/src/app/api/users/[userId]/route.ts +++ b/src/app/api/users/[userId]/route.ts @@ -1,7 +1,7 @@ import { z } from 'zod'; import { hashPassword } from '@/lib/password'; import { parseRequest } from '@/lib/request'; -import { badRequest, json, ok, unauthorized } from '@/lib/response'; +import { badRequest, json, notFound, ok, unauthorized } from '@/lib/response'; import { userRoleParam } from '@/lib/schema'; import { canDeleteUser, canUpdateUser, canViewUser } from '@/permissions'; import { deleteUser, getUser, getUserByUsername, updateUser } from '@/queries/prisma'; @@ -27,7 +27,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ user export async function POST(request: Request, { params }: { params: Promise<{ userId: string }> }) { const schema = z.object({ username: z.string().max(255).optional(), - password: z.string().max(255).optional(), + password: z.string().min(8).max(255).optional(), role: userRoleParam.optional(), }); @@ -47,6 +47,10 @@ export async function POST(request: Request, { params }: { params: Promise<{ use const user = await getUser(userId); + if (!user) { + return notFound(); + } + const data: any = {}; if (password) { diff --git a/src/app/api/users/route.ts b/src/app/api/users/route.ts index dbb114cf..4335c33f 100644 --- a/src/app/api/users/route.ts +++ b/src/app/api/users/route.ts @@ -4,6 +4,7 @@ import { uuid } from '@/lib/crypto'; import { hashPassword } from '@/lib/password'; import { parseRequest } from '@/lib/request'; import { badRequest, json, unauthorized } from '@/lib/response'; +import { userRoleParam } from '@/lib/schema'; import { canCreateUser } from '@/permissions'; import { createUser, getUserByUsername } from '@/queries/prisma'; @@ -11,8 +12,8 @@ export async function POST(request: Request) { const schema = z.object({ id: z.uuid().optional(), username: z.string().max(255), - password: z.string(), - role: z.string().regex(/admin|user|view-only/i), + password: z.string().min(8).max(255), + role: userRoleParam, }); const { auth, body, error } = await parseRequest(request, schema); diff --git a/src/lib/auth.ts b/src/lib/auth.ts index ba6d8b09..832dfb60 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -1,7 +1,6 @@ import debug from 'debug'; import { ROLE_PERMISSIONS, ROLES, SHARE_TOKEN_HEADER } from '@/lib/constants'; -import { secret } from '@/lib/crypto'; -import { getRandomChars } from '@/lib/generate'; +import { createAuthKey, secret } from '@/lib/crypto'; import { createSecureToken, parseSecureToken, parseToken } from '@/lib/jwt'; import redis from '@/lib/redis'; import { ensureArray } from '@/lib/utils'; @@ -53,7 +52,7 @@ export async function checkAuth(request: Request) { } export async function saveAuth(data: any, expire = 0) { - const authKey = `auth:${getRandomChars(32)}`; + const authKey = `auth:${createAuthKey()}`; if (redis.enabled) { await redis.client.set(authKey, data); diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts index a6d912b8..ee4c977f 100644 --- a/src/lib/crypto.ts +++ b/src/lib/crypto.ts @@ -63,3 +63,7 @@ export function uuid(...args: any) { return process.env.USE_UUIDV7 ? v7() : v4(); } + +export function createAuthKey() { + return crypto.randomBytes(16).toString('hex'); +}