fix(uploads): set Content-Type once on presigned PUTs; document x-goog-meta-folderid for GCS CORS - #6121
Conversation
…g-meta-folderid in the GCS CORS example XMLHttpRequest.setRequestHeader appends on repeated calls (values join with a comma), and GCS is the only provider whose signed uploadHeaders include Content-Type — so single-shot GCS uploads sent 'x, x', which fails V4 signature verification with 403 (headers canonicalize to a comma-separated value that must match what was signed; multipart part PUTs are unaffected since part URLs don't sign Content-Type). The client now sets its default Content-Type only when the server's signed headers don't already carry one, with regression tests for both paths. Also adds x-goog-meta-folderid to the documented GCS CORS responseHeader list — workspace uploads now sign a folderId metadata header, and GCS CORS matches preflight request headers against that list exactly (no wildcards), so the missing entry blocked browser uploads into folders.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview Self-hosting GCS CORS docs add Regression tests cover one Reviewed by Cursor Bugbot for commit 3a62851. Configure here. |
Greptile SummaryFixes presigned object-storage uploads and updates GCS configuration guidance.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/uploads/client/direct-upload.ts | Uses a case-insensitive header-name check to ensure presigned PUT requests set Content-Type exactly once. |
| apps/sim/lib/uploads/client/direct-upload.test.ts | Adds regression tests for both server-supplied and client-defaulted Content-Type headers. |
| apps/docs/content/docs/en/platform/self-hosting/object-storage.mdx | Adds the folder metadata header required by workspace uploads to the documented GCS CORS configuration. |
Reviews (2): Last reviewed commit: "chore(uploads): drop inline comment" | Re-trigger Greptile
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 3a62851. Configure here.
Summary
XMLHttpRequest.setRequestHeaderappends on repeated calls (values join with a comma), and GCS is the only storage provider whose signeduploadHeadersincludeContent-Type— so the browser sentContent-Type: x, xwhile the URL was signed for the single value. Per Google's canonical-request rules, headers canonicalize to a comma-separated value that must match what was signed exactly, so the doubled value 403s. The client now sets its defaultContent-Typeonly when the server's signed headers don't already carry one (case-insensitive check); S3/Azure behavior is unchanged, and multipart part PUTs were never affected (part URLs don't sign Content-Type).x-goog-meta-folderid. Workspace uploads sign afolderIdmetadata header, and GCS matches preflightAccess-Control-Request-Headersagainst the CORSresponseHeaderlist exactly (no wildcards) — so the missing entry blocked browser uploads into folders. Added to the documented list; a sweep of every metadata key passed to presigned-upload generation confirms the list is now complete (originalname, uploadedat, purpose, userid, workspaceid, workflowid, executionid, folderid).Type of Change
Testing
Content-Typepresent → header set exactly once with the signed value; absent (S3/Azure shape) → client default set exactly onceresponseHeadergoverns preflight allow-headers with exact matching)Checklist