Escape markdown in MS Teams notifications. cc: #426

This commit is contained in:
Pēteris Caune 2020-09-11 11:48:52 +03:00
parent b64c8d1cb8
commit 05c81e0a41
No known key found for this signature in database
GPG Key ID: E28D7679E9A9EDE2
4 changed files with 32 additions and 4 deletions

View File

@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file.
- When copying a check, copy all fields from the "Filtering Rules" dialog (#417) - When copying a check, copy all fields from the "Filtering Rules" dialog (#417)
- Fix missing Resume button (#421) - Fix missing Resume button (#421)
- When decoding inbound emails, decode encoded headers (#420) - When decoding inbound emails, decode encoded headers (#420)
- Escape markdown in MS Teams notifications (#426)
## v1.16.0 - 2020-08-04 ## v1.16.0 - 2020-08-04

View File

@ -866,6 +866,26 @@ class NotifyTestCase(BaseTestCase):
payload = kwargs["json"] payload = kwargs["json"]
self.assertEqual(payload["@type"], "MessageCard") self.assertEqual(payload["@type"], "MessageCard")
@patch("hc.api.transports.requests.request")
def test_msteams_escapes_markdown(self, mock_post):
self._setup_data("msteams", "http://example.com/webhook")
mock_post.return_value.status_code = 200
self.check.name = """
TEST _underscore_ `backticks` <u>underline</u> \\backslash\\ "quoted"
"""
self.channel.notify(self.check)
args, kwargs = mock_post.call_args
text = kwargs["json"]["text"]
self.assertIn(r"\_underscore\_", text)
self.assertIn(r"\`backticks\`", text)
self.assertIn("&lt;u&gt;underline&lt;/u&gt;", text)
self.assertIn(r"\\backslash\\ ", text)
self.assertIn("&quot;quoted&quot;", text)
@patch("hc.api.transports.os.system") @patch("hc.api.transports.os.system")
@override_settings(SHELL_ENABLED=True) @override_settings(SHELL_ENABLED=True)
def test_shell(self, mock_system): def test_shell(self, mock_system):

View File

@ -3,6 +3,7 @@ import os
from django.conf import settings from django.conf import settings
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
from django.utils.html import escape
import json import json
import requests import requests
from urllib.parse import quote, urlencode from urllib.parse import quote, urlencode
@ -582,6 +583,15 @@ class MsTeams(HttpTransport):
def notify(self, check): def notify(self, check):
text = tmpl("msteams_message.json", check=check) text = tmpl("msteams_message.json", check=check)
payload = json.loads(text) payload = json.loads(text)
# Escape special HTML characters in check's name
safe_name = escape(check.name_then_code())
# Escape characters that have special meaning in Markdown
for c in r"\`*_{}[]()#+-.!|":
safe_name = safe_name.replace(c, "\\" + c)
payload["text"] = f"{safe_name}” is {check.status.upper()}."
return self.post(self.channel.value, json=payload) return self.post(self.channel.value, json=payload)
@ -629,7 +639,5 @@ class LineNotify(HttpTransport):
"Content-Type": "application/x-www-form-urlencoded", "Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer %s" % self.channel.linenotify_token, "Authorization": "Bearer %s" % self.channel.linenotify_token,
} }
payload = { payload = {"message": tmpl("linenotify_message.html", check=check)}
"message": tmpl("linenotify_message.html", check=check)
}
return self.post(self.URL, headers=headers, params=payload) return self.post(self.URL, headers=headers, params=payload)

View File

@ -3,7 +3,6 @@
"@type": "MessageCard", "@type": "MessageCard",
"@context": "https://schema.org/extensions", "@context": "https://schema.org/extensions",
"themeColor": "{% if check.status == "up" %}5cb85c{% endif %}{% if check.status == "down" %}d9534f{% endif %}", "themeColor": "{% if check.status == "up" %}5cb85c{% endif %}{% if check.status == "down" %}d9534f{% endif %}",
"text": "“{{ check.name_then_code|escapejs }}” is {{ check.status|upper }}.",
"sections": [ "sections": [
{ {
"facts": [ "facts": [