From df6057bc9f3d0dc1de28e42a1de6396a254ffb43 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Mon, 26 May 2025 15:28:07 +0000 Subject: [PATCH] i18n: forbid "English" translations This adds an additional layer of defense. Potential attack: - "English" gets added as a language on the crowdin project - (this can only be done by a moderator or the crowdin devs) - malicious strings get added on crowdin as English translations - we don't notice and pull the updates into the locale git submodule - users with the "default" lang option selected who have their OS lang set to English - and users with "English" explicitly selected in Electrum - would see the malicious strings --- electrum/i18n.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/electrum/i18n.py b/electrum/i18n.py index 18df3ec1f..cc76e915a 100644 --- a/electrum/i18n.py +++ b/electrum/i18n.py @@ -33,9 +33,15 @@ from .logging import get_logger _logger = get_logger(__name__) LOCALE_DIR = os.path.join(os.path.dirname(__file__), 'locale', 'locale') + +def _get_null_translations(): + """Returns a gettext Translations obj with translations explicitly disabled.""" + return gettext.translation('electrum', fallback=True, class_=gettext.NullTranslations) + + # Set initial default language to None. i.e. translations explicitly disabled. # The main script or GUIs can call set_language to enable translations. -_language = gettext.translation('electrum', fallback=True, class_=gettext.NullTranslations) +_language = _get_null_translations() # note: do not use old-style (%) formatting inside translations, @@ -66,7 +72,13 @@ def _(msg: str, *, context=None) -> str: def set_language(x: Optional[str]) -> None: _logger.info(f"setting language to {x!r}") global _language - if x: + if not x: + return + if x.startswith("en_"): + # Setting the language to "English" is a protected special-case: + # we disable all translations and use the source strings. + _language = _get_null_translations() + else: _language = gettext.translation('electrum', LOCALE_DIR, fallback=True, languages=[x]) @@ -79,7 +91,7 @@ languages = { 'de_DE': _('German'), 'el_GR': _('Greek'), 'eo_UY': _('Esperanto'), - 'en_UK': _('English'), + 'en_UK': _('English'), # selecting this guarantees seeing the untranslated source strings 'es_ES': _('Spanish'), 'fa_IR': _('Persian'), 'fr_FR': _('French'),