"""Tests for opt-in WebUI extension hooks. The extension surface must stay deliberately small and safe: - disabled unless configured by environment - same-origin script/style URLs only - no filesystem path leakage in public config - static assets sandboxed to the configured extension directory """ from types import SimpleNamespace class FakeHandler: def __init__(self): self.status = None self.headers = {} self.sent_headers = [] self.body = bytearray() self.wfile = self def send_response(self, status): self.status = status def send_header(self, name, value): self.sent_headers.append((name, value)) def end_headers(self): pass def write(self, data): self.body.extend(data) def header(self, name): for key, value in self.sent_headers: if key.lower() == name.lower(): return value return None def test_extension_config_disabled_by_default(monkeypatch): monkeypatch.delenv("HERMES_WEBUI_EXTENSION_DIR", raising=False) monkeypatch.delenv("HERMES_WEBUI_EXTENSION_SCRIPT_URLS", raising=False) monkeypatch.delenv("HERMES_WEBUI_EXTENSION_STYLESHEET_URLS", raising=False) from api.extensions import get_extension_config assert get_extension_config() == { "enabled": False, "script_urls": [], "stylesheet_urls": [], } def test_extension_config_accepts_only_safe_same_origin_urls(tmp_path, monkeypatch): root = tmp_path / "extensions" root.mkdir() monkeypatch.setenv("HERMES_WEBUI_EXTENSION_DIR", str(root)) monkeypatch.setenv( "HERMES_WEBUI_EXTENSION_SCRIPT_URLS", ", ".join( [ "/extensions/app.js", "https://example.com/evil.js", "//example.com/evil.js", "javascript:alert(1)", "/api/session", "/extensions/../api/session", "/extensions/%2e%2e/api/session", "/extensions/%252e%252e/api/session", "/static/../api/session", ] ), ) monkeypatch.setenv( "HERMES_WEBUI_EXTENSION_STYLESHEET_URLS", "/extensions/app.css, /static/theme.css, data:text/css,body{}", ) from api.extensions import get_extension_config assert get_extension_config() == { "enabled": True, "script_urls": ["/extensions/app.js"], "stylesheet_urls": ["/extensions/app.css", "/static/theme.css"], } def test_index_html_injection_escapes_urls_and_preserves_disabled_default(tmp_path, monkeypatch): monkeypatch.delenv("HERMES_WEBUI_EXTENSION_DIR", raising=False) monkeypatch.setenv("HERMES_WEBUI_EXTENSION_SCRIPT_URLS", "/extensions/app.js") from api.extensions import inject_extension_tags html = "
" assert inject_extension_tags(html) == html root = tmp_path / "extensions" root.mkdir() monkeypatch.setenv("HERMES_WEBUI_EXTENSION_DIR", str(root)) monkeypatch.setenv("HERMES_WEBUI_EXTENSION_SCRIPT_URLS", "/extensions/app.js?v=1&mode=dev") monkeypatch.setenv("HERMES_WEBUI_EXTENSION_STYLESHEET_URLS", "/extensions/app.css") injected = inject_extension_tags(html) assert '' in injected assert '' in injected assert injected.index("/extensions/app.css") < injected.index("") assert injected.index("/extensions/app.js") < injected.index("