diff --git a/package-lock.json b/package-lock.json index 669fb22..8f6ab62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "bcrypt": "^6.0.0", "daisyui": "^5.7.50", "drizzle-orm": "^0.45.4", + "mime": "^4.1.2", "mysql2": "^3.24.6", "nanostores": "^1.5.5", "pino": "^10.4.0", @@ -5576,6 +5577,21 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/mime": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.2.tgz", + "integrity": "sha512-ikRzZ2RwbUVDyL9SsU0f2Phcmav4yq5r8faH5ZZKHtOZrMBLnrQOrj0uvqZ5R1uQnj+RO7Wcklw/lAf81mP9LQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", diff --git a/package.json b/package.json index cd56ce8..d7cb8d1 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "bcrypt": "^6.0.0", "daisyui": "^5.7.50", "drizzle-orm": "^0.45.4", + "mime": "^4.1.2", "mysql2": "^3.24.6", "nanostores": "^1.5.5", "pino": "^10.4.0", diff --git a/src/actions/report.ts b/src/actions/report.ts index 4f89ed0..0b3f002 100644 --- a/src/actions/report.ts +++ b/src/actions/report.ts @@ -9,6 +9,7 @@ import crypto from 'node:crypto'; import path from 'node:path'; import { sendWebhook, WebhookAction } from '@util/webhook.ts'; import { allowedImageTypes, allowedVideoTypes } from '@util/media.ts'; +import mime from 'mime/lite'; export const report = { submitReport: defineAction({ @@ -26,14 +27,6 @@ export const report = { .nullable() }), handler: async (input) => { - const fileSize = input.files?.reduce((prev, curr) => prev + curr.size, 0); - if (fileSize && fileSize > MAX_UPLOAD_BYTES) { - throw new ActionError({ - code: 'BAD_REQUEST', - message: 'Die Anhänge sind zu groß' - }); - } - const report = await db.getReportByUrlHash({ urlHash: input.urlHash }); if (!report) { throw new ActionError({ @@ -54,13 +47,33 @@ export const report = { } } - if (!UPLOAD_PATH) { + if (input.files && !UPLOAD_PATH) { throw new ActionError({ code: 'FORBIDDEN', message: 'Es dürfen keine Anhänge hochgeladen werden' }); } + let totalFilesSize = 0; + for (const file of (input.files ?? [])) { + const realType = mime.getType(file.name); + if (realType != file.type) { + throw new ActionError({ + code: 'NOT_ACCEPTABLE', + message: `Falscher Dateityp für '${file.name}': ${realType}` + }); + } + + totalFilesSize += file.size; + } + + if (totalFilesSize > MAX_UPLOAD_BYTES) { + throw new ActionError({ + code: 'BAD_REQUEST', + message: 'Die Anhänge sind zu groß' + }); + } + const filePaths = [] as string[]; try { await db.transaction(async (tx) => {