fix(Security): restrict draft action execution to editors#41614
fix(Security): restrict draft action execution to editors#41614
Conversation
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on April 9. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
WalkthroughCentralizes action execution permission selection into a new helper that chooses edit vs execute permission based on normalized viewMode and draft/published state, updates execution flows to use it, and adds tests covering viewer/editor execution for published and unpublished actions. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCEImpl.java`:
- Around line 194-200: Update getActionExecutionPermission to treat a null
ExecuteActionDTO.getViewMode() the same as FALSE by checking
Boolean.TRUE.equals(executeActionDTO.getViewMode()) and returning
executePermission only in that case; otherwise return editPermission (use
actionPermission.getEditPermission()/getExecutePermission()). Also ensure
getValidActionForExecution computes permissions after anonymous-user
normalization (so normalize the user/anonymous state first, then call
getActionExecutionPermission) to avoid a missing viewMode bypass.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 47b453d1-fe11-4ba0-9d1d-d18ba18ab464
📒 Files selected for processing (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCEImpl.javaapp/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java
...ith-server/src/main/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCEImpl.java
Show resolved
Hide resolved
Authenticated viewers could execute unpublished/draft actions by sending viewMode=false in the action execute request. The server only blocked anonymous users from draft execution; any authenticated user with EXECUTE_ACTIONS permission could bypass the editor/publish boundary. When viewMode=false (draft execution), require MANAGE_ACTIONS (edit) permission instead of EXECUTE_ACTIONS. Editors retain MANAGE_ACTIONS via the MANAGE_PAGES hierarchy; viewers only have EXECUTE_ACTIONS via READ_PAGES and are now blocked from draft execution. Adds regression tests for viewer blocked from draft, viewer allowed published, and editor allowed draft execution scenarios. Fixes APP-15010
a2d3635 to
3d3c58b
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCEImpl.java`:
- Around line 212-214: The execute path can NPE when
executeActionDTO.getViewMode() is null; normalize viewMode before downstream
calls by setting a local Boolean viewMode =
Boolean.TRUE.equals(executeActionDTO.getViewMode()) ? Boolean.TRUE :
Boolean.FALSE (or otherwise treat null as FALSE) and pass that normalized value
into getActionExecutionPermission and getTrueEnvironmentId (or alternatively
change getTrueEnvironmentId to use Boolean.TRUE.equals(viewMode)); update the
call sites around getActionExecutionPermission, getTrueEnvironmentId, and
newActionService.findById to consume the normalized viewMode so nulls never
reach branching logic.
In
`@app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java`:
- Around line 2073-2252: Add a regression test that calls the real execution
entrypoint instead of only getValidActionForExecution: invoke
ActionExecutionSolutionCEImpl.populateAndExecuteAction (via
actionExecutionSolution.populateAndExecuteAction) with a prepared
ExecuteActionDTO and ExecuteActionMetaDTO and the appropriate SecurityContext
for viewMode=false and/or viewMode=true, then verify the end-to-end execution
behavior (response/error) using StepVerifier; this ensures the multipart/public
execution path that routes through populateAndExecuteAction is exercised rather
than stopping at getValidActionForExecution.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: dd5d49ca-6c2a-4ff6-9e66-bd44c59a020e
📒 Files selected for processing (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCEImpl.javaapp/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java
...ith-server/src/main/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCEImpl.java
Show resolved
Hide resolved
...ith-server/src/test/java/com/appsmith/server/solutions/ce/ActionExecutionSolutionCETest.java
Show resolved
Hide resolved
Replace raw Boolean auto-unboxing with TRUE.equals() in getTrueEnvironmentId to prevent NPE when viewMode is null. This is consistent with the null-safe pattern used throughout the rest of the execution path. Addresses CodeRabbit review feedback on PR #41614.
Description
TL;DR: Authenticated viewers can bypass the editor/publish boundary by sending
viewMode=falsein the action execute request, causing the server to run unpublished draft actions. This fix requiresMANAGE_ACTIONS(edit) permission for draft execution instead ofEXECUTE_ACTIONS.Root Cause
The action execution path (
getValidActionForExecution,populateAndExecuteAction) only checksEXECUTE_ACTIONSpermission regardless ofviewMode. Since viewers inheritEXECUTE_ACTIONSviaREAD_APPLICATIONS → READ_PAGES → EXECUTE_ACTIONS, any authenticated viewer can setviewMode=falseand execute unpublished draft actions. Only anonymous users were blocked.Fix
Introduced
getActionExecutionPermission()helper that returns:MANAGE_ACTIONS(edit permission) whenviewMode=false(draft execution)EXECUTE_ACTIONS(execute permission) whenviewMode=true(published execution)Editors retain
MANAGE_ACTIONSvia theMANAGE_PAGES → MANAGE_ACTIONShierarchy and continue working normally. Viewers only haveEXECUTE_ACTIONSand are now blocked from draft execution.Regression Tests
testViewerCannotExecuteUnpublishedAction— viewer withviewMode=falseis rejectedtestViewerCanExecutePublishedAction— viewer withviewMode=truesucceedstestEditorCanExecuteUnpublishedAction— editor withviewMode=falsesucceedsFixes https://linear.app/appsmith/issue/APP-15010/vulnerability-authenticated-viewers-can-execute-unpublished-draft
Automation
/ok-to-test tags="@tag.All"
🔍 Cypress test results
Tip
🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/23017716243
Commit: ed2b421
Cypress dashboard.
Tags:
@tag.AllSpec:
Thu, 12 Mar 2026 19:57:26 UTC
Communication
Should the DevRel and Marketing teams inform users about this change?
Summary by CodeRabbit
Bug Fixes
Tests