From f32d7702bcb592a70f5cf7e01f9e830047645240 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Mon, 20 Jul 2026 07:31:02 +0200 Subject: [PATCH] fix(inputs): add ownership check to saved-input args read endpoint (#10194) `get_args_from_history_or_saved_input` looked up saved-input rows by `id + workspace_id` only, without checking ownership or `is_public`. The `input` table has no RLS policy, so any authenticated workspace member could read another member's private saved-input args (which may hold credentials) just by knowing the UUID, even though `inputs/list` already scopes to `is_public OR created_by = caller`. Add `AND (is_public IS true OR created_by = $4)` to both `input`-table queries in the handler (the `?input=true` single-query branch and the default `v2_job UNION ALL input` branch), matching the predicate already used by `list_saved_inputs`. The `v2_job` branches are already covered by the table's RLS policies enforced via `SET LOCAL ROLE windmill_user` in `UserDB::begin()`, so they are left unchanged. Fixes WIN-2202 Co-authored-by: Claude Opus 4.8 (1M context) --- ...3d9d316e6cf0437c4efcdeae45fba0f240fc1175bf003.json} | 7 ++++--- ...fe086dd55f62df8b7f510b4c478818ff8ab1a11d7d5ea.json} | 7 ++++--- backend/windmill-api-inputs/src/lib.rs | 10 ++++++---- 3 files changed, 14 insertions(+), 10 deletions(-) rename backend/.sqlx/{query-a22146c0a924e5a1a99bc72775399a8c3db31b57ae9ded5bff55f44321c3f3c2.json => query-cb26dc8e09c0525963c3d9d316e6cf0437c4efcdeae45fba0f240fc1175bf003.json} (68%) rename backend/.sqlx/{query-55002cccf17e32af5b077a17707122cfeeeebf5a9392c6798a486274d8d233d5.json => query-ef57c1622cc57e4485ffe086dd55f62df8b7f510b4c478818ff8ab1a11d7d5ea.json} (75%) diff --git a/backend/.sqlx/query-a22146c0a924e5a1a99bc72775399a8c3db31b57ae9ded5bff55f44321c3f3c2.json b/backend/.sqlx/query-cb26dc8e09c0525963c3d9d316e6cf0437c4efcdeae45fba0f240fc1175bf003.json similarity index 68% rename from backend/.sqlx/query-a22146c0a924e5a1a99bc72775399a8c3db31b57ae9ded5bff55f44321c3f3c2.json rename to backend/.sqlx/query-cb26dc8e09c0525963c3d9d316e6cf0437c4efcdeae45fba0f240fc1175bf003.json index daeaccb715..dce702a619 100644 --- a/backend/.sqlx/query-a22146c0a924e5a1a99bc72775399a8c3db31b57ae9ded5bff55f44321c3f3c2.json +++ b/backend/.sqlx/query-cb26dc8e09c0525963c3d9d316e6cf0437c4efcdeae45fba0f240fc1175bf003.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2", + "query": "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2 AND (is_public IS true OR created_by = $4)", "describe": { "columns": [ { @@ -13,12 +13,13 @@ "Left": [ "Uuid", "Text", - "Bool" + "Bool", + "Text" ] }, "nullable": [ null ] }, - "hash": "a22146c0a924e5a1a99bc72775399a8c3db31b57ae9ded5bff55f44321c3f3c2" + "hash": "cb26dc8e09c0525963c3d9d316e6cf0437c4efcdeae45fba0f240fc1175bf003" } diff --git a/backend/.sqlx/query-55002cccf17e32af5b077a17707122cfeeeebf5a9392c6798a486274d8d233d5.json b/backend/.sqlx/query-ef57c1622cc57e4485ffe086dd55f62df8b7f510b4c478818ff8ab1a11d7d5ea.json similarity index 75% rename from backend/.sqlx/query-55002cccf17e32af5b077a17707122cfeeeebf5a9392c6798a486274d8d233d5.json rename to backend/.sqlx/query-ef57c1622cc57e4485ffe086dd55f62df8b7f510b4c478818ff8ab1a11d7d5ea.json index f9d007d4c1..bd2d3cefda 100644 --- a/backend/.sqlx/query-55002cccf17e32af5b077a17707122cfeeeebf5a9392c6798a486274d8d233d5.json +++ b/backend/.sqlx/query-ef57c1622cc57e4485ffe086dd55f62df8b7f510b4c478818ff8ab1a11d7d5ea.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM v2_job WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2", + "query": "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM v2_job WHERE id = $1 AND workspace_id = $2\n UNION ALL\n SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2 AND (is_public IS true OR created_by = $4)", "describe": { "columns": [ { @@ -13,12 +13,13 @@ "Left": [ "Uuid", "Text", - "Bool" + "Bool", + "Text" ] }, "nullable": [ null ] }, - "hash": "55002cccf17e32af5b077a17707122cfeeeebf5a9392c6798a486274d8d233d5" + "hash": "ef57c1622cc57e4485ffe086dd55f62df8b7f510b4c478818ff8ab1a11d7d5ea" } diff --git a/backend/windmill-api-inputs/src/lib.rs b/backend/windmill-api-inputs/src/lib.rs index 348b434eea..8764736e83 100644 --- a/backend/windmill-api-inputs/src/lib.rs +++ b/backend/windmill-api-inputs/src/lib.rs @@ -246,10 +246,11 @@ async fn get_args_from_history_or_saved_input( let result_o = if let Some(input) = g.input { if input { sqlx::query_scalar!( - "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2", + "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2 AND (is_public IS true OR created_by = $4)", job_or_input_id, w_id, - g.allow_large.unwrap_or(true) + g.allow_large.unwrap_or(true), + authed.username ) .fetch_optional(&mut *tx) .await? @@ -267,10 +268,11 @@ async fn get_args_from_history_or_saved_input( sqlx::query_scalar!( "SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM v2_job WHERE id = $1 AND workspace_id = $2 UNION ALL - SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2", + SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2 AND (is_public IS true OR created_by = $4)", job_or_input_id, w_id, - g.allow_large.unwrap_or(true) + g.allow_large.unwrap_or(true), + authed.username ) .fetch_optional(&mut *tx) .await?