mirror of https://github.com/zulip/zulip.git
compatibility: Add more strict desktop app blocking.
This allows us to block use of the desktop app with insecure versions (we simply fail to load the Zulip webapp at all, instead rendering an error page). For now we block only versions that are known to be both insecure and not auto-updating, but we can easily adjust these parameters in the future.
This commit is contained in:
parent
6fb438277e
commit
d9bb6d0081
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -29,11 +29,12 @@
|
|||
<div data-process="insecure-desktop-app" class="alert alert-info red">
|
||||
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}">×</span>
|
||||
<div data-step="1">
|
||||
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.
|
||||
<a class="alert-link" href="https://zulipchat.com/apps" target="_blank">
|
||||
Download the latest version.
|
||||
</a>
|
||||
{% endtrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
{% extends "zerver/portico.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="error_page">
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<img src="/static/images/400art.svg" alt=""/>
|
||||
<div class="errorbox config-error">
|
||||
<div class="errorcontent">
|
||||
<h1 class="lead">{{ _('Update required') }}</h1>
|
||||
<p>
|
||||
{% trans %}
|
||||
You are using old version of the Zulip desktop
|
||||
app that is no longer supported.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
|
||||
{% if auto_update_broken %}
|
||||
<p>
|
||||
{% trans %}
|
||||
The auto-update feature in this old version of
|
||||
Zulip desktop app no longer works.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<p>
|
||||
<a href="https://zulipchat.com/apps" target="_blank">
|
||||
{{ _("Download the latest release.") }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue