Vercel Blob Client Upload Tokens
The authorization decision behind handleUpload, built around a fact visible in the type rather than the prose: in @vercel/blob 2.6.1 onBeforeGenerateToken receives pathname as an argument but the value it returns is a Pick that does not include pathname, so there is no supported way to hand back a corrected path. Whatever the browser passed to upload() is what the token is minted for, which makes the near-universal hook body that returns only allowedContentTypes an authorization to write any path in the store, including another user's avatar. A client-proposed pathname can only be refused, so this validates it against the session's own prefix and rejects traversal, leading slashes, backslashes, doubled separators and null bytes rather than trying to sanitize them. It also pins the media type server-side from the purpose instead of letting the client's file extension choose it, since contentType is inferred from the pathname by default and a blob served as text/html from a public store is stored XSS on your blob domain; sets maximumSizeInBytes on the signed token constraints rather than the advisory put option; and makes the overwrite decision explicit, because addRandomSuffix and allowOverwrite both default to false and reaching for allowOverwrite the first time a user re-uploads turns a client-controlled pathname into a primitive for replacing anyone's file at a URL that never changes. tokenPayload is derived from the session, never echoed from the client. Pinned to @vercel/[email protected].
npx shadcn@latest add https://ui.aryank.space/r/vercel-blob-client-upload-tokens.jsonInstalls from ui.aryank.space. To add it by hand, copy the files in Files below, or register the @compronents namespace via the docs.
The hook returns only allowedContentTypes, which reads as an allowlist and is one for content type alone. The pathname is not in the value the hook returns, so it cannot be corrected: the token is minted for exactly the path the browser asked for. A signed-in user writes over someone else's avatar, and because the blob URL does not change, every cache and embed keeps serving the new bytes.
@vercel/[email protected]