Closes the long-documented follow-up. Inline <script> and onclick handlers
inside the assistant's ```html fence were dead under the previous srcdoc
path because Chromium inherits the embedder CSP (script-src 'self') for
srcdoc / data: / blob: iframes per HTML / CSP3. The only browser-supported
escape is a same-origin URL whose response headers carry an overriding CSP.
Backend: new POST /api/preview/html stashes the source for 10 min behind a
192-bit random token; GET /api/preview/html/{id} serves the snippet with
default-src 'none' + script-src 'unsafe-inline' + frame-ancestors 'self' +
X-Frame-Options SAMEORIGIN so the host chat page can iframe it but third
parties cannot. The GET is intentionally unauthenticated because browsers
do not attach Authorization to iframe subresource loads -- the unguessable
URL token is the authorisation. Eviction caps the in-memory store at 256
entries per worker; TTL sweep runs on each access.
Frontend: HtmlPreview now POSTs the source on mount, holds about:blank
until the URL arrives, then sets iframe src to the returned path. The
iframe sandbox stays "allow-scripts allow-modals allow-popups" with NO
allow-same-origin / allow-top-navigation, so even though the URL is
same-origin the iframe document is treated as a unique opaque origin
(cannot reach parent storage / DOM, cannot navigate the host page).
A srcdoc fallback kicks in if the POST fails so the layout still renders.
Tests:
* 9 new backend cases pin auth gating on POST, the unauth GET path,
CSP shape, X-Frame-Options override, TTL expiry, oldest-first eviction,
and per-call token uniqueness.
* Frontend vitest mocks the fetch round-trip; two existing tests rewritten
to await data-preview-state=ready, plus a new failing-fetch case that
exercises the srcdoc fallback (so a future regression there is loud).
Updates the in-host-CSP comment in main.py to reflect that the
"same-origin backend route" follow-up is now landed.
34 lines
1.1 KiB
Python
34 lines
1.1 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
"""
|
|
API Routes
|
|
"""
|
|
|
|
from routes.training import router as training_router
|
|
from routes.models import router as models_router
|
|
from routes.inference import router as inference_router
|
|
from routes.inference import studio_router as inference_studio_router
|
|
from routes.datasets import router as datasets_router
|
|
from routes.auth import router as auth_router
|
|
from routes.data_recipe import router as data_recipe_router
|
|
from routes.export import router as export_router
|
|
from routes.training_history import router as training_history_router
|
|
from routes.chat_history import router as chat_history_router
|
|
from routes.providers import router as providers_router
|
|
from routes.html_preview import router as html_preview_router
|
|
|
|
__all__ = [
|
|
"training_router",
|
|
"models_router",
|
|
"inference_router",
|
|
"inference_studio_router",
|
|
"datasets_router",
|
|
"auth_router",
|
|
"data_recipe_router",
|
|
"export_router",
|
|
"training_history_router",
|
|
"chat_history_router",
|
|
"providers_router",
|
|
"html_preview_router",
|
|
]
|