forked from GithubBackups/healthchecks
Add handling for non-latin-1 characters in webhook headers
This commit is contained in:
parent
78113e1aea
commit
544ec7ea69
@ -1,6 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## v1.23.0 - Unreleased
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
- Add handling for non-latin-1 characters in webhook headers
|
||||||
|
|
||||||
## v1.22.0 - 2020-08-06
|
## v1.22.0 - 2020-08-06
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
@ -342,3 +342,20 @@ class NotifyWebhookTestCase(BaseTestCase):
|
|||||||
|
|
||||||
n = Notification.objects.get()
|
n = Notification.objects.get()
|
||||||
self.assertEqual(n.error, "Webhook notifications are not enabled.")
|
self.assertEqual(n.error, "Webhook notifications are not enabled.")
|
||||||
|
|
||||||
|
@patch("hc.api.transports.requests.request")
|
||||||
|
def test_webhooks_handle_non_ascii_in_headers(self, mock_request):
|
||||||
|
definition = {
|
||||||
|
"method_down": "GET",
|
||||||
|
"url_down": "http://foo.com",
|
||||||
|
"headers_down": {"X-Foo": "bār"},
|
||||||
|
"body_down": "",
|
||||||
|
}
|
||||||
|
|
||||||
|
self._setup_data(json.dumps(definition))
|
||||||
|
self.check.save()
|
||||||
|
|
||||||
|
self.channel.notify(self.check)
|
||||||
|
args, kwargs = mock_request.call_args
|
||||||
|
|
||||||
|
self.assertEqual(kwargs["headers"]["X-Foo"], "bār")
|
||||||
|
@ -203,7 +203,7 @@ class HttpTransport(Transport):
|
|||||||
|
|
||||||
|
|
||||||
class Webhook(HttpTransport):
|
class Webhook(HttpTransport):
|
||||||
def prepare(self, template, check, urlencode=False):
|
def prepare(self, template, check, urlencode=False, latin1=False):
|
||||||
""" Replace variables with actual values. """
|
""" Replace variables with actual values. """
|
||||||
|
|
||||||
def safe(s):
|
def safe(s):
|
||||||
@ -220,7 +220,12 @@ class Webhook(HttpTransport):
|
|||||||
for i, tag in enumerate(check.tags_list()):
|
for i, tag in enumerate(check.tags_list()):
|
||||||
ctx["$TAG%d" % (i + 1)] = safe(tag)
|
ctx["$TAG%d" % (i + 1)] = safe(tag)
|
||||||
|
|
||||||
return replace(template, ctx)
|
result = replace(template, ctx)
|
||||||
|
if latin1:
|
||||||
|
# Replace non-latin-1 characters with XML character references.
|
||||||
|
result = result.encode("latin-1", "xmlcharrefreplace").decode()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
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:
|
||||||
@ -242,7 +247,8 @@ class Webhook(HttpTransport):
|
|||||||
url = self.prepare(spec["url"], check, urlencode=True)
|
url = self.prepare(spec["url"], check, urlencode=True)
|
||||||
headers = {}
|
headers = {}
|
||||||
for key, value in spec["headers"].items():
|
for key, value in spec["headers"].items():
|
||||||
headers[key] = self.prepare(value, check)
|
# Header values should contain ASCII and latin-1 only
|
||||||
|
headers[key] = self.prepare(value, check, latin1=True)
|
||||||
|
|
||||||
body = spec["body"]
|
body = spec["body"]
|
||||||
if body:
|
if body:
|
||||||
|
@ -15,6 +15,14 @@ from hc.front.validators import (
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
def _is_latin1(s):
|
||||||
|
try:
|
||||||
|
s.encode("latin-1")
|
||||||
|
return True
|
||||||
|
except UnicodeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class HeadersField(forms.Field):
|
class HeadersField(forms.Field):
|
||||||
message = """Use "Header-Name: value" pairs, one per line."""
|
message = """Use "Header-Name: value" pairs, one per line."""
|
||||||
|
|
||||||
@ -35,6 +43,11 @@ class HeadersField(forms.Field):
|
|||||||
if not n or not v:
|
if not n or not v:
|
||||||
raise ValidationError(message=self.message)
|
raise ValidationError(message=self.message)
|
||||||
|
|
||||||
|
if not _is_latin1(n):
|
||||||
|
raise ValidationError(
|
||||||
|
message="Header names must not contain special characters"
|
||||||
|
)
|
||||||
|
|
||||||
headers[n] = v
|
headers[n] = v
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
|
@ -149,6 +149,19 @@ class AddWebhookTestCase(BaseTestCase):
|
|||||||
self.assertContains(r, """invalid-header""")
|
self.assertContains(r, """invalid-header""")
|
||||||
self.assertEqual(Channel.objects.count(), 0)
|
self.assertEqual(Channel.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_it_rejects_non_latin1_in_header_name(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
form = {
|
||||||
|
"method_down": "GET",
|
||||||
|
"url_down": "http://example.org",
|
||||||
|
"headers_down": "fō:bar",
|
||||||
|
"method_up": "GET",
|
||||||
|
}
|
||||||
|
|
||||||
|
r = self.client.post(self.url, form)
|
||||||
|
self.assertContains(r, """must not contain special characters""")
|
||||||
|
self.assertEqual(Channel.objects.count(), 0)
|
||||||
|
|
||||||
def test_it_strips_headers(self):
|
def test_it_strips_headers(self):
|
||||||
form = {
|
form = {
|
||||||
"method_down": "GET",
|
"method_down": "GET",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user