diff --git a/src/lib/ffmpeg.worker.ts b/src/lib/ffmpeg.worker.ts index f72ef583..105e88e6 100644 --- a/src/lib/ffmpeg.worker.ts +++ b/src/lib/ffmpeg.worker.ts @@ -6,9 +6,16 @@ import { buildTextFilter } from "./text-overlay"; const CORE_BASE_URL = "https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/umd"; const MT_CORE_BASE_URL = "https://cdn.jsdelivr.net/npm/@ffmpeg/core-mt@0.12.6/dist/esm"; -const SRI_HASHES: Record = { - "ffmpeg-core.js": "sha384-sKfkiFtvUk+vexk+0EUhEh366190/4WpgUAsUvaxEfyg7+E1Zt5Y5hrsU808g8Q9", - "ffmpeg-core.wasm": "sha384-U1VDhkPYrM3wTCT4/vjSpSsKqG/UjljYrYCI4hBSJ02svbCkxuCi6U6u/peg5vpW", +const SRI_HASHES: Record> = { + umd: { + "ffmpeg-core.js": "sha384-sKfkiFtvUk+vexk+0EUhEh366190/4WpgUAsUvaxEfyg7+E1Zt5Y5hrsU808g8Q9", + "ffmpeg-core.wasm": "sha384-U1VDhkPYrM3wTCT4/vjSpSsKqG/UjljYrYCI4hBSJ02svbCkxuCi6U6u/peg5vpW", + }, + esm: { + "ffmpeg-core.js": "sha384-W///EnBaTc/koJ2li+z9tlVIZpfvrFSyePilMXKRK5PVInCGTUgCCX/CLz0XOJMK", + "ffmpeg-core.wasm": "sha384-FycsKH8SDTkBt19cTwetE082xjCaWrSu1JpBG7O1+kZRu1xnfgD4rAiCnpRPQQSX", + "ffmpeg-core.worker.js": "sha384-32plzPULGD7+hN54cJPtCAjBlATPw/00oahYoyI5MlZ6CP5/IZJ/rkeUJ6PW/Ozy", + }, }; type SerializedFile = { @@ -63,15 +70,14 @@ let ffmpegLoaded = false; let activeExportAbortController: AbortController | null = null; let activeExportId: string | null = null; -async function fetchWithIntegrity(url: string, mimeType: string): Promise { - const key = url.split("/").pop()!; - const integrity = SRI_HASHES[key]; +async function fetchWithIntegrity(url: string, mimeType: string, baseURL: string): Promise { + const filename = url.split("/").pop()!; + const isESM = baseURL === MT_CORE_BASE_URL; + const version = isESM ? "esm" : "umd"; + const integrity = SRI_HASHES[version]?.[filename]; - // Fallback to standard fetch if SRI is missing (Prevents ffmpeg-core.worker.js from crashing the thread) if (!integrity) { - const response = await fetch(url, { credentials: "omit" }); - const blob = new Blob([await response.arrayBuffer()], { type: mimeType }); - return URL.createObjectURL(blob); + throw new Error(`Missing SRI hash for ${filename} (${version} version). Refusing to load unsigned content from CDN.`); } const response = await fetch(url, { integrity, credentials: "omit" }); @@ -336,10 +342,10 @@ async function loadCore(onProgress?: (percent: number) => void): Promise { try { await ffmpeg.load({ - coreURL: await fetchWithIntegrity(`${baseURL}/ffmpeg-core.js`, "text/javascript"), - wasmURL: await fetchWithIntegrity(`${baseURL}/ffmpeg-core.wasm`, "application/wasm"), + coreURL: await fetchWithIntegrity(`${baseURL}/ffmpeg-core.js`, "text/javascript", baseURL), + wasmURL: await fetchWithIntegrity(`${baseURL}/ffmpeg-core.wasm`, "application/wasm", baseURL), ...(isIsolated && { - workerURL: await fetchWithIntegrity(`${baseURL}/ffmpeg-core.worker.js`, "text/javascript"), + workerURL: await fetchWithIntegrity(`${baseURL}/ffmpeg-core.worker.js`, "text/javascript", baseURL), }), });