mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
fix: add CORS headers to static assets for iframe context sharing (#7454)
* fix: add CORS headers to static assets for iframe context sharing Add Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy, and Cross-Origin-Resource-Policy headers to static assets served by the backend to match the vite preview config. This enables the TypeScript worker to work correctly when ui_builder is loaded in an iframe. Fixes #7453 Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> * fix: add CORS headers only for /apps_raw paths and force reload on navigation - Only add Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy, and Cross-Origin-Resource-Policy headers for /apps_raw/ paths (not all static assets) - Add frontend navigation handler to force page reload when navigating from non-apps_raw paths to /apps_raw/add or /apps_raw/edit - This ensures the TypeScript worker works correctly while avoiding CORS issues for external iframe embeds in other parts of the app Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com> 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: also reload when navigating from /apps/get_raw/ to apps_raw editor When viewing a raw app at /apps/get_raw/, the cross-origin isolation headers are not present. Force a page reload when navigating from this path to /apps_raw/add or /apps_raw/edit to ensure the headers are fetched from the server. Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com> * Update +layout.svelte --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
@@ -19,7 +19,7 @@ use mime_guess::mime;
|
||||
#[cfg(feature = "static_frontend")]
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
// Content Security Policy configuration
|
||||
// Content Security Policy configuration
|
||||
#[cfg(feature = "static_frontend")]
|
||||
lazy_static::lazy_static! {
|
||||
static ref CSP_POLICY: String = std::env::var("CSP_POLICY").unwrap_or_default();
|
||||
@@ -38,15 +38,24 @@ pub struct StaticFile(Uri);
|
||||
|
||||
impl IntoResponse for StaticFile {
|
||||
fn into_response(self) -> Response<Body> {
|
||||
let path = self.0.path().trim_start_matches('/');
|
||||
serve_path(path)
|
||||
let original_path = self.0.path();
|
||||
let path = original_path.trim_start_matches('/');
|
||||
serve_path(path, original_path)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "static_frontend")]
|
||||
const TWO_HUNDRED: &str = "200.html";
|
||||
|
||||
fn serve_path(path: &str) -> Response<Body> {
|
||||
/// Check if the original path requires cross-origin isolation headers
|
||||
/// These headers are needed for SharedArrayBuffer and TypeScript workers
|
||||
/// Only enabled for /apps_raw paths (raw app editor)
|
||||
#[cfg(feature = "static_frontend")]
|
||||
fn needs_cross_origin_isolation(original_path: &str) -> bool {
|
||||
original_path.starts_with("/apps_raw/")
|
||||
}
|
||||
|
||||
fn serve_path(path: &str, original_path: &str) -> Response<Body> {
|
||||
if path.starts_with("api/") {
|
||||
return Response::builder().status(404).body(Body::empty()).unwrap();
|
||||
}
|
||||
@@ -59,7 +68,16 @@ fn serve_path(path: &str) -> Response<Body> {
|
||||
let mut res = Response::builder()
|
||||
.header(header::CONTENT_TYPE, mime.as_ref())
|
||||
.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
|
||||
|
||||
// Add cross-origin isolation headers only for paths that need them
|
||||
// (apps_raw editor needs SharedArrayBuffer for TypeScript workers)
|
||||
if needs_cross_origin_isolation(original_path) {
|
||||
res = res
|
||||
.header("Cross-Origin-Opener-Policy", "same-origin")
|
||||
.header("Cross-Origin-Embedder-Policy", "require-corp")
|
||||
.header("Cross-Origin-Resource-Policy", "cross-origin");
|
||||
}
|
||||
|
||||
// Add Content-Security-Policy header for static assets when policy is set
|
||||
if !CSP_POLICY.is_empty() {
|
||||
if let Ok(header_value) = HeaderValue::try_from(CSP_POLICY.as_str()) {
|
||||
@@ -84,11 +102,12 @@ fn serve_path(path: &str) -> Response<Body> {
|
||||
None if path.starts_with("_app/") => {
|
||||
Response::builder().status(404).body(Body::empty()).unwrap()
|
||||
}
|
||||
None => serve_path(TWO_HUNDRED),
|
||||
None => serve_path(TWO_HUNDRED, original_path),
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "static_frontend"))]
|
||||
{
|
||||
let _ = original_path; // suppress unused warning
|
||||
Response::builder().status(404).body(Body::empty()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,8 +113,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
beforeNavigate(() => {
|
||||
beforeNavigate((navigation) => {
|
||||
menuOpen = false
|
||||
|
||||
// Force page reload when navigating to /apps_raw/add or /apps_raw/edit
|
||||
// This ensures the cross-origin isolation headers are fetched from the server
|
||||
// which are required for SharedArrayBuffer and TypeScript workers to work correctly
|
||||
const toPath = navigation.to?.url.pathname
|
||||
if (
|
||||
toPath &&
|
||||
(toPath.startsWith('/apps_raw/add') || toPath.startsWith('/apps_raw/edit'))
|
||||
) {
|
||||
const currentPath = navigation.from?.url.pathname
|
||||
// Reload if we're not on an apps_raw path, or if we're on /apps/get_raw/ (viewing a raw app)
|
||||
// The /apps/get_raw/ path doesn't have cross-origin isolation headers, so we need to reload
|
||||
if (!currentPath?.startsWith('/apps_raw/') || currentPath?.startsWith('/apps_raw/get/')) {
|
||||
navigation.cancel()
|
||||
window.location.href = navigation.to!.url.href
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let innerWidth = $state(BROWSER ? window.innerWidth : 2000)
|
||||
|
||||
Reference in New Issue
Block a user