From 1cdd451d703a9c1b0a395d81dacadbf5c4d85277 Mon Sep 17 00:00:00 2001 From: Tim Abbott Date: Sun, 22 Jan 2017 20:22:40 -0800 Subject: [PATCH] streams: Fix autosubscribe security bug (CVE-2017-0881). A bug in Zulip's implementation of the "stream exists" endpoint meant that any user of a Zulip server could subscribe to an invite-only stream without needing to be invited by using the "autosubscribe" argument. Thanks to Rafid Aslam for discovering this issue. --- zerver/tests/test_subs.py | 23 +++++++++++++++++++++++ zerver/views/streams.py | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/zerver/tests/test_subs.py b/zerver/tests/test_subs.py index e8777235c2..3298ccf35f 100644 --- a/zerver/tests/test_subs.py +++ b/zerver/tests/test_subs.py @@ -1501,6 +1501,29 @@ class SubscriptionAPITest(ZulipTestCase): self.assertIn("exists", json) self.assertTrue(json["exists"]) + def test_existing_subscriptions_autosubscription_private_stream(self): + # type: () -> None + """Call /json/subscriptions/exist on an existing private stream with + autosubscribe should fail. + """ + stream_name = "Saxony" + result = self.common_subscribe_to_streams("cordelia@zulip.com", [stream_name], + invite_only=True) + stream = get_stream(stream_name, self.realm) + + result = self.client_post("/json/subscriptions/exists", + {"stream": stream_name, "autosubscribe": True}) + self.assert_json_success(result) + json = ujson.loads(result.content) + self.assertIn("exists", json) + self.assertTrue(json["exists"]) + self.assertIn("subscribed", json) + # Importantly, we are not now subscribed + self.assertFalse(json["subscribed"]) + self.assertEqual(Subscription.objects.filter( + recipient__type=Recipient.STREAM, + recipient__type_id=stream.id).count(), 1) + def get_subscription(self, user_profile, stream_name): # type: (UserProfile, text_type) -> Subscription stream = Stream.objects.get(realm=self.realm, name=stream_name) diff --git a/zerver/views/streams.py b/zerver/views/streams.py index 23a47be933..7779412ea1 100644 --- a/zerver/views/streams.py +++ b/zerver/views/streams.py @@ -447,7 +447,7 @@ def stream_exists_backend(request, user_profile, stream_name, autosubscribe): result = {"exists": bool(stream)} if stream is not None: recipient = get_recipient(Recipient.STREAM, stream.id) - if autosubscribe: + if not stream.invite_only and autosubscribe: bulk_add_subscriptions([stream], [user_profile]) result["subscribed"] = Subscription.objects.filter(user_profile=user_profile, recipient=recipient,