mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
fix: restrict logout redirect to whitelisted domains (#8524)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Generated
+1
@@ -16386,6 +16386,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tower-cookies",
|
||||
"tracing",
|
||||
"url",
|
||||
"windmill-api-auth",
|
||||
"windmill-audit",
|
||||
"windmill-common",
|
||||
|
||||
@@ -34,3 +34,4 @@ time.workspace = true
|
||||
tokio.workspace = true
|
||||
tower-cookies.workspace = true
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
@@ -49,13 +49,13 @@ use windmill_common::users::truncate_token;
|
||||
use windmill_common::users::COOKIE_NAME;
|
||||
use windmill_common::utils::paginate;
|
||||
use windmill_common::worker::CLOUD_HOSTED;
|
||||
use windmill_common::BASE_URL;
|
||||
use windmill_common::{
|
||||
auth::{get_folders_for_user, get_groups_for_user},
|
||||
db::UserDB,
|
||||
error::{self, Error, JsonResult, Result},
|
||||
utils::{not_found_if_none, rd_string, require_admin, Pagination, StripPath},
|
||||
};
|
||||
use windmill_common::{BASE_URL, HUB_BASE_URL};
|
||||
use windmill_git_sync::handle_deployment_metadata;
|
||||
|
||||
const COOKIE_PATH: &str = "/";
|
||||
@@ -577,12 +577,44 @@ async fn logout(
|
||||
}
|
||||
tx.commit().await?;
|
||||
if let Some(rd) = rd {
|
||||
Ok((StatusCode::TEMPORARY_REDIRECT, [(LOCATION, rd)]).into_response())
|
||||
if is_valid_logout_redirect(&rd).await {
|
||||
Ok((StatusCode::TEMPORARY_REDIRECT, [(LOCATION, rd)]).into_response())
|
||||
} else {
|
||||
tracing::warn!("Blocked logout redirect to non-whitelisted URL: {}", rd);
|
||||
Ok((StatusCode::OK, "logged out successfully".to_string()).into_response())
|
||||
}
|
||||
} else {
|
||||
Ok((StatusCode::OK, "logged out successfully".to_string()).into_response())
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_valid_logout_redirect(rd: &str) -> bool {
|
||||
// Allow relative paths (same-origin redirects)
|
||||
if rd.starts_with('/') && !rd.starts_with("//") {
|
||||
return true;
|
||||
}
|
||||
let parsed = match url::Url::parse(rd) {
|
||||
Ok(u) => u,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let host: &str = match parsed.host_str() {
|
||||
Some(h) => h,
|
||||
None => return false,
|
||||
};
|
||||
if host == "windmill.dev" || host.ends_with(".windmill.dev") {
|
||||
return true;
|
||||
}
|
||||
let hub_url = HUB_BASE_URL.read().await.clone();
|
||||
if let Ok(hub_parsed) = url::Url::parse(&hub_url) {
|
||||
if let Some(hub_host) = hub_parsed.host_str() {
|
||||
if host == hub_host {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
async fn whoami(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { get } from 'svelte/store'
|
||||
import { hubBaseUrlStore } from './stores'
|
||||
|
||||
export function isValidLogoutRedirect(url: string): boolean {
|
||||
if (url.startsWith('/') && !url.startsWith('//')) {
|
||||
return true
|
||||
}
|
||||
try {
|
||||
const parsed = new URL(url)
|
||||
const host = parsed.hostname
|
||||
if (host === 'windmill.dev' || host.endsWith('.windmill.dev')) {
|
||||
return true
|
||||
}
|
||||
const hubBaseUrl = get(hubBaseUrlStore)
|
||||
try {
|
||||
const hubHost = new URL(hubBaseUrl).hostname
|
||||
if (host === hubHost) {
|
||||
return true
|
||||
}
|
||||
} catch {}
|
||||
} catch {}
|
||||
return false
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
import { page } from '$app/state'
|
||||
import CenteredModal from '$lib/components/CenteredModal.svelte'
|
||||
import { clearUser } from '$lib/logout'
|
||||
import { isValidLogoutRedirect } from '$lib/logoutRedirect'
|
||||
import { userStore } from '$lib/stores'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
@@ -29,7 +30,11 @@
|
||||
return
|
||||
}
|
||||
|
||||
window.location.href = rd ?? '/user/login'
|
||||
if (rd && isValidLogoutRedirect(rd)) {
|
||||
window.location.href = rd
|
||||
} else {
|
||||
window.location.href = '/user/login'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user