From 17f184820b26ae1c16bea09b80ed5f16010dbe24 Mon Sep 17 00:00:00 2001 From: ussoewwin <136552381+ussoewwin@users.noreply.github.com> Date: Mon, 14 Sep 2026 20:57:18 +0900 Subject: [PATCH] fix(logo): don't let a non-SVG / proxy error response break the logo route get_logo_svg() cached whatever LOGO_URL returned without validating it, and get_logo() then ran str.format() over it. Any literal '{' in the payload - an HTML error page from a proxy, captive portal or CDN, e.g. a Cloudflare 523 page whose CSS uses braces - raised ValueError: unexpected '{' in field name and broke the route for every request until restart. Validate that the fetched markup is SVG, fall back to the bundled web/common/media/rgthree.svg, substitute {bg}/{fg} with str.replace instead of str.format, and never serve non-SVG content from the route. --- py/pyproject.py | 24 ++++++++++++++++++++++-- py/server/routes_config.py | 7 ++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/py/pyproject.py b/py/pyproject.py index 26eab34..50a6f76 100644 --- a/py/pyproject.py +++ b/py/pyproject.py @@ -45,6 +45,19 @@ if not LOGO_URL.endswith('.svg'): raise ValueError('Bad logo url.') LOGO_SVG = None + + +def _local_logo_svg(): + """The logo bundled with this repository, used when the remote one is unavailable.""" + import os + try: + path = os.path.join(_THIS_DIR, '..', 'web', 'common', 'media', 'rgthree.svg') + with open(path, 'r', encoding='utf-8') as f: + return f.read() + except Exception: + return '' + + async def get_logo_svg(): import aiohttp global LOGO_SVG @@ -62,9 +75,16 @@ async def get_logo_svg(): 'Expires': '0' } async with session.get(LOGO_URL, headers=headers) as resp: - LOGO_SVG = await resp.text() + fetched = await resp.text() + # The response is not guaranteed to be our SVG: a proxy, a captive portal or a + # CDN error page answers with HTML (Cloudflare pages contain `{` in their CSS, + # which later blows up `str.format` in the route). Only accept real SVG markup + # and otherwise fall back to the bundled logo below. + if ']*?)width="[^\"]+"', r'\1', resp) if str(w).isnumeric():