diff --git a/backend/windmill-api-workspaces/src/workspaces.rs b/backend/windmill-api-workspaces/src/workspaces.rs index cbcb6f7953..8ab82fafca 100644 --- a/backend/windmill-api-workspaces/src/workspaces.rs +++ b/backend/windmill-api-workspaces/src/workspaces.rs @@ -2433,6 +2433,9 @@ async fn get_datatable_schema( FROM pg_namespace WHERE nspname NOT IN ('information_schema', 'pg_toast', 'pg_catalog') AND nspname NOT LIKE 'pg_%' + -- Only what this connection's role can reach: a schema it cannot + -- enter would list no tables and read as an empty one. + AND has_schema_privilege(oid, 'USAGE, CREATE') ORDER BY nspname "#, &[], @@ -2524,6 +2527,9 @@ async fn get_datatable_tables( FROM pg_namespace WHERE nspname NOT IN ('information_schema', 'pg_toast', 'pg_catalog') AND nspname NOT LIKE 'pg_%' + -- Only what this connection's role can reach: a schema it cannot + -- enter would list no tables and read as an empty one. + AND has_schema_privilege(oid, 'USAGE, CREATE') ORDER BY nspname "#, &[], diff --git a/backend/windmill-common/src/query_builders.rs b/backend/windmill-common/src/query_builders.rs index 60d74f189d..ecbb23ebba 100644 --- a/backend/windmill-common/src/query_builders.rs +++ b/backend/windmill-common/src/query_builders.rs @@ -2598,7 +2598,10 @@ WHERE table_catalog = current_database()", ) } else { ( - "\nWHERE c.relkind = 'r' AND a.attnum > 0 AND NOT a.attisdropped\n AND ns.nspname != 'pg_catalog' AND ns.nspname != 'information_schema'".to_string(), + // pg_catalog is readable by everyone, so without the + // privilege check this lists tables of schemas the + // connection's role cannot even enter. + "\nWHERE c.relkind = 'r' AND a.attnum > 0 AND NOT a.attisdropped\n AND ns.nspname != 'pg_catalog' AND ns.nspname != 'information_schema'\n AND has_schema_privilege(ns.oid, 'USAGE, CREATE')".to_string(), ",\n ns.nspname AS schema_name,\n c.relname AS table_name".to_string(), "\nJOIN pg_catalog.pg_class c ON a.attrelid = c.oid\nJOIN pg_catalog.pg_namespace ns ON c.relnamespace = ns.oid".to_string(), "ns.nspname, c.relname, a.attnum".to_string(), @@ -4468,6 +4471,9 @@ mod tests { assert!(sql.contains("schema_name")); assert!(sql.contains("table_name")); assert!(sql.contains("c.relkind = 'r'")); + // Listing every table must not reach into schemas the connection's role + // cannot enter: pg_catalog itself is readable by everyone. + assert!(sql.contains("has_schema_privilege(ns.oid, 'USAGE, CREATE')")); } #[test]