fix(files): require workspace write permission for execution-context uploads#5402
fix(files): require workspace write permission for execution-context uploads#5402waleedlatif1 wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview 403 is returned when the caller only has read access; missing Reviewed by Cursor Bugbot for commit bc4d2d1. Configure here. |
Greptile SummaryThis PR closes a missing authorization check on the
Confidence Score: 4/5Safe to merge — the change is a narrowly scoped authorization fix with thorough test coverage and no observable behavior change for callers who already have write/admin access. The route fix is correct and well-tested. The only notable point is that the permission check is placed inside the per-file loop, meaning multi-file execution uploads incur one extra DB call per file. This is consistent with how knowledge-base and workspace contexts already behave, so it is an inherited pattern rather than a new regression, but worth a tidy-up to keep the auth gate from scaling with batch size. Both changed files look good. route.ts has the minor loop-placement note on the permission check; route.test.ts is clean. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant POST as POST /api/files/upload
participant Auth as getSession
participant Perms as getUserEntityPermissions
participant Upload as uploadExecutionFile
Client->>POST: "multipart/form-data (file, context=execution, workspaceId, workflowId, executionId)"
POST->>Auth: getSession()
Auth-->>POST: session or null
alt session missing
POST-->>Client: 401 Unauthorized
end
POST->>POST: "parse & validate form fields"
alt workflowId / executionId / workspaceId missing
POST-->>Client: 400 InvalidRequestError
end
POST->>Perms: getUserEntityPermissions(userId, workspace, workspaceId)
Perms-->>POST: "read | write | admin | null"
alt "permission != write AND != admin"
POST-->>Client: 403 Write or Admin access required
end
POST->>Upload: uploadExecutionFile(workspaceId, workflowId, executionId, buffer)
Upload-->>POST: userFile result
POST-->>Client: 200 upload result
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant POST as POST /api/files/upload
participant Auth as getSession
participant Perms as getUserEntityPermissions
participant Upload as uploadExecutionFile
Client->>POST: "multipart/form-data (file, context=execution, workspaceId, workflowId, executionId)"
POST->>Auth: getSession()
Auth-->>POST: session or null
alt session missing
POST-->>Client: 401 Unauthorized
end
POST->>POST: "parse & validate form fields"
alt workflowId / executionId / workspaceId missing
POST-->>Client: 400 InvalidRequestError
end
POST->>Perms: getUserEntityPermissions(userId, workspace, workspaceId)
Perms-->>POST: "read | write | admin | null"
alt "permission != write AND != admin"
POST-->>Client: 403 Write or Admin access required
end
POST->>Upload: uploadExecutionFile(workspaceId, workflowId, executionId, buffer)
Upload-->>POST: userFile result
POST-->>Client: 200 upload result
|
|
Superseded by #5403 — same fix, that PR has the more complete test coverage and an added note about the self-hosted/no-cloud-storage deployment behavior change. Closing this duplicate to avoid two open PRs for the same change. |
Summary
/api/files/uploadonly checked for a valid session — unlike the sibling knowledge-base/workspace/workspace-logos branches in the same file, and unlike the equivalent execution branch in/api/files/presigned, it never verified the caller has write/admin access to the target workspacegetUserEntityPermissionswrite/admin gate already used by every other branch in this routeworkspaceIdis now required for execution uploads instead of silently defaulting to an empty string when omittedType of Change
Testing
workspaceIdis rejected (400) instead of defaultingbun run check:api-validationpassesChecklist