diff --git a/apps/server/src/imageMime.test.ts b/apps/server/src/imageMime.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..a420e1f6b8bcf5d2e20f34a76f948ba5ce3ee0fb --- /dev/null +++ b/apps/server/src/imageMime.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { parseBase64DataUrl } from "./imageMime.ts"; + +describe("imageMime", () => { + it("parses base64 data URL with mime type", () => { + expect(parseBase64DataUrl("data:image/png;base64,SGVsbG8=")).toEqual({ + mimeType: "image/png", + base64: "SGVsbG8=", + }); + }); + + it("parses base64 data URL with mime parameters", () => { + expect(parseBase64DataUrl("data:image/png;charset=utf-8;base64,SGVsbG8=")).toEqual({ + mimeType: "image/png", + base64: "SGVsbG8=", + }); + }); + + it("rejects non-base64 data URL", () => { + expect(parseBase64DataUrl("data:image/png;charset=utf-8,hello")).toBeNull(); + }); + + it("rejects missing mime type", () => { + expect(parseBase64DataUrl("data:;base64,SGVsbG8=")).toBeNull(); + }); +}); diff --git a/apps/server/src/imageMime.ts b/apps/server/src/imageMime.ts index 249deff0ca8efb37a8708c6fbe3039a9aa3a2ca4..9ecf7615a6eed2fc8e88b17d7daab6023ff40df7 100644 --- a/apps/server/src/imageMime.ts +++ b/apps/server/src/imageMime.ts @@ -32,10 +32,22 @@ export const SAFE_IMAGE_FILE_EXTENSIONS = new Set([ export function parseBase64DataUrl( dataUrl: string, ): { readonly mimeType: string; readonly base64: string } | null { - const match = /^data:([^;,]+);base64,([a-z0-9+/=\r\n]+)$/i.exec(dataUrl.trim()); + const match = /^data:([^,]+),([a-z0-9+/=\r\n]+)$/i.exec(dataUrl.trim()); if (!match) return null; - const mimeType = match[1]?.trim().toLowerCase(); + const headerParts = (match[1] ?? "") + .split(";") + .map((part) => part.trim()) + .filter((part) => part.length > 0); + if (headerParts.length < 2) { + return null; + } + const trailingToken = headerParts.at(-1)?.toLowerCase(); + if (trailingToken !== "base64") { + return null; + } + + const mimeType = headerParts[0]?.toLowerCase(); const base64 = match[2]?.replace(/\s+/g, ""); if (!mimeType || !base64) return null; diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts index 921f45b1631cbe03fcd6627691f9ecf9542dd76f..1b5271c51ac371058fd0ff29d2c0e6c102f9e62e 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts @@ -421,7 +421,6 @@ const make = Effect.gen(function* () { { threadId: input.threadId, cwd, oldBranch, cause: Cause.pretty(cause) }, ), ), - Effect.forkScoped, ); }); @@ -464,7 +463,7 @@ const make = Effect.gen(function* () { messageId: message.id, messageText: message.text, ...(message.attachments !== undefined ? { attachments: branchGenerationAttachments } : {}), - }); + }).pipe(Effect.forkScoped); yield* sendTurnForThread({ threadId: event.payload.threadId,