From 298a77888976795516fe11b2bc390a88b3e75f7b Mon Sep 17 00:00:00 2001 From: "DESKTOP-RTLN3BA\\$punk" Date: Tue, 13 Oct 2026 22:34:28 -0700 Subject: [PATCH] fix(plugins): read the first-party list as UTF-8 on every system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The list was read in the system's code page. Its about texts hold curly apostrophes, so the packaged app on Windows showed "site’s" in Western code pages and could not read the list at all on cp932 or cp936 systems: the Plugins screen, connecting SurfSense's plugins and adding a custom one all failed. The test writes the apostrophe as raw UTF-8 and fails on a cp1252 Windows machine without the fix. --- .../lists/first_party/first_party_list.py | 4 +++- .../unit/plugins/lists/test_first_party_list.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/surfsense_local/backend/modules/plugins/lists/first_party/first_party_list.py b/surfsense_local/backend/modules/plugins/lists/first_party/first_party_list.py index c47c5dc9b..bb6e6285f 100644 --- a/surfsense_local/backend/modules/plugins/lists/first_party/first_party_list.py +++ b/surfsense_local/backend/modules/plugins/lists/first_party/first_party_list.py @@ -21,7 +21,9 @@ def icons_beside(path: Path) -> Path: def checked_entries(path: Path) -> list[ListEntry]: """Every entry, or a ValueError naming the entry and the field that is wrong.""" entries: list[ListEntry] = [] - for raw in json.loads(path.read_text())["plugins"]: + # UTF-8 whatever the system's code page: Windows would read the list's curly + # apostrophes as cp1252, and fail on a cp932 or cp936 system. + for raw in json.loads(path.read_text(encoding="utf-8"))["plugins"]: name = raw.get("id", "?") try: entry = ListEntry.model_validate(raw) diff --git a/surfsense_local/backend/tests/unit/plugins/lists/test_first_party_list.py b/surfsense_local/backend/tests/unit/plugins/lists/test_first_party_list.py index d88be0904..d7c04562b 100644 --- a/surfsense_local/backend/tests/unit/plugins/lists/test_first_party_list.py +++ b/surfsense_local/backend/tests/unit/plugins/lists/test_first_party_list.py @@ -38,6 +38,22 @@ def listed(tmp_path: Path, *entries: dict[str, Any]) -> Path: return path +def test_the_list_is_read_as_utf_8_whatever_the_system_code_page( + tmp_path: Path, +) -> None: + """The shipped list holds curly apostrophes, and Windows reads in its code page unless told.""" + about = "Read a site\u2019s pages as clean text." + path = tmp_path / "plugins.json" + path.write_bytes( + json.dumps( + {"schema_version": 1, "plugins": [scrapers(about=about)]}, + ensure_ascii=False, + ).encode("utf-8") + ) + + assert checked_entries(path)[0].about == about + + def test_the_packaged_list_passes_its_check() -> None: """A bad entry fails CI here, not on a user's machine.