diff options
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/api/package.json | 1 | ||||
| -rw-r--r-- | packages/api/utils/upload.ts | 12 |
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)) { |
