webhook: Catch potential JsonableError when parsing widget_content.

The `widget_content` key is expected to contain a string which parses
as JSON; in the event that it does not, log the error and notify the
bot owner, instead of failing silently.

Fixes #16850.
This commit is contained in:
Sundar Guntnur
2021-03-22 09:48:35 +05:30
committed by Alex Vandiver
parent 954aced781
commit 5aefb5e656
2 changed files with 39 additions and 1 deletions

View File

@@ -316,7 +316,15 @@ def do_rest_call(
if not response:
return None
if str(response.status_code).startswith("2"):
process_success_response(event, service_handler, response)
try:
process_success_response(event, service_handler, response)
except JsonableError as e:
response_message = e.msg
logging.info("Outhook trigger failed:", stack_info=True)
fail_with_message(event, response_message)
response_message = f"The outgoing webhook server attempted to send a message in Zulip, but that request resulted in the following error:\n> {e}"
notify_bot_owner(event, failure_message=response_message)
return None
else:
logging.warning(
"Message %(message_url)s triggered an outgoing webhook, returning status "

View File

@@ -3,6 +3,7 @@ from unittest import mock
import orjson
import requests
import responses
from version import ZULIP_VERSION
from zerver.lib.actions import do_create_user
@@ -231,6 +232,35 @@ I'm a generic exception :(
assert bot_user.bot_owner is not None
self.assertEqual(bot_owner_notification.recipient_id, bot_user.bot_owner.recipient_id)
def test_jsonable_exception(self) -> None:
bot_user = self.example_user("outgoing_webhook_bot")
mock_event = self.mock_event(bot_user)
service_handler = GenericOutgoingWebhookService("token", bot_user, "service")
# The "widget_content" key is required to be a string which is
# itself JSON-encoded; passing arbitrary text data in it will
# cause the hook to fail.
response = {"content": "whatever", "widget_content": "test"}
expect_logging_info = self.assertLogs(level="INFO")
expect_fail = mock.patch("zerver.lib.outgoing_webhook.fail_with_message")
with responses.RequestsMock(assert_all_requests_are_fired=True) as requests_mock:
requests_mock.add(
requests_mock.POST, "https://example.zulip.com", status=200, json=response
)
with expect_logging_info, expect_fail as mock_fail:
do_rest_call("https://example.zulip.com", mock_event, service_handler)
self.assertTrue(mock_fail.called)
bot_owner_notification = self.get_last_message()
self.assertEqual(
bot_owner_notification.content,
"""[A message](http://zulip.testserver/#narrow/stream/999-Verona/topic/Foo/near/) to your bot @_**Outgoing Webhook** triggered an outgoing webhook.
The outgoing webhook server attempted to send a message in Zulip, but that request resulted in the following error:
> Widgets: API programmer sent invalid JSON content""",
)
assert bot_user.bot_owner is not None
self.assertEqual(bot_owner_notification.recipient_id, bot_user.bot_owner.recipient_id)
class TestOutgoingWebhookMessaging(ZulipTestCase):
def create_outgoing_bot(self, bot_owner: UserProfile) -> UserProfile: