Use hc.lib.string.replace for webhooks too.

hc.lib.string.replace only replaces placeholders that appear in the original template. It ignores any placeholders that "emerge" while doing string substitutions. This is done mainly to avoid unexpected behavior when check names or tags contain dollar signs.
This commit is contained in:
Pēteris Caune 2019-11-20 17:44:41 +02:00
parent e4646205cb
commit 98ba51f44f
No known key found for this signature in database
GPG Key ID: E28D7679E9A9EDE2
2 changed files with 23 additions and 27 deletions

View File

@ -91,6 +91,18 @@ class NotifyTestCase(BaseTestCase):
self.assertEqual(kwargs["headers"], {"User-Agent": "healthchecks.io"}) self.assertEqual(kwargs["headers"], {"User-Agent": "healthchecks.io"})
self.assertEqual(kwargs["timeout"], 5) self.assertEqual(kwargs["timeout"], 5)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_variable_variables(self, mock_get):
self._setup_data("webhook", "http://host/$$NAMETAG1")
self.check.tags = "foo bar"
self.check.save()
self.channel.notify(self.check)
# $$NAMETAG1 should *not* get transformed to "foo"
args, kwargs = mock_get.call_args
self.assertEqual(args[1], "http://host/$TAG1")
@patch("hc.api.transports.requests.request") @patch("hc.api.transports.requests.request")
def test_webhooks_support_post(self, mock_request): def test_webhooks_support_post(self, mock_request):
template = "http://example.com\n\nThe Time Is $NOW" template = "http://example.com\n\nThe Time Is $NOW"

View File

@ -188,39 +188,23 @@ class HttpTransport(Transport):
class Webhook(HttpTransport): class Webhook(HttpTransport):
def prepare(self, template, check, urlencode=False): def prepare(self, template, check, urlencode=False):
""" Replace variables with actual values. """ Replace variables with actual values. """
There should be no bad translations if users use $ symbol in
check's name or tags, because $ gets urlencoded to %24
"""
def safe(s): def safe(s):
return quote(s) if urlencode else s return quote(s) if urlencode else s
result = template ctx = {
if "$CODE" in result: "$CODE": str(check.code),
result = result.replace("$CODE", str(check.code)) "$STATUS": check.status,
"$NOW": safe(timezone.now().replace(microsecond=0).isoformat()),
"$NAME": safe(check.name),
"$TAGS": safe(check.tags),
}
if "$STATUS" in result:
result = result.replace("$STATUS", check.status)
if "$NOW" in result:
s = timezone.now().replace(microsecond=0).isoformat()
result = result.replace("$NOW", safe(s))
if "$NAME" in result:
result = result.replace("$NAME", safe(check.name))
if "$TAGS" in result:
result = result.replace("$TAGS", safe(check.tags))
if "$TAG" in result:
for i, tag in enumerate(check.tags_list()): for i, tag in enumerate(check.tags_list()):
placeholder = "$TAG%d" % (i + 1) ctx["$TAG%d" % (i + 1)] = safe(tag)
result = result.replace(placeholder, safe(tag))
return result return replace(template, ctx)
def is_noop(self, check): def is_noop(self, check):
if check.status == "down" and not self.channel.url_down: if check.status == "down" and not self.channel.url_down: