Skip to content

Commit 0b8ead9

Browse files
authored
fix(@vercel/blob): Disallow passing callbackUrl from clients (#875)
* fix(@vercel/blob): Disallow passing callbackUrl from clients BREAKING CHANGE: To continue receiving onUploadCompleted callback once a file is uploaded with Client Uploads, you need to provide the callbackUrl at the onBeforeGenerateToken step when using handleUpload. Before: await handleUpload({ body, request, onBeforeGenerateToken: async (pathname) => { /* options */ }, onUploadCompleted: async ({ blob, tokenPayload }) => { /* code */ }, }); After: await handleUpload({ body, request, onBeforeGenerateToken: async (pathname) => { callbackUrl: 'https://example.com/api/upload' }, onUploadCompleted: async ({ blob, tokenPayload }) => { /* code */ }, }); See the updated documentation at https://vercel.com/docs/vercel-blob/client-upload to know more. Details: Before this commit, during Client Uploads, we would infer the `callbackUrl` at the client side level (browser) based on location.href (for convenience). This is wrong and allows browsers to redirect the onUploadCompleted callback to a different website. While not a security risk because the blob urls are already public and the browser already knows them, it still pose a risk of database drift if you're relying on onUploadCompleted callback to update any system on your side. * changeset * update nodejs version * update * update * fix * update * change minimum requirement * update * update * update changeset * update * update * update * update
1 parent acaa9a0 commit 0b8ead9

File tree

7 files changed

+765
-25
lines changed

7 files changed

+765
-25
lines changed

.changeset/tiny-cups-push.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
'@vercel/blob': major
3+
---
4+
5+
**BREAKING CHANGE:**
6+
7+
To continue receiving `onUploadCompleted` callback once a file is uploaded with Client Uploads when **not hosted on Vercel**, you need to provide the `callbackUrl` at the `onBeforeGenerateToken` step when using `handleUpload`.
8+
9+
**When hosted on Vercel:**
10+
No code changes required. The `callbackUrl` is inferred from [Vercel system environment variables](https://vercel.com/docs/environment-variables/system-environment-variables):
11+
12+
- In preview environment: `VERCEL_BRANCH_URL` when available, otherwise `VERCEL_URL`
13+
- In production environment: `VERCEL_PROJECT_PRODUCTION_URL`
14+
15+
If you're not hosted on Vercel or you're not using Vercel system environment variables, your will need to provide the `callbackUrl`:
16+
17+
**Before:**
18+
19+
```ts
20+
await handleUpload({ body, request,
21+
onBeforeGenerateToken: async (pathname) => { /* options */ },
22+
onUploadCompleted: async ({ blob, tokenPayload }) => { /* code */ },
23+
});
24+
```
25+
26+
**After:**
27+
28+
```ts
29+
await handleUpload({ body, request,
30+
onBeforeGenerateToken: async (pathname) => {
31+
return { callbackUrl: 'https://example.com' }; // the path to call will be automatically computed
32+
},
33+
onUploadCompleted: async ({ blob, tokenPayload }) => { /* code */ },
34+
});
35+
```
36+
37+
**For local development:**
38+
Set the `VERCEL_BLOB_CALLBACK_URL` environment variable to your tunnel URL:
39+
40+
```bash
41+
VERCEL_BLOB_CALLBACK_URL=https://abc123.ngrok-free.app
42+
```
43+
44+
See the updated documentation at https://vercel.com/docs/vercel-blob/client-upload to know more.
45+
46+
**Details:**
47+
48+
Before this commit, during Client Uploads, we would infer the `callbackUrl` at the client side level (browser) based on `location.href` (for convenience).
49+
This is wrong and allows browsers to redirect the onUploadCompleted callback to a different website.
50+
51+
While not a security risk, because the blob urls are already public and the browser knows them, it still pose a risk of database drift if you're relying on onUploadCompleted callback to update any system on your side.

.github/workflows/unit-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
runs-on: ubuntu-latest
2020
strategy:
2121
matrix:
22-
node-version: [18.x, 20.x, 22.x]
22+
node-version: [20.x, 22.x]
2323
steps:
2424
- name: Checkout
2525
uses: actions/checkout@v4

.node-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
lts/hydrogen
1+
lts/iron

packages/blob/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,6 @@
8181
"tsup": "8.4.0"
8282
},
8383
"engines": {
84-
"node": ">=16.14"
84+
"node": ">=20.0.0"
8585
}
8686
}

packages/blob/src/client.browser.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ describe('client', () => {
7878
1,
7979
'http://localhost:3000/api/upload',
8080
{
81-
body: '{"type":"blob.generate-client-token","payload":{"pathname":"foo.txt","callbackUrl":"http://localhost:3000/api/upload","clientPayload":null,"multipart":false}}',
81+
body: '{"type":"blob.generate-client-token","payload":{"pathname":"foo.txt","clientPayload":null,"multipart":false}}',
8282
headers: { 'content-type': 'application/json' },
8383
method: 'POST',
8484
},

0 commit comments

Comments
 (0)