diff --git a/CHANGELOG.md b/CHANGELOG.md index 992cdd66..139451bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ All notable changes to this project will be documented in this file. ### Improvements - Add the `prunetokenbucket` management command +### Bug Fixes +- Fix badges for tags containing special characters (#240, #237) + + ## 1.7.0 - 2019-05-02 ### Improvements diff --git a/hc/api/tests/test_badge.py b/hc/api/tests/test_badge.py index 388d440d..038483b0 100644 --- a/hc/api/tests/test_badge.py +++ b/hc/api/tests/test_badge.py @@ -60,3 +60,14 @@ class BadgeTestCase(BaseTestCase): r = self.client.get(self.json_url) self.assertContains(r, "late") + + def test_it_handles_special_characters(self): + self.check.tags = "db@dc1" + self.check.save() + + sig = base64_hmac(str(self.project.badge_key), "db@dc1", settings.SECRET_KEY) + sig = sig[:8] + url = "/badge/%s/%s/db%%2540dc1.svg" % (self.project.badge_key, sig) + + r = self.client.get(url) + self.assertEqual(r.status_code, 200) diff --git a/hc/api/urls.py b/hc/api/urls.py index 4b865ead..cf035c45 100644 --- a/hc/api/urls.py +++ b/hc/api/urls.py @@ -1,7 +1,21 @@ -from django.urls import path +from urllib.parse import quote, unquote +from django.urls import path, register_converter from hc.api import views + +class QuoteConverter: + regex = '[\w%~_.-]+' + + def to_python(self, value): + return unquote(value) + + def to_url(self, value): + return quote(value, safe="") + + +register_converter(QuoteConverter, 'quoted') + urlpatterns = [ path('ping//', views.ping, name="hc-ping-slash"), path('ping/', views.ping, name="hc-ping"), @@ -17,13 +31,13 @@ urlpatterns = [ path('api/v1/channels/', views.channels), - path('badge///.svg', views.badge, + path('badge///.svg', views.badge, name="hc-badge"), path('badge//.svg', views.badge, {"tag": "*"}, name="hc-badge-all"), - path('badge///.json', views.badge, + path('badge///.json', views.badge, {"format": "json"}, name="hc-badge-json"), path('badge//.json', views.badge, diff --git a/hc/accounts/tests/test_badges.py b/hc/front/tests/test_badges.py similarity index 68% rename from hc/accounts/tests/test_badges.py rename to hc/front/tests/test_badges.py index 0f86407d..705193e3 100644 --- a/hc/accounts/tests/test_badges.py +++ b/hc/front/tests/test_badges.py @@ -4,12 +4,17 @@ from hc.api.models import Check class BadgesTestCase(BaseTestCase): + def setUp(self): + super(BadgesTestCase, self).setUp() + + self.url = "/projects/%s/badges/" % self.project.code + def test_it_shows_badges(self): Check.objects.create(project=self.project, tags="foo a-B_1 baz@") Check.objects.create(project=self.bobs_project, tags="bobs-tag") self.client.login(username="alice@example.org", password="password") - r = self.client.get("/projects/%s/badges/" % self.project.code) + r = self.client.get(self.url) self.assertContains(r, "foo.svg") self.assertContains(r, "a-B_1.svg") @@ -27,6 +32,13 @@ class BadgesTestCase(BaseTestCase): self.project.save() self.client.login(username="alice@example.org", password="password") - r = self.client.get("/projects/%s/badges/" % self.project.code) + r = self.client.get(self.url) self.assertContains(r, "badge/alices-badge-key/") self.assertContains(r, "badge/alices-badge-key/") + + def test_it_handles_special_characers_in_tags(self): + Check.objects.create(project=self.project, tags="db@dc1") + + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertContains(r, "db%2540dc1.svg") diff --git a/hc/front/views.py b/hc/front/views.py index 3b5310d2..ca896917 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -528,9 +528,6 @@ def badges(request, code): urls = [] for tag in sorted_tags: - if not re.match("^[\w-]+$", tag) and tag != "*": - continue - urls.append({ "tag": tag, "svg": get_badge_url(project.badge_key, tag), diff --git a/templates/front/badges.html b/templates/front/badges.html index 1ac159e0..9270ca90 100644 --- a/templates/front/badges.html +++ b/templates/front/badges.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% load compress static hc_extras %} -{% block title %}Account Settings - {% site_name %}{% endblock %} +{% block title %}Status Badges - {% site_name %}{% endblock %} {% block content %}