Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions assets/gui/css/advanced-material-alas.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
:root {
/* === 色彩系统 === */
--alas-apple-bg: #f5f5f7;
/* 主题样式通过 WebSocket 内联注入,资源路径相对页面解析。 */
--alas-apple-bg-image: url("static/assets/gui/css/bj.jpg");
--alas-apple-bg-image: url("https://api.yppp.net/api.php");
--alas-apple-bg-noise: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
--alas-apple-card-bg: rgba(255, 255, 255, 0.72);
--alas-apple-card-bg-solid: #ffffff;
Expand Down
11 changes: 2 additions & 9 deletions tests/test_webui_static_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def test_default_pywebio_assets_are_self_hosted(self):
self.assertIn('href="pywebio_static/css/app.css?v=', response.text)
self.assertNotIn("cdn.jsdelivr.net", response.text)

def test_theme_assets_do_not_depend_on_external_font_services(self):
def test_theme_keeps_random_background_without_external_font_services(self):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): 建议增加一个检查,确保除了允许的随机背景 API 外,没有引入其他外部资源主机。

目前,这个测试验证了随机背景使用 https://api.yppp.net/api.php,并且避免了外部字体服务。为了更好地落实「自托管资源 + 单一白名单 API」的策略,可以考虑增加一个断言,检查 theme_css/obs_overlay 中除了 api.yppp.net 之外不包含其他 http/https URL(例如,通过提取 url(...) 的值,或扫描 http 字样,并只允许白名单主机或相对 URL)。

建议实现:

    def test_theme_keeps_random_background_without_external_font_services(self):
        theme_css = (PROJECT_ROOT / "assets/gui/css/advanced-material-alas.css").read_text(
            encoding="utf-8"
        )

        # 确保主题 CSS 不依赖外部字体服务
        self.assertNotIn("fonts.googleapis.com", theme_css)
        self.assertNotIn("fonts.gstatic.com", theme_css)

        # 随机背景必须使用白名单中的 API
        self.assertIn('url("https://api.yppp.net/api.php")', theme_css)

        # 确保 OBS overlay 不依赖外部字体服务
        self.assertNotIn("fonts.googleapis.com", obs_overlay)
        self.assertNotIn("fonts.gstatic.com", obs_overlay)

        # 强制执行「自托管资源 + 单一白名单 API」策略:
        # 任何绝对 http/https URL 必须指向 api.yppp.net;相对 URL 是允许的。
        def assert_only_whitelisted_external_host(css_text: str) -> None:
            # 查找 CSS 中所有的 http/https URL(例如在 url(...) 内或其他位置)
            for match in re.finditer(r"https?://([^/]+)/[^\s\"')]*", css_text):
                host = match.group(1)
                self.assertEqual(
                    "api.yppp.net",
                    host,
                    msg=f"Unexpected external asset host {host!r} in CSS: {match.group(0)!r}",
                )

        assert_only_whitelisted_external_host(theme_css)
        assert_only_whitelisted_external_host(obs_overlay)

为了让这个测试生效,请确保在 tests/test_webui_static_assets.py 顶部已导入 re

  1. 在现有的标准库导入(例如已有的 import pathlibimport os 等)附近添加 import re
  2. 确认 obs_overlay 已在测试模块中提前定义(看起来已经在使用;如果它已经被设置为 overlay 的 CSS 内容,则无需更改)。
Original comment in English

suggestion (testing): Consider adding a check that no other external asset hosts are introduced besides the allowed random background API.

Currently, the test confirms the random background uses https://api.yppp.net/api.php and that external font services are avoided. To better enforce the “self-hosted assets + one whitelisted API” policy, consider adding an assertion that theme_css/obs_overlay contain no other http/https URLs besides api.yppp.net (e.g., by extracting url(...) values or scanning for http and allowing only the whitelisted host or relative URLs).

Suggested implementation:

    def test_theme_keeps_random_background_without_external_font_services(self):
        theme_css = (PROJECT_ROOT / "assets/gui/css/advanced-material-alas.css").read_text(
            encoding="utf-8"
        )

        # Ensure we don't depend on external font services in the theme CSS
        self.assertNotIn("fonts.googleapis.com", theme_css)
        self.assertNotIn("fonts.gstatic.com", theme_css)

        # Random background must use the whitelisted API
        self.assertIn('url("https://api.yppp.net/api.php")', theme_css)

        # Ensure we don't depend on external font services in the OBS overlay
        self.assertNotIn("fonts.googleapis.com", obs_overlay)
        self.assertNotIn("fonts.gstatic.com", obs_overlay)

        # Enforce "self-hosted assets + one whitelisted API" policy:
        # any absolute http/https URL must point to api.yppp.net; relative URLs are allowed.
        def assert_only_whitelisted_external_host(css_text: str) -> None:
            # Find all http/https URLs in the CSS (e.g. inside url(...) or elsewhere)
            for match in re.finditer(r"https?://([^/]+)/[^\s\"')]*", css_text):
                host = match.group(1)
                self.assertEqual(
                    "api.yppp.net",
                    host,
                    msg=f"Unexpected external asset host {host!r} in CSS: {match.group(0)!r}",
                )

        assert_only_whitelisted_external_host(theme_css)
        assert_only_whitelisted_external_host(obs_overlay)

To make this test work, ensure re is imported at the top of tests/test_webui_static_assets.py:

  1. Add import re alongside the other standard-library imports (e.g., near existing import pathlib, import os, etc.).
  2. Confirm that obs_overlay is defined earlier in the test module (it appears to be used already; no changes needed if it's already set to the overlay CSS text).

theme_css = (PROJECT_ROOT / "assets/gui/css/advanced-material-alas.css").read_text(
encoding="utf-8"
)
Expand All @@ -83,13 +83,6 @@ def test_theme_assets_do_not_depend_on_external_font_services(self):

self.assertNotIn("fonts.googleapis.com", theme_css)
self.assertNotIn("fonts.gstatic.com", theme_css)
self.assertNotIn("api.yppp.net", theme_css)
self.assertIn('url("https://api.yppp.net/api.php")', theme_css)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): 对背景 API URL 的断言过于严格;可以考虑稍微放宽一些,同时仍然保证预期行为。

当前断言对 CSS 的具体序列化和路径进行了硬编码,因此小幅改动(例如增加查询参数、改变引号/空白)即使不影响行为,也会导致测试失败。可以考虑使用更灵活的匹配方式(例如检查 theme_css 是否在 url( 的上下文中包含 api.yppp.net,或通过正则表达式确保存在 https://api.yppp.net/ URL),从而在不依赖格式细节的前提下验证主机和协议约束。

建议实现:

        self.assertNotIn("fonts.googleapis.com", theme_css)
        self.assertNotIn("fonts.gstatic.com", theme_css)
        # 确保随机背景使用指向 api.yppp.net 的 HTTPS URL,而不依赖精确的 CSS 序列化格式
        self.assertRegex(
            theme_css,
            r'url\(\s*[\'"]https://api\.yppp\.net/[^\'"]*[\'"]\s*\)'
        )
        self.assertNotIn("fonts.googleapis.com", obs_overlay)

如果这个测试类目前没有继承自提供 assertRegex 方法的 unittest.TestCase 或其子类,你可能需要确保它这样做,或者导入 unittest 并作相应调整。该正则有意对 https://api.yppp.net/ 后面的路径和查询字符串保持宽松;如果你希望约束得更严格(例如只允许 /api.php),可以将 [^\'"]* 改成 api\.php[^\'"]*

Original comment in English

suggestion (testing): The assertion on the exact background API URL may be brittle; consider loosening it slightly while still enforcing the intended behavior.

This assertion hard-codes the exact CSS serialization and path, so minor changes (e.g. added query params, different quoting/whitespace) will break the test without changing behavior. Consider asserting on a more flexible pattern (e.g. checking that theme_css contains api.yppp.net with a url( guard, or using a regex to ensure an https://api.yppp.net/ URL) so the test validates the host/protocol constraint without depending on formatting details.

Suggested implementation:

        self.assertNotIn("fonts.googleapis.com", theme_css)
        self.assertNotIn("fonts.gstatic.com", theme_css)
        # Ensure the random background uses an HTTPS URL to api.yppp.net without depending on exact CSS serialization
        self.assertRegex(
            theme_css,
            r'url\(\s*[\'"]https://api\.yppp\.net/[^\'"]*[\'"]\s*\)'
        )
        self.assertNotIn("fonts.googleapis.com", obs_overlay)

If this test class does not already inherit from unittest.TestCase or a subclass that provides assertRegex, you may need to ensure it does so, or import unittest and adjust accordingly. The regex is intentionally permissive about the path and query string after https://api.yppp.net/; if you want to constrain it further (e.g. specifically /api.php), you can change [^\'"]* to api\.php[^\'"]*.

self.assertNotIn("fonts.googleapis.com", obs_overlay)
self.assertNotIn("fonts.gstatic.com", obs_overlay)
self.assertIn('url("static/assets/gui/css/bj.jpg")', theme_css)
self.assertEqual(
urljoin(
"https://example.test/azur/", "static/assets/gui/css/bj.jpg"
),
"https://example.test/azur/static/assets/gui/css/bj.jpg",
)
Loading