diff --git a/CHANGES/5563.bugfix b/CHANGES/5563.bugfix new file mode 100644 index 0000000000..c30c686005 --- /dev/null +++ b/CHANGES/5563.bugfix @@ -0,0 +1 @@ +Browsable HREFs now have clickable links again. diff --git a/pulpcore/app/templatetags/pulp_urls.py b/pulpcore/app/templatetags/pulp_urls.py index 4aa1a8cb17..a94e968bae 100644 --- a/pulpcore/app/templatetags/pulp_urls.py +++ b/pulpcore/app/templatetags/pulp_urls.py @@ -3,7 +3,7 @@ from django import template from django.conf import settings from django.utils.encoding import force_str -from django.utils.html import escape +from django.utils.html import escape, smart_urlquote from django.utils.safestring import SafeData, mark_safe register = template.Library() @@ -20,6 +20,8 @@ (""", """), ] word_split_re = re.compile(r"(\s+)") +api_root_prefix = r"^" + settings.V3_API_ROOT.replace("/", r"\/") +href_re = re.compile(api_root_prefix, re.IGNORECASE) @register.filter(needs_autoescape=True) @@ -57,6 +59,9 @@ def trim_url(x, limit=trim_url_limit): url = None nofollow_attr = ' rel="nofollow"' if nofollow else "" + if href_re.match(middle): + url = smart_urlquote(middle) + # Check if it's a real URL if url and ("{" in url or "%7B" in url): url = None @@ -74,8 +79,6 @@ def trim_url(x, limit=trim_url_limit): words[i] = mark_safe(word) elif autoescape: words[i] = escape(word) - elif safe_input: - words[i] = mark_safe(word) - elif autoescape: + else: words[i] = escape(word) return "".join(words) diff --git a/pulpcore/tests/unit/test_pulp_urls.py b/pulpcore/tests/unit/test_pulp_urls.py new file mode 100644 index 0000000000..cfaa9bd232 --- /dev/null +++ b/pulpcore/tests/unit/test_pulp_urls.py @@ -0,0 +1,58 @@ +import pytest +from django.conf import settings + +from pulpcore.app.templatetags import pulp_urls + +pytestmark = pytest.mark.usefixtures("fake_domain") + + +def test_urlize_quoted_hrefs_basic_url(): + """ + text starts with API_ROOT, defaults. Should be made clickable + """ + txt = settings.V3_API_ROOT + "foo/bar/" + ret = pulp_urls.urlize_quoted_hrefs(txt) + assert ret == f'{txt}' + + +def test_urlize_quoted_hrefs_nofollow(): + """ + text starts with API_ROOT, defaults. Should be made clickable + """ + txt = settings.V3_API_ROOT + "foo/bar/" + ret = pulp_urls.urlize_quoted_hrefs(txt, nofollow=False) + assert ret == f'{txt}' + + +def test_urlize_quoted_hrefs_trim(): + """ + text starts with API_ROOT, defaults. Should be made clickable + """ + txt = settings.V3_API_ROOT + "foo/bar/" + trim_txt = txt[0] + "..." + ret = pulp_urls.urlize_quoted_hrefs(txt, trim_url_limit=4) + assert ret == f'{trim_txt}' + + +def test_urlize_quoted_hrefs_basic_url_xss(): + """ + text starts with API_ROOT, includes XSS, defaults. Should be made clickable, escape XSS + """ + txt = settings.V3_API_ROOT + "foo/bar/blech/" + escapified_linked_text = ( + '' + settings.V3_API_ROOT + "foo/bar/<script>" + "alert('ALERT!')</script>blech/" + ) + ret = pulp_urls.urlize_quoted_hrefs(txt) + assert ret == escapified_linked_text + + +def test_urlize_quoted_hrefs_basic_escape(): + """ + text contains XSS. Expect escaped + """ + txt = "foo/bar/blech/" + ret = pulp_urls.urlize_quoted_hrefs(txt) + assert ret == "foo/bar/<script>alert('ALERT!')</script>blech/"