diff --git a/claude_code_log/html/assistant_formatters.py b/claude_code_log/html/assistant_formatters.py
index 3128a50c..02295cf9 100644
--- a/claude_code_log/html/assistant_formatters.py
+++ b/claude_code_log/html/assistant_formatters.py
@@ -108,7 +108,10 @@ def format_image_content(image: ImageContent) -> str:
src = export_image(image, mode="embedded")
if src is None:
return "[Image]"
- return f'
'
+ # Escape the src: export_image allowlists the media type and
+ # validates the base64, but the data: URL still must not be able to
+ # break out of the attribute (issue #277).
+ return f'
'
def format_unknown_content(content: UnknownMessage) -> str:
diff --git a/claude_code_log/html/renderer.py b/claude_code_log/html/renderer.py
index 92900896..3c8c4d55 100644
--- a/claude_code_log/html/renderer.py
+++ b/claude_code_log/html/renderer.py
@@ -473,7 +473,10 @@ def _format_image(self, image: ImageContent) -> str:
)
if src is None:
return "[Image]"
- return f'
'
+ # Escape the src: export_image allowlists the media type and
+ # validates the base64, but the data: URL (embedded mode) still
+ # must not be able to break out of the attribute (issue #277).
+ return f'
'
# -------------------------------------------------------------------------
# System Content Formatters
diff --git a/claude_code_log/image_export.py b/claude_code_log/image_export.py
index 1113ac9a..089e2d58 100644
--- a/claude_code_log/image_export.py
+++ b/claude_code_log/image_export.py
@@ -13,6 +13,47 @@
from .models import ImageContent
+# Image media types we are willing to emit into a data: URL or write to
+# disk. Deliberately excludes ``image/svg+xml`` — SVG can carry inline
+# ``')
+ assert export_image(hostile, mode="embedded") is None
+
+ def test_embedded_rejects_svg(self):
+ """SVG is scriptable; embedded mode must not emit a data:image/svg+xml URL."""
+ svg = _image("image/svg+xml")
+ assert export_image(svg, mode="embedded") is None
+
+ def test_embedded_rejects_invalid_base64(self):
+ """Malformed base64 data is rejected rather than emitted verbatim."""
+ bad = _image("image/png", data='not"base64>')
+ html = format_image_content(hostile)
+ assert "