Security audit improvements: authentication, path traversal protection, file validation, rate limiting, security headers

This commit is contained in:
Hördle Bot
2025-11-24 09:34:54 +01:00
parent 0f7d66c619
commit 2d6481a42f
11 changed files with 287 additions and 15 deletions

View File

@@ -4,6 +4,7 @@ import { writeFile, unlink } from 'fs/promises';
import path from 'path';
import { parseBuffer } from 'music-metadata';
import { isDuplicateSong } from '@/lib/fuzzyMatch';
import { requireAdminAuth } from '@/lib/auth';
const prisma = new PrismaClient();
@@ -42,6 +43,10 @@ export async function GET() {
}
export async function POST(request: Request) {
// Check authentication
const authError = await requireAdminAuth(request as any);
if (authError) return authError;
try {
const formData = await request.formData();
const file = formData.get('file') as File;
@@ -52,6 +57,29 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'No file provided' }, { status: 400 });
}
// Security: Validate file size (max 50MB)
const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB
if (file.size > MAX_FILE_SIZE) {
return NextResponse.json({
error: `File too large. Maximum size is 50MB, got ${(file.size / 1024 / 1024).toFixed(2)}MB`
}, { status: 400 });
}
// Security: Validate MIME type
const allowedMimeTypes = ['audio/mpeg', 'audio/mp3'];
if (!allowedMimeTypes.includes(file.type)) {
return NextResponse.json({
error: `Invalid file type. Expected MP3, got ${file.type}`
}, { status: 400 });
}
// Security: Validate file extension
if (!file.name.toLowerCase().endsWith('.mp3')) {
return NextResponse.json({
error: 'Invalid file extension. Only .mp3 files are allowed'
}, { status: 400 });
}
const buffer = Buffer.from(await file.arrayBuffer());
// Validate and extract metadata from file
@@ -214,6 +242,10 @@ export async function POST(request: Request) {
}
export async function PUT(request: Request) {
// Check authentication
const authError = await requireAdminAuth(request as any);
if (authError) return authError;
try {
const { id, title, artist, releaseYear, genreIds, specialIds } = await request.json();
@@ -289,6 +321,10 @@ export async function PUT(request: Request) {
}
export async function DELETE(request: Request) {
// Check authentication
const authError = await requireAdminAuth(request as any);
if (authError) return authError;
try {
const { id } = await request.json();