From 3bba1c227059a44c7eeefa97b8c69a63bf4f3454 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Mon, 11 May 2026 01:56:02 +0900 Subject: [PATCH] feat: add IFRAME_CSP env var for srcdoc iframe content security policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an IFRAME_CSP environment variable that injects a Content-Security-Policy tag into all srcdoc iframes rendering untrusted content: - Artifacts (LLM-generated HTML previews) - FullHeightIframe (tool/embed output) - FilePreview (user-uploaded HTML files) - CitationModal (RAG document HTML) Shared utility in src/lib/utils/csp.ts handles injection with HTML-safe attribute escaping. URL-based iframes (src=) are correctly excluded. Env-var only — no PersistentConfig, no admin UI, no DB. Set once at deploy time, requires restart. Empty string (default) means no CSP restriction. --- backend/open_webui/config.py | 1 + backend/open_webui/main.py | 2 ++ src/lib/components/chat/Artifacts.svelte | 4 +++- src/lib/components/chat/FileNav/FilePreview.svelte | 5 +++-- .../chat/Messages/Citations/CitationModal.svelte | 5 +++-- src/lib/components/common/FullHeightIframe.svelte | 4 +++- src/lib/stores/index.ts | 1 + src/lib/utils/csp.ts | 14 ++++++++++++++ 8 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 src/lib/utils/csp.ts diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index dc61ad5cd7..6a29504cb6 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -1376,6 +1376,7 @@ RESPONSE_WATERMARK = PersistentConfig( os.environ.get('RESPONSE_WATERMARK', ''), ) +IFRAME_CSP = os.environ.get('IFRAME_CSP', '') USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS = ( os.environ.get('USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS', 'False').lower() == 'true' diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 52e59d26e0..4adece3825 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -460,6 +460,7 @@ from open_webui.config import ( OAUTH_PROVIDERS, WEBUI_URL, RESPONSE_WATERMARK, + IFRAME_CSP, # Admin ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_ANALYTICS, @@ -2444,6 +2445,7 @@ async def get_app_config(request: Request): 'pending_user_overlay_title': app.state.config.PENDING_USER_OVERLAY_TITLE, 'pending_user_overlay_content': app.state.config.PENDING_USER_OVERLAY_CONTENT, 'response_watermark': app.state.config.RESPONSE_WATERMARK, + 'iframe_csp': IFRAME_CSP, }, 'license_metadata': app.state.LICENSE_METADATA, **( diff --git a/src/lib/components/chat/Artifacts.svelte b/src/lib/components/chat/Artifacts.svelte index 7e0bdf2060..f8483d122e 100644 --- a/src/lib/components/chat/Artifacts.svelte +++ b/src/lib/components/chat/Artifacts.svelte @@ -7,12 +7,14 @@ import { artifactCode, chatId, + config, settings, showArtifacts, showControls, artifactContents } from '$lib/stores'; import { copyToClipboard, createMessagesList } from '$lib/utils'; + import { injectCsp } from '$lib/utils/csp'; import XMark from '../icons/XMark.svelte'; import ArrowsPointingOut from '../icons/ArrowsPointingOut.svelte'; @@ -242,7 +244,7 @@ {:else} diff --git a/src/lib/components/common/FullHeightIframe.svelte b/src/lib/components/common/FullHeightIframe.svelte index fe629c8b59..1bb18943a1 100644 --- a/src/lib/components/common/FullHeightIframe.svelte +++ b/src/lib/components/common/FullHeightIframe.svelte @@ -1,5 +1,7 @@