Wrap raw Jinja HTML in (raw! "...") for sx source embedding

Post edit, data, entries, and settings pages pass raw Jinja HTML
as content to full_page_sx/oob_page_sx, which wraps it in SxExpr().
This injects unescaped HTML directly into sx source, breaking the
parser. Fix by serializing the HTML into a (raw! "...") expression
that the sx evaluator renders unescaped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 15:16:42 +00:00
parent 48696498ef
commit 0ef4a93a92

View File

@@ -13,6 +13,7 @@ from typing import Any
from markupsafe import escape
from shared.sx.jinja_bridge import load_service_components
from shared.sx.parser import serialize as sx_serialize
from shared.sx.helpers import (
SxExpr, sx_call,
call_url, get_asset_url,
@@ -1341,13 +1342,13 @@ async def render_post_data_page(ctx: dict) -> str:
post_hdr = _post_header_sx(ctx)
admin_hdr = _post_admin_header_sx(ctx, selected="data")
header_rows = "(<> " + root_hdr + " " + post_hdr + ")" + admin_hdr
content = ctx.get("data_html", "")
content = _raw_html_sx(ctx.get("data_html", ""))
return full_page_sx(ctx, header_rows=header_rows, content=content)
async def render_post_data_oob(ctx: dict) -> str:
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="data")
content = ctx.get("data_html", "")
content = _raw_html_sx(ctx.get("data_html", ""))
return oob_page_sx(oobs=admin_hdr_oob, content=content)
@@ -1358,31 +1359,37 @@ async def render_post_entries_page(ctx: dict) -> str:
post_hdr = _post_header_sx(ctx)
admin_hdr = _post_admin_header_sx(ctx, selected="entries")
header_rows = "(<> " + root_hdr + " " + post_hdr + ")" + admin_hdr
content = ctx.get("entries_html", "")
content = _raw_html_sx(ctx.get("entries_html", ""))
return full_page_sx(ctx, header_rows=header_rows, content=content)
async def render_post_entries_oob(ctx: dict) -> str:
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="entries")
content = ctx.get("entries_html", "")
content = _raw_html_sx(ctx.get("entries_html", ""))
return oob_page_sx(oobs=admin_hdr_oob, content=content)
# ---- Post edit ----
def _raw_html_sx(html: str) -> str:
"""Wrap raw HTML in (raw! "...") so it's valid inside sx source."""
if not html:
return ""
return "(raw! " + sx_serialize(html) + ")"
async def render_post_edit_page(ctx: dict) -> str:
root_hdr = root_header_sx(ctx)
post_hdr = _post_header_sx(ctx)
admin_hdr = _post_admin_header_sx(ctx, selected="edit")
header_rows = "(<> " + root_hdr + " " + post_hdr + ")" + admin_hdr
content = ctx.get("edit_html", "")
body_end = ctx.get("body_end_html", "")
content = _raw_html_sx(ctx.get("edit_html", ""))
return full_page_sx(ctx, header_rows=header_rows, content=content)
async def render_post_edit_oob(ctx: dict) -> str:
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="edit")
content = ctx.get("edit_html", "")
content = _raw_html_sx(ctx.get("edit_html", ""))
return oob_page_sx(oobs=admin_hdr_oob, content=content)
@@ -1393,13 +1400,13 @@ async def render_post_settings_page(ctx: dict) -> str:
post_hdr = _post_header_sx(ctx)
admin_hdr = _post_admin_header_sx(ctx, selected="settings")
header_rows = "(<> " + root_hdr + " " + post_hdr + ")" + admin_hdr
content = ctx.get("settings_html", "")
content = _raw_html_sx(ctx.get("settings_html", ""))
return full_page_sx(ctx, header_rows=header_rows, content=content)
async def render_post_settings_oob(ctx: dict) -> str:
admin_hdr_oob = _post_admin_header_sx(ctx, oob=True, selected="settings")
content = ctx.get("settings_html", "")
content = _raw_html_sx(ctx.get("settings_html", ""))
return oob_page_sx(oobs=admin_hdr_oob, content=content)