Browse Source

Validate HTTP header names in the "Add Webhook" form.

pull/163/head
Pēteris Caune 7 years ago
parent
commit
55d6471156
5 changed files with 69 additions and 30 deletions
  1. +20
    -5
      hc/front/forms.py
  2. +17
    -2
      hc/front/tests/test_add_webhook.py
  3. +4
    -0
      static/css/add_webhook.css
  4. +1
    -0
      templates/base.html
  5. +27
    -23
      templates/integrations/add_webhook.html

+ 20
- 5
hc/front/forms.py View File

@ -1,5 +1,6 @@
import json
from datetime import timedelta as td from datetime import timedelta as td
import json
import re
from django import forms from django import forms
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
@ -55,28 +56,42 @@ class AddUrlForm(forms.Form):
value = forms.URLField(max_length=1000, validators=[WebhookValidator()]) value = forms.URLField(max_length=1000, validators=[WebhookValidator()])
_valid_header_name = re.compile(r'\A[^:\s][^:\r\n]*\Z').match
class AddWebhookForm(forms.Form): class AddWebhookForm(forms.Form):
error_css_class = "has-error" error_css_class = "has-error"
url_down = forms.URLField(max_length=1000, required=False, url_down = forms.URLField(max_length=1000, required=False,
validators=[WebhookValidator()])
validators=[WebhookValidator()])
url_up = forms.URLField(max_length=1000, required=False, url_up = forms.URLField(max_length=1000, required=False,
validators=[WebhookValidator()])
validators=[WebhookValidator()])
post_data = forms.CharField(max_length=1000, required=False) post_data = forms.CharField(max_length=1000, required=False)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(AddWebhookForm, self).__init__(*args, **kwargs) super(AddWebhookForm, self).__init__(*args, **kwargs)
self.invalid_header_names = set()
self.headers = {} self.headers = {}
if "header_key[]" in self.data and "header_value[]" in self.data: if "header_key[]" in self.data and "header_value[]" in self.data:
keys = self.data.getlist("header_key[]") keys = self.data.getlist("header_key[]")
values = self.data.getlist("header_value[]") values = self.data.getlist("header_value[]")
for key, value in zip(keys, values): for key, value in zip(keys, values):
if key:
self.headers[key] = value
if not key:
continue
if not _valid_header_name(key):
self.invalid_header_names.add(key)
self.headers[key] = value
def clean(self):
if self.invalid_header_names:
raise forms.ValidationError("Invalid header names")
return self.cleaned_data
def get_value(self): def get_value(self):
val = dict(self.cleaned_data) val = dict(self.cleaned_data)


+ 17
- 2
hc/front/tests/test_add_webhook.py View File

@ -72,12 +72,27 @@ class AddWebhookTestCase(BaseTestCase):
self.assertEqual(c.value, '{"headers": {}, "post_data": "hello", "url_down": "http://foo.com", "url_up": ""}') self.assertEqual(c.value, '{"headers": {}, "post_data": "hello", "url_down": "http://foo.com", "url_up": ""}')
def test_it_adds_headers(self): def test_it_adds_headers(self):
form = {"url_down": "http://foo.com", "header_key[]": ["test", "test2"], "header_value[]": ["123", "abc"]}
form = {
"url_down": "http://foo.com",
"header_key[]": ["test", "test2"],
"header_value[]": ["123", "abc"]
}
self.client.login(username="[email protected]", password="password") self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, form) r = self.client.post(self.url, form)
self.assertRedirects(r, "/integrations/") self.assertRedirects(r, "/integrations/")
c = Channel.objects.get() c = Channel.objects.get()
self.assertEqual(c.value, '{"headers": {"test": "123", "test2": "abc"}, "post_data": "", "url_down": "http://foo.com", "url_up": ""}')
self.assertEqual(c.headers, {"test": "123", "test2": "abc"})
def test_it_rejects_bad_header_names(self):
self.client.login(username="[email protected]", password="password")
form = {
"url_down": "http://example.org",
"header_key[]": ["ill:egal"],
"header_value[]": ["123"]
}
r = self.client.post(self.url, form)
self.assertContains(r, "Please use valid HTTP header names.")
self.assertEqual(Channel.objects.count(), 0)

+ 4
- 0
static/css/add_webhook.css View File

@ -0,0 +1,4 @@
.webhook-header .error {
border-color: #a94442;
}

+ 1
- 0
templates/base.html View File

@ -34,6 +34,7 @@
<link rel="stylesheet" href="{% static 'css/channel_checks.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/channel_checks.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/log.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/log.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/add_pushover.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/add_webhook.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/settings.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/settings.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/last_ping.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/last_ping.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/profile.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/profile.css' %}" type="text/css">


+ 27
- 23
templates/integrations/add_webhook.html View File

@ -105,32 +105,36 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="form-group {{ form.headers.css_classes }}">
<div class="form-group">
<label class="col-sm-2 control-label">Request Headers</label> <label class="col-sm-2 control-label">Request Headers</label>
<div id="webhook-headers" class="col-xs-12 col-sm-10">
{% for k, v in form.headers.items %}
<div class="form-inline webhook-header">
<input
type="text"
class="form-control key"
name="header_key[]"
placeholder="Content-Type"
value="{{ k }}" />
<input
type="text"
class="form-control value"
name="header_value[]"
placeholder="application/json"
value="{{ v }}" />
<button class="btn btn-default" type="button">
<span class="icon-delete"></span>
</button>
<div class="col-xs-12 col-sm-10">
<div id="webhook-headers">
{% for k, v in form.headers.items %}
<div class="form-inline webhook-header">
<input
type="text"
class="form-control key {% if k in form.invalid_header_names %}error{% endif %}"
name="header_key[]"
placeholder="Content-Type"
value="{{ k }}" />
<input
type="text"
class="form-control value"
name="header_value[]"
placeholder="application/json"
value="{{ v }}" />
<button class="btn btn-default" type="button">
<span class="icon-delete"></span>
</button>
</div>
{% endfor %}
</div> </div>
{% endfor %}
</div>
<div class="text-center">
{% if form.invalid_header_names %}
<div class="text-danger">
Please use valid HTTP header names.
</div>
{% endif %}
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">


Loading…
Cancel
Save