cloudflare / cloudflare/vinext
next/image + next/og: include VipsForeignLoadSvg in Sharp unblock allowlist (prevent process-wide ImageResponse crash after first image optimization)
- Dominant language
- TypeScript
- Stars
- 8.8k
- Forks
- 406
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 120
Description
## Next.js Change
**Commit:** [`806fbd4`](https://github.com/vercel/next.js/commit/806fbd43676b1012091a94f0462ae793b000adc6)
**PR:** [#96681](https://github.com/vercel/next.js/pull/96681) (fork of #96621)
## What changed
Fixes a process-wide crash where the first `/_next/image` optimization permanently disabled Sharp's SVG loader, breaking every subsequent `ImageResponse` (`next/og`) render in the same process.
`getSharp()` calls `_sharp.block({ operation: ['VipsForeignLoad'] })` to block every Vips image loader and then unblocks a specific allowlist. Because `_sharp` is a module-level singleton, the block is process-wide and permanent. The allowlist omitted `VipsForeignLoadSvg`, so after the first image optimization ran, Sharp could no longer load SVG.
`ImageResponse` rasterizes via resvg and then hands SVG to Sharp, so once the loader was blocked it threw `Input buffer contains unsupported image format`, surfacing as a **socket hang up / crashed response** on any `ImageResponse` route requested after an uncached `/_next/image` optimization in the same process.
Introduced by #96301, which aligned the Sharp allowlist with `detectContentType()` but missed SVG. Fixes vercel/next.js#96612.
### Mechanism (from the diff)
A one-line change in `packages/next/src/server/image-optimizer.ts`:
```ts
_sharp.block({ operation: ['VipsForeignLoad'] })
// unblock allowlist:
// 'VipsForeignLoadJpeg',
// 'VipsForeignLoadNsgif',
// 'VipsForeignLoadPng',
+// 'VipsForeignLoadSvg',
// 'VipsForeignLoadTiff',
// 'VipsForeignLoadWebp',
```
This does not weaken SVG protection for user-supplied images: `detectContentType()` still returns SVG and untrusted SVG is gated separately by `dangerouslyAllowSVG`, which throws a 400 in `imageOptimizer()` before Sharp is invoked. Unblocking the loader only restores Sharp's ability to process SVG that Next.js itself generates (e.g. from `next/og`).
## Impact on vinext
Relevant only if vinext uses Sharp with `_sharp.block(...)` for image optimization (`/_next/image`) and also supports `next/og` `ImageResponse` in the same worker/process. If vinext ports Next.js's Sharp-blocking allowlist, it must include `VipsForeignLoadSvg`, otherwise the same permanent-disable bug will crash `ImageResponse` renders after the first image optimization.
What to check/do:
1. If vinext calls `_sharp.block({ operation: ['VipsForeignLoad'] })` and unblocks an allowlist, ensure `VipsForeignLoadSvg` is in the allowlist.
2. Keep `dangerouslyAllowSVG` as the gate for untrusted user SVG — unblocking the loader is only about SVG that the framework generates.
3. On Cloudflare Workers, Sharp is typically not used (Cloudflare Images / no native Sharp), so this may be N/A for the primary target. Confirm which image backend vinext uses before acting.
## Related
_None._
Contributor guide
Research direction
Search vinext's image optimization and ImageResponse entry points for Sharp usage and any VipsForeignLoad allowlist. Confirm which image backend is used, especially for the Cloudflare Workers target, then verify whether SVG loading is needed for framework-generated output while user SVG remains gated by dangerouslyAllowSVG. Done means either documenting that this is not applicable or adding the SVG loader allowlist entry with coverage for the affected sequence.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- next.js, typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100