[security] fix(workflows): enforce subworkflow access checks#93
Merged
mbakgun merged 1 commit intoMay 10, 2026
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes a sub-workflow authorization gap in workflow execution. The root workflow execution path validated the workflow being invoked, but referenced execute-node and agent sub-workflow targets were loaded by UUID only and cached for execution without checking whether the actor was allowed to access each referenced workflow.
The patch threads an actor user id through referenced-workflow collection, checks access for every referenced workflow before caching it, and applies the same boundary across direct workflow execution, streaming execution, dashboard assistant execution, portal execution, MCP workflow execution, and background trigger executions.
Security issues covered
subWorkflowIdsthat reference another workflow by UUIDBefore this PR
collect_referenced_workflows()loaded execute-node targets withselect(Workflow).where(Workflow.id == uuid.UUID(target_id)).subWorkflowIdsused the same UUID-only lookup.After this PR
collect_referenced_workflows()accepts anactor_user_idfor access decisions.user_has_workflow_access()before it is cached.403 Forbidden.Why this matters
Workflow UUIDs are authorization-sensitive. If a user can create and execute their own workflow, and referenced workflows are loaded by UUID only, the user can make their workflow point at another user's workflow once they learn or obtain its id.
That can expose target workflow outputs and can also trigger workflow nodes with owner-intended side effects through an attacker-controlled root execution path.
Attack flow
data.executeWorkflowIdset to the victim UUID, ordata.subWorkflowIdscontaining the victim UUID.Affected code
backend/app/api/workflows.pycollect_referenced_workflows()execute_workflow_endpoint()execute_workflow_stream()backend/app/api/ai_assistant.pybackend/app/api/portal.pybackend/app/api/mcp.pybackend/app/api/mcp_servers.pybackend/app/api/slack.pybackend/app/api/telegram.pybackend/app/services/cron_scheduler.pybackend/app/services/imap_trigger_service.pybackend/app/services/rabbitmq_consumer.pybackend/app/services/websocket_trigger_service.pyRoot cause
CVSS assessment
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:LSafe reproduction steps
On vulnerable code:
executeWorkflowIdis user B's workflow id, or an agent node whosesubWorkflowIdscontains that id.This PR adds focused unit coverage for both execute-node and agent sub-workflow reference rejection.
Expected vulnerable behavior
A user who can execute their own workflow can cause a referenced workflow owned by another user to be loaded and executed by UUID, even when the user has no access to that referenced workflow.
Changes in this PR
_add_referenced_workflow_to_cache()to centralize referenced-workflow loading and access enforcement.actor_user_idtocollect_referenced_workflows().user_has_workflow_access()for referenced workflow authorization.403 Forbiddenwhen a referenced workflow exists but is not accessible to the actor.Files changed
backend/app/api/workflows.pybackend/app/api/ai_assistant.py,backend/app/api/portal.py,backend/app/api/mcp.py,backend/app/api/mcp_servers.py,backend/app/api/slack.py,backend/app/api/telegram.py, trigger servicesbackend/tests/test_workflow_execution_api.pyMaintainer impact
This preserves normal sub-workflow execution for workflows the actor owns or can access through existing individual/team share mechanisms. The behavior change is limited to references that point to workflows the actor is not allowed to access.
If a deployment intentionally relies on owner-run background triggers, those paths pass the workflow owner as the actor so owner-owned sub-workflow composition continues to work.
Fix rationale
The referenced workflow cache is the boundary immediately before sub-workflow execution. Enforcing access there prevents both execute-node and agent sub-workflow references from bypassing the root workflow authorization check, and it keeps the fix centralized instead of relying on every caller to pre-filter node JSON.
Type of change
Test plan
Commands run:
Result:
Note:
./check.shcould not complete in this local environment becausebunis not installed (./check.sh: line 10: bun: command not found).Disclosure notes
This PR is intentionally bounded to sub-workflow authorization for referenced workflows. It does not claim broader changes to workflow UUID secrecy, sharing semantics, or node-level sandboxing.