diff --git a/server/src/__tests__/attachment-types.test.ts b/server/src/__tests__/attachment-types.test.ts index af0a58b3..5a430102 100644 --- a/server/src/__tests__/attachment-types.test.ts +++ b/server/src/__tests__/attachment-types.test.ts @@ -86,4 +86,12 @@ describe("matchesContentType", () => { expect(matchesContentType("text/csv", patterns)).toBe(true); expect(matchesContentType("application/zip", patterns)).toBe(false); }); + + it("handles plain * as allow-all wildcard", () => { + const patterns = ["*"]; + expect(matchesContentType("image/png", patterns)).toBe(true); + expect(matchesContentType("application/pdf", patterns)).toBe(true); + expect(matchesContentType("text/plain", patterns)).toBe(true); + expect(matchesContentType("application/zip", patterns)).toBe(true); + }); }); diff --git a/server/src/attachment-types.ts b/server/src/attachment-types.ts index 3f95156b..f9625de1 100644 --- a/server/src/attachment-types.ts +++ b/server/src/attachment-types.ts @@ -45,6 +45,7 @@ export function parseAllowedTypes(raw: string | undefined): string[] { export function matchesContentType(contentType: string, allowedPatterns: string[]): boolean { const ct = contentType.toLowerCase(); return allowedPatterns.some((pattern) => { + if (pattern === "*") return true; if (pattern.endsWith("/*") || pattern.endsWith(".*")) { return ct.startsWith(pattern.slice(0, -1)); } diff --git a/server/src/routes/assets.ts b/server/src/routes/assets.ts index ed8d5944..bd2f154d 100644 --- a/server/src/routes/assets.ts +++ b/server/src/routes/assets.ts @@ -33,7 +33,7 @@ export function assetRoutes(db: Db, storage: StorageService) { } catch (err) { if (err instanceof multer.MulterError) { if (err.code === "LIMIT_FILE_SIZE") { - res.status(422).json({ error: `Image exceeds ${MAX_ATTACHMENT_BYTES} bytes` }); + res.status(422).json({ error: `File exceeds ${MAX_ATTACHMENT_BYTES} bytes` }); return; } res.status(400).json({ error: err.message });