aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-12-27 00:36:21 +0000
committerMohamed Bassem <me@mbassem.com>2025-12-27 00:36:21 +0000
commit2dbdf76c75852f14fee9564b7b29be070754ed5b (patch)
tree7f1e49388c89e397d42518d7930a901fbee00c29 /packages
parent347793ada2217f07e519c22147020664a52b122f (diff)
downloadkarakeep-2dbdf76c75852f14fee9564b7b29be070754ed5b.tar.zst
fix: reject spoofed content types on uploads
Diffstat (limited to 'packages')
-rw-r--r--packages/api/package.json1
-rw-r--r--packages/api/utils/upload.ts12
2 files changed, 12 insertions, 1 deletions
diff --git a/packages/api/package.json b/packages/api/package.json
index c9f34bcb..b5d90f03 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -22,6 +22,7 @@
"@karakeep/trpc": "workspace:*",
"@trpc/server": "^11.4.3",
"drizzle-orm": "^0.44.2",
+ "file-type": "^21.2.0",
"hono": "^4.10.6",
"prom-client": "^15.1.3",
"rss": "^1.2.2",
diff --git a/packages/api/utils/upload.ts b/packages/api/utils/upload.ts
index a843e29c..b82bc855 100644
--- a/packages/api/utils/upload.ts
+++ b/packages/api/utils/upload.ts
@@ -3,6 +3,7 @@ import * as os from "os";
import * as path from "path";
import { Readable } from "stream";
import { pipeline } from "stream/promises";
+import { fileTypeFromBlob, supportedMimeTypes } from "file-type";
import { assets, AssetTypes } from "@karakeep/db/schema";
import { QuotaService, StorageQuotaError } from "@karakeep/shared-server";
@@ -58,7 +59,16 @@ export async function uploadAsset(
data = formData.image;
}
- const contentType = data.type;
+ const detectedType = await fileTypeFromBlob(data);
+ const fallbackType =
+ data.type && data.type.trim().length > 0 ? data.type : null;
+ // Security: reject browser-provided MIME when we cannot sniff a valid type.
+ if (fallbackType && supportedMimeTypes.has(fallbackType) && !detectedType) {
+ return { error: "Unsupported asset type", status: 400 };
+ }
+ const contentType =
+ detectedType?.mime ?? fallbackType ?? "application/octet-stream";
+
// Replace all non-ascii characters with underscores
const fileName = data.name.replace(/[^\x20-\x7E]/g, "_");
if (!SUPPORTED_UPLOAD_ASSET_TYPES.has(contentType)) {