From 30e48735e51433e118bc0ff4cfaf1873480fb9f8 Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Fri, 10 Oct 2025 20:53:30 +0200 Subject: [PATCH] fix: Safeguard prevents button from deleting non-fork workspaces (#6795) * Fix: Safeguard to prevent `delete fork` button from deleting non-fork workspaces * fix typo * Consistent prefix --- backend/windmill-api/openapi.yaml | 5 +++++ backend/windmill-api/src/workspaces_extra.rs | 14 ++++++++++++++ .../lib/components/sidebar/SidebarContent.svelte | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 4b342ca145..0732cbed26 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1718,6 +1718,11 @@ paths: - workspace parameters: - $ref: "#/components/parameters/WorkspaceId" + - name: only_delete_forks + in: query + required: false + schema: + type: boolean responses: "200": description: status diff --git a/backend/windmill-api/src/workspaces_extra.rs b/backend/windmill-api/src/workspaces_extra.rs index 02996c0d32..33ddbd17e5 100644 --- a/backend/windmill-api/src/workspaces_extra.rs +++ b/backend/windmill-api/src/workspaces_extra.rs @@ -3,6 +3,7 @@ use crate::db::ApiAuthed; use crate::workspaces::{check_w_id_conflict, CREATE_WORKSPACE_REQUIRE_SUPERADMIN, WM_FORK_PREFIX}; use crate::{db::DB, utils::require_super_admin}; +use axum::extract::Query; use axum::{ extract::{Extension, Path}, Json, @@ -415,10 +416,16 @@ pub(crate) async fn change_workspace_id( )) } +#[derive(Deserialize)] +pub(crate) struct DeleteWorkspaceQuery { + pub(crate) only_delete_forks: Option, +} + pub(crate) async fn delete_workspace( Extension(db): Extension, Path(w_id): Path, authed: ApiAuthed, + Query(dwq): Query, ) -> Result { let w_id = match w_id.as_str() { "starter" => Err(Error::BadRequest( @@ -429,6 +436,13 @@ pub(crate) async fn delete_workspace( )), _ => Ok(w_id), }?; + + if dwq.only_delete_forks.unwrap_or(false) && !w_id.starts_with(WM_FORK_PREFIX) { + return Err(Error::BadRequest( + "Cannot delete this workspace because it is not a workspace fork.".to_string(), + )); + } + let mut tx = db.begin().await?; if !(w_id.starts_with(WM_FORK_PREFIX) && is_workspace_owner(&authed, &w_id, &mut tx).await?) { require_super_admin(&db, &authed.email).await?; diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte index bdbb17ba3f..f0bba47a7b 100644 --- a/frontend/src/lib/components/sidebar/SidebarContent.svelte +++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte @@ -73,7 +73,7 @@ } async function deleteFork() { - await WorkspaceService.deleteWorkspace({ workspace: $workspaceStore ?? '' }) + await WorkspaceService.deleteWorkspace({ workspace: $workspaceStore ?? '', onlyDeleteForks: true }) sendUserToast('You deleted the workspace') clearStores() goto('/user/workspaces') @@ -364,7 +364,7 @@ } ] : []), - ...($workspaceStore?.startsWith('wm-fork') + ...($workspaceStore?.startsWith('wm-fork-') ? [ { label: 'Delete Forked Workspace',