Uh oh!
There was an error while loading. Please reload this page.
fix(files): authorize presigned upload contexts per type - #6175
Conversation
The batch presign endpoint validated its type param against the shared seven-value upload enum while only authorizing knowledge-base, so any authenticated user could mint an S3 presigned PUT into workspace-logos, profile-pictures, execution, mothership, chat and copilot prefixes — objects that are then served unauthenticated from the app origin with a one-year public cache. The single presign endpoint had the same gap for chat, which has no authorization predicate and no client. - Batch presign now accepts only knowledge-base, and always requires a workspaceId the caller has write/admin on before anything is minted - Single presign drops chat from its accepted contexts; every remaining context has a per-context predicate - Both allowlists live in the contract module so the enforced enum and the documented one cannot drift apart again
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview
Tests add full coverage for the batch route (401, rejected contexts, missing params, 403, happy path, fallback) and a regression that Reviewed by Cursor Bugbot for commit 999b80d. Configure here. |
Greptile SummaryThe PR closes unauthorized presigned-upload paths by defining endpoint-specific upload-context schemas and requiring workspace write access for batch knowledge-base uploads.
Confidence Score: 5/5The PR appears safe to merge with no actionable regressions identified in the changed upload authorization paths. The endpoint-specific allowlists match all repository callers, workspace authorization occurs before batch URL issuance or fallback, and the removed profile-picture session check remains enforced by the route-level authentication guard.
|
| Filename | Overview |
|---|---|
| apps/sim/app/api/files/presigned/batch/route.ts | Restricts batch uploads to knowledge-base files, requires workspace write authorization, and consistently records ownership for generated keys. |
| apps/sim/app/api/files/presigned/route.ts | Uses the narrowed single-upload context schema so chat is rejected while each remaining context follows its existing authorization and validation path. |
| apps/sim/lib/api/contracts/storage-transfer.ts | Introduces endpoint-specific upload-context enums and reuses the single-upload enum in the documented query contract. |
| apps/sim/app/api/files/presigned/batch/route.test.ts | Adds comprehensive regression tests covering authentication, authorization, context and file validation, storage fallback, and ownership metadata. |
| apps/sim/app/api/files/presigned/route.test.ts | Replaces obsolete chat success cases and verifies that the unauthorizable chat context cannot mint a URL. |
Sequence Diagram
sequenceDiagram
participant Client
participant Route as Presigned Upload Route
participant Auth as Session / Workspace Auth
participant Storage as Object Storage
participant Metadata as File Metadata
Client->>Route: POST upload request with context
Route->>Auth: Validate session
Auth-->>Route: Authenticated user
Route->>Route: Validate endpoint-specific context
alt Batch knowledge-base upload
Route->>Auth: Check workspace write/admin access
Auth-->>Route: Authorized
end
Route->>Storage: Mint presigned upload URL
Storage-->>Route: URL and object key
opt Knowledge-base upload
Route->>Metadata: Record workspace ownership
end
Route-->>Client: Presigned URL and file information
Reviews (1): Last reviewed commit: "fix(files): authorize presigned upload c..." | Re-trigger Greptile
Summary
POST /api/files/presigned/batchvalidated itstypeparam against the shared 7-value upload enum but only authorizedknowledge-base, so any authenticated user could mint an S3 presigned PUT intoworkspace-logos/,profile-pictures/,execution/,mothership/,chat/andcopilot/— objects then served unauthenticated from the app origin with a one-year public cacheknowledge-baseonly (its sole client and its sole authorizable context) and always requires aworkspaceIdthe caller has write/admin on before any URL is mintedPOST /api/files/presignedhad the same class of gap forchat: no authorization predicate, no content-type restriction, no client. Dropped it — every remaining context there has a per-context predicate (copilotself-scoped + type-restricted,profile-picturesself-scoped + image-only,mothership/executionworkspace write,workspace-logosworkspace admin,knowledge-baseworkspace write)lib/api/contracts/storage-transfer.tsso the enum that is enforced and the one that is documented can't drift apart again — that drift was the root causeType of Change
Testing
app/api/files/presigned/batch/route.test.ts: 401 unauthenticated, 400 for each of the six refused contexts with no URL minted, 400 missingworkspaceId, 403 for read/no access, authz enforced ahead of the local-storage fallback, file-type rejection before minting, and the authorized happy pathtype=chatis rejected without mintingapp/api/files+lib/uploads: 449 tests passing;tsc -p apps/simclean;bun run check:api-validationpassedNot included
Per-user upload quota / billing attribution for presign issuance. With authorization restored, only workspace members with write access can mint these URLs, so metering is a separate billing feature rather than part of this fix.
Checklist