diff --git a/furtka/api.py b/furtka/api.py
index c461cd8..e7da027 100644
--- a/furtka/api.py
+++ b/furtka/api.py
@@ -12,6 +12,7 @@ out at the top. Auth lands when Authentik does.
"""
import json
+import re
from http.server import BaseHTTPRequestHandler, HTTPServer
from furtka import dockerops, installer, reconciler
@@ -19,6 +20,47 @@ from furtka.manifest import ManifestError, load_manifest
from furtka.paths import apps_dir, bundled_apps_dir
from furtka.scanner import scan
+_ICON_MAX_BYTES = 16 * 1024
+_UNSAFE_SVG_PATTERNS = (
+ re.compile(r"")
+ assert api._read_icon_svg(tmp_path, "icon.svg") is None
+
+
+def test_read_icon_svg_rejects_event_handler(tmp_path):
+ _write_icon(tmp_path, '')
+ assert api._read_icon_svg(tmp_path, "icon.svg") is None
+
+
+def test_read_icon_svg_rejects_javascript_url(tmp_path):
+ _write_icon(tmp_path, '')
+ assert api._read_icon_svg(tmp_path, "icon.svg") is None
+
+
+def test_list_bundled_inlines_icon_svg(fake_dirs):
+ _, bundled = fake_dirs
+ app = _write_bundled(bundled, "fileshare")
+ _write_icon(app, _SIMPLE_SVG)
+ [entry] = api._list_bundled()
+ assert entry["icon_svg"] == _SIMPLE_SVG
+
+
+def test_list_installed_inlines_icon_svg(fake_dirs, no_docker):
+ apps, bundled = fake_dirs
+ app = _write_bundled(bundled, "fileshare", env_example="A=real")
+ _write_icon(app, _SIMPLE_SVG)
+ api._do_install("fileshare")
+ [entry] = api._list_installed()
+ assert entry["icon_svg"] == _SIMPLE_SVG
+
+
def test_list_bundled_hides_already_installed(fake_dirs, no_docker):
apps, bundled = fake_dirs
_write_bundled(bundled, "fileshare", env_example="A=real")