RAG preview: serve preview files frame-safe for the native PDF viewer

This commit is contained in:
Roland Tannous 2026-06-03 12:01:54 +04:00
commit 0930e08496
2 changed files with 24 additions and 1 deletions

View file

@ -440,6 +440,18 @@ _CSP_SCRIPT_NONCE_HEADER = "x-internal-script-nonce"
_ARTIFACT_PREVIEW_FRAME_PATH = "/api/inference/artifact-preview-frame"
def _is_frameable_path(path: str) -> bool:
"""Paths allowed to be embedded in a same-origin / Tauri iframe. These
responses set their own CSP frame-ancestors; X-Frame-Options: DENY would
override that, so it is skipped for them."""
if path == _ARTIFACT_PREVIEW_FRAME_PATH:
return True
# RAG PDF preview is shown in the browser's native viewer inside an iframe.
return path.startswith("/api/rag/documents/") and path.endswith(
("/file", "/file-signed")
)
# /content is Colab's working directory — more reliable than env vars which
# aren't always set depending on Colab runtime version.
import importlib.util as _importlib_util
@ -510,7 +522,7 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
response.headers.setdefault("Content-Security-Policy", _build_csp(nonce))
# Omit X-Frame-Options in Colab — CSP frame-ancestors handles it, and
# DENY would block serve_kernel_port_as_iframe regardless of CSP.
if not _IS_COLAB and request.url.path != _ARTIFACT_PREVIEW_FRAME_PATH:
if not _IS_COLAB and not _is_frameable_path(request.url.path):
response.headers.setdefault("X-Frame-Options", "DENY")
response.headers.setdefault("X-Content-Type-Options", "nosniff")
response.headers.setdefault("Referrer-Policy", "no-referrer")

View file

@ -1181,6 +1181,12 @@ def _iter_file_range(path: Path, start: int, end: int):
yield chunk
# Origins allowed to embed the preview file in an iframe: the web app (same
# origin) and the desktop Tauri webview. Mirrors routes/inference.py's
# artifact-preview frame ancestors.
_PREVIEW_FRAME_ANCESTORS = "'self' tauri://localhost http://tauri.localhost"
def _serve_document_file_row(
doc_row: Any,
document_id: str,
@ -1194,6 +1200,11 @@ def _serve_document_file_row(
"X-Content-Type-Options": "nosniff",
"Cache-Control": "private, max-age=0, must-revalidate",
"Accept-Ranges": "bytes",
# The PDF preview embeds this file in a same-origin / Tauri iframe shown
# in the browser's native viewer. The global SecurityHeadersMiddleware
# sets frame-ancestors 'none'; override it here (and this path is exempt
# from X-Frame-Options: DENY in main.py) so the iframe can load.
"Content-Security-Policy": f"frame-ancestors {_PREVIEW_FRAME_ANCESTORS}",
}
size = resolved.stat().st_size