fix: enforce folder ACL on flow run-by-version routes (GHSA-8mv7-hmrg-96xv)#9202
Conversation
…-96xv) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Claude finished @rubenfiszel's task in 5m 3s —— View job Good to merge — correct, minimal, consistently applied security fix with solid regression coverage. ReviewI traced the vulnerable path and the fix end-to-end against The fix is correct. Coverage is complete. All three handlers ( SQLx cache is clean. Exactly one added entry matching the new RLS query, zero deletions. The fallback raw query ( AGENTS.md complianceNew Observations (non-blocking, not gating)
Test coverageBackend: the regression test No further manual verification needed beyond the end-to-end repro already documented in the PR description. |
There was a problem hiding this comment.
1 issue found across 5 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/windmill-common/src/lib.rs">
<violation number="1" location="backend/windmill-common/src/lib.rs:1584">
P2: NotAuthorized response leaks sensitive metadata (flow path and permission details) to unauthorized users.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Fix all with cubic | Re-trigger cubic
Codex ReviewGood to merge No issues found. Checked for bugs, security problems, public-surface auth expectations, and AGENTS.md compliance. Test coverageBackend API/security coverage is present via Manual verification should still exercise |
|
Now I have enough context to complete the review. Pi ReviewGood to merge This is a clean, well-scoped security fix. The vulnerability is real, the fix correctly mirrors the RLS-gating pattern already used by the path-keyed sibling routes ( What was reviewed
Public surface checklist
Test coverageBackend — regression test ( Manual verification — already performed per the PR body (pre-fix repro confirmed bypass, post-fix all four routes return 401 for unauthorized member, authorized users still get 201). No further manual scenarios needed. |
…bic P2) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks @cubic-dev-ai — addressed in a1da7d7. P2 (NotAuthorized leaks flow path + permission details), identified by cubic — valid and fixed. Nuance: the message was copied verbatim from the existing path-keyed sibling Fix:
Did not touch the path-keyed sibling's message — out of scope for this advisory, and there the path is caller-supplied so it's a non-issue; changing it would also make the two routes inconsistent in a different way. The other three reviewers (Claude, Codex, Pi) returned Good to merge with no blocking findings. |
@rubenfiszel That looks right — the fix in The key design choice that stands out as correct:
No further action needed from my end. The three other reviewers' "good to merge" verdicts plus the updated CI check all align. |
Codex ReviewGood to merge No issues found. Checked for bugs, security problems, public-surface auth expectations, and AGENTS.md compliance. Test coverageBackend API/security coverage is present via Manual verification should include |
…re-removal) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pi ReviewGood to merge The final revision (a1da7d7) addresses the information-disclosure concern raised in the prior discussion and is now complete. The fix is correct, minimal, and consistently applied across all affected routes. What changed since the initial reviewThe initial version of The fix in a1da7d7:
The three handlers ( Public surface checklist
SQLx cacheTwo new entries added ( Consistency with existing patternsThe Test coverageBackend — regression test (
The Manual verification — already performed per the PR body (pre-fix repro confirmed bypass, post-fix all four version routes return 401 for unauthorized member, authorized users still get 201). No further manual scenarios needed. |
Summary
Private security fix for GHSA-8mv7-hmrg-96xv (HIGH). The version-keyed flow
run routes resolved
flow_version.id -> pathon the raw DB pool and thengated only on
check_scopes(...), which checks API-token scopes — not thecaller's folder-level RBAC. The folder ACL is enforced by Postgres RLS on the
flowtable, reached only throughuser_db; the version routes never wentthrough it. A low-privilege workspace member with a normal (unscoped) token
could therefore execute an admin-owned, folder-scoped flow they have zero
permission on, by passing its
flow_version.id. The path-keyed siblingPOST /jobs/run/f/{path}was always correctly gated (RLS-aware lookup) andreturned 401 — confirming the asymmetry.
Affected routes (all share the fix):
POST /jobs/run/fv/{version},POST|GET /jobs/run_wait_result/fv/{version}, and (transitively)GET|POST /jobs/run_and_stream/fv/{version}.Introduced in v1.576.0 (PR #7062, "webhook by flow version"); vulnerable
through current main / v1.703.1.
Changes
get_flow_path_for_version_authedinwindmill-common/src/lib.rs: resolves aflow_version.idto its path through an RLS-filteredflow_version INNER JOIN flowquery onuser_db, mirroring the exists-but-unauthorized →NotAuthorized/ missing →NotFoundsemantics ofget_latest_flow_version_id_for_path.run_flow_by_version_inner,run_wait_result_flow_by_version,run_wait_result_flow_by_version_get) through the new helper instead of the raw-pool lookup, beforecheck_scopes.stream_flow_by_versionis covered transitively viarun_flow_by_version_inner.backend/tests/ghsa_8mv7_hmrg_96xv.rs+ fixture.Test plan
fv/{id}executed an admin folder-scoped flow as a zero-permission member; post-fix all four version routes return 401 while authorized users (admin + folder-granted member) still get 201local-reviewclean (no P0/P1/P2)🤖 Generated with Claude Code
Summary by cubic
Fixes GHSA-8mv7-hmrg-96xv by enforcing folder ACL on all flow run-by-version routes. Blocks running a folder-scoped flow via
flow_version.idwithout permission and avoids leaking the flow path in unauthorized errors.get_flow_path_for_version_authedto resolveflow_version.id -> pathvia an RLS-filtered join onuser_db, with NotAuthorized/NotFound semantics and no path leakage.POST /jobs/run/fv/{version},POST|GET /jobs/run_wait_result/fv/{version}, and the streaming variant (viarun_flow_by_version_inner) to use the helper before scope checks.Written for commit 1a1f2eb. Summary will update on new commits. Review in cubic