diff --git a/static/styles/portico/portico.scss b/static/styles/portico/portico.scss index d3aa09a0e5..f8fbb930bc 100644 --- a/static/styles/portico/portico.scss +++ b/static/styles/portico/portico.scss @@ -1297,12 +1297,16 @@ input.new-organization-button { } .error_page { - padding: 20px 0px; min-height: calc(100vh - 290px); + height: 100%; background-color: hsl(163, 42%, 85%); font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; } +.error_page .container { + padding: 20px 0px; +} + .error_page .row-fluid { margin-top: 60px; } diff --git a/templates/zerver/app/navbar_alerts.html b/templates/zerver/app/navbar_alerts.html index a808ed318d..2e0ad38853 100644 --- a/templates/zerver/app/navbar_alerts.html +++ b/templates/zerver/app/navbar_alerts.html @@ -29,11 +29,12 @@
×
- You are using an old, insecure version of the Zulip - desktop app that cannot auto-update. + {% trans %} + You are using an old version of the Zulip desktop app with known security bugs. Download the latest version. + {% endtrans %}
diff --git a/templates/zerver/insecure_desktop_app.html b/templates/zerver/insecure_desktop_app.html new file mode 100644 index 0000000000..b29a9ffe80 --- /dev/null +++ b/templates/zerver/insecure_desktop_app.html @@ -0,0 +1,40 @@ +{% extends "zerver/portico.html" %} + +{% block content %} + +
+
+
+ +
+
+

{{ _('Update required') }}

+

+ {% trans %} + You are using old version of the Zulip desktop + app that is no longer supported. + {% endtrans %} +

+ + {% if auto_update_broken %} +

+ {% trans %} + The auto-update feature in this old version of + Zulip desktop app no longer works. + {% endtrans %} +

+ {% endif %} + +

+ + {{ _("Download the latest release.") }} + +

+
+ +
+
+
+
+ +{% endblock %} diff --git a/zerver/tests/test_compatibility.py b/zerver/tests/test_compatibility.py index e3cab24373..d91dcb06eb 100644 --- a/zerver/tests/test_compatibility.py +++ b/zerver/tests/test_compatibility.py @@ -1,5 +1,6 @@ from zerver.lib.test_classes import ZulipTestCase -from zerver.views.compatibility import find_mobile_os, version_lt +from zerver.views.compatibility import find_mobile_os, version_lt, is_outdated_desktop_app + class VersionTest(ZulipTestCase): data = [case.split() for case in ''' @@ -93,13 +94,11 @@ class CompatibilityTest(ZulipTestCase): assert False # nocoverage def test_insecure_desktop_app(self) -> None: - from zerver.views.compatibility import is_outdated_desktop_app + self.assertEqual(is_outdated_desktop_app('ZulipDesktop/0.5.2 (Mac)'), (True, True, True)) + self.assertEqual(is_outdated_desktop_app('ZulipElectron/2.3.82 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Zulip/2.3.82 Chrome/61.0.3163.100 Electron/2.0.9 Safari/537.36'), (True, True, True)) + self.assertEqual(is_outdated_desktop_app('ZulipElectron/4.0.0 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Zulip/4.0.3 Chrome/66.0.3359.181 Electron/3.1.10 Safari/537.36'), (True, False, False)) + self.assertEqual(is_outdated_desktop_app('ZulipElectron/4.0.3 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Zulip/4.0.3 Chrome/66.0.3359.181 Electron/3.1.10 Safari/537.36'), (False, False, False)) - self.assertTrue(is_outdated_desktop_app('ZulipDesktop/0.5.2 (Mac)')) - self.assertTrue(is_outdated_desktop_app('ZulipElectron/2.3.82 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Zulip/2.3.82 Chrome/61.0.3163.100 Electron/2.0.9 Safari/537.36')) - self.assertFalse(is_outdated_desktop_app('ZulipElectron/4.0.0 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Zulip/4.0.3 Chrome/66.0.3359.181 Electron/3.1.10 Safari/537.36')) - self.assertFalse(is_outdated_desktop_app('ZulipElectron/4.0.3 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Zulip/4.0.3 Chrome/66.0.3359.181 Electron/3.1.10 Safari/537.36')) + self.assertEqual(is_outdated_desktop_app('Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'), (False, False, False)) - self.assertFalse(is_outdated_desktop_app('Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36')) - - self.assertFalse(is_outdated_desktop_app('')) + self.assertEqual(is_outdated_desktop_app(''), (False, False, False)) diff --git a/zerver/tests/test_home.py b/zerver/tests/test_home.py index 697a72c995..d61df80290 100644 --- a/zerver/tests/test_home.py +++ b/zerver/tests/test_home.py @@ -390,6 +390,15 @@ class HomeTest(ZulipTestCase): html = result.content.decode('utf-8') self.assertIn('Accept the new Terms of Service', html) + def test_banned_desktop_app_versions(self) -> None: + user = self.example_user('hamlet') + self.login_user(user) + + result = self.client_get('/', + HTTP_USER_AGENT="ZulipElectron/2.3.82") + html = result.content.decode('utf-8') + self.assertIn('You are using old version of the Zulip desktop', html) + def test_terms_of_service_first_time_template(self) -> None: user = self.example_user('hamlet') self.login_user(user) diff --git a/zerver/views/compatibility.py b/zerver/views/compatibility.py index 047dfc7a42..f54b60546c 100644 --- a/zerver/views/compatibility.py +++ b/zerver/views/compatibility.py @@ -88,16 +88,25 @@ def check_global_compatibility(request: HttpRequest) -> HttpResponse: return json_error(legacy_compatibility_error_message) return json_success() -def is_outdated_desktop_app(user_agent_str: str) -> bool: +def is_outdated_desktop_app(user_agent_str: str) -> Tuple[bool, bool, bool]: + # Returns (insecure, banned, auto_update_broken user_agent = parse_user_agent(user_agent_str) if user_agent['name'] == 'ZulipDesktop': # The deprecated QT/webkit based desktop app, last updated in ~2016. - return True + return (True, True, True) - if user_agent['name'] == 'ZulipElectron' and version_lt(user_agent['version'], '4.0.0'): - # Versions of the modern Electron-based Zulip desktop app with - # known security issues. Versions before 2.3.82 won't - # auto-update; we may want a special notice to distinguish - # those from modern releases. - return True - return False + if user_agent['name'] != 'ZulipElectron': + return (False, False, False) + + if version_lt(user_agent['version'], '4.0.0'): + # Version 2.3.82 and older (aka <4.0.0) of the modern + # Electron-based Zulip desktop app with known security issues. + # won't auto-update; we may want a special notice to + # distinguish those from modern releases. + return (True, True, True) + + if version_lt(user_agent['version'], '4.0.3'): + # Other insecure versions should just warn. + return (True, False, False) + + return (False, False, False) diff --git a/zerver/views/home.py b/zerver/views/home.py index ed394c6023..7d779e0020 100644 --- a/zerver/views/home.py +++ b/zerver/views/home.py @@ -146,6 +146,18 @@ def home(request: HttpRequest) -> HttpResponse: @zulip_login_required def home_real(request: HttpRequest) -> HttpResponse: + # Before we do any real work, check if the app is banned. + (insecure_desktop_app, banned_desktop_app, auto_update_broken) = is_outdated_desktop_app( + request.META.get("HTTP_USER_AGENT", "")) + if banned_desktop_app: + return render( + request, + 'zerver/insecure_desktop_app.html', + context={ + "auto_update_broken": auto_update_broken, + } + ) + # We need to modify the session object every two weeks or it will expire. # This line makes reloading the page a sufficient action to keep the # session alive. @@ -228,7 +240,7 @@ def home_real(request: HttpRequest) -> HttpResponse: debug_mode = settings.DEBUG, test_suite = settings.TEST_SUITE, poll_timeout = settings.POLL_TIMEOUT, - insecure_desktop_app = is_outdated_desktop_app(request.META.get("HTTP_USER_AGENT", "")), + insecure_desktop_app = insecure_desktop_app, login_page = settings.HOME_NOT_LOGGED_IN, root_domain_uri = settings.ROOT_DOMAIN_URI, max_file_upload_size = settings.MAX_FILE_UPLOAD_SIZE,