fix: security hardening, UI fixes, and validation improvements

- Fix header nav overflow by switching to lg: breakpoint with tighter gaps
- Fix file upload path traversal by whitelisting allowed folders and extensions
- Fix BookingModal using hardcoded content instead of DB-backed data
- Add input length validation on public master-class registration API
- Add ID validation on team member and reorder API routes
- Fix BookingModal useCallback missing groupInfo/contact dependencies
- Improve admin news date field to use native date picker
- Add missing Мастер-классы and Новости cards to admin dashboard

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-17 17:37:29 +03:00
parent 26cb9a9772
commit 3ac6a4d840
8 changed files with 73 additions and 31 deletions
+12 -3
View File
@@ -3,12 +3,15 @@ import { writeFile, mkdir } from "fs/promises";
import path from "path";
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp", "image/avif"];
const ALLOWED_EXTENSIONS = [".jpg", ".jpeg", ".png", ".webp", ".avif"];
const ALLOWED_FOLDERS = ["team", "master-classes", "news", "classes"];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
export async function POST(request: NextRequest) {
const formData = await request.formData();
const file = formData.get("file") as File | null;
const folder = (formData.get("folder") as string) || "team";
const rawFolder = (formData.get("folder") as string) || "team";
const folder = ALLOWED_FOLDERS.includes(rawFolder) ? rawFolder : "team";
if (!file) {
return NextResponse.json({ error: "No file provided" }, { status: 400 });
@@ -28,8 +31,14 @@ export async function POST(request: NextRequest) {
);
}
// Sanitize filename
const ext = path.extname(file.name) || ".webp";
// Validate and sanitize filename
const ext = path.extname(file.name).toLowerCase() || ".webp";
if (!ALLOWED_EXTENSIONS.includes(ext)) {
return NextResponse.json(
{ error: "Invalid file extension" },
{ status: 400 }
);
}
const baseName = file.name
.replace(ext, "")
.toLowerCase()