Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bouncer/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ def goto_url(request):
# append our own.
url = parse.urldefrag(url)[0]

group = request.params.get("group", "")
group = parse.quote(request.params.get("group", ""), safe="")
query = parse.quote(request.params.get("q", ""))
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot also add the unit test for edge cases

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added three edge case tests in commit c3f146a:

  • test_it_percent_encodes_group_with_special_characters: verifies a <script>alert("xss")</script> payload is fully encoded and raw < never appears in the output URLs
  • test_it_percent_encodes_group_with_angle_brackets_and_quotes: verifies <"group">%3C%22group%22%3E
  • test_it_percent_encodes_group_with_ampersand_and_equals: verifies group&id=1group%26id%3D1


# Translate any refining querystring parameters into a URL fragment
Expand Down
39 changes: 39 additions & 0 deletions tests/unit/bouncer/views_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,45 @@ def test_it_sets_group_in_fragment_if_both_group_and_query_present(self):
assert data["viaUrl"].endswith(expected_frag)
assert data["extensionUrl"].endswith(expected_frag)

def test_it_percent_encodes_group_with_special_characters(self):
request = mock_request()
request.GET["url"] = "https://example.com/article.html"
request.GET["group"] = '<script>alert("xss")</script>'

ctx = views.goto_url(request)

data = json.loads(ctx["data"])
# The group value must be percent-encoded; raw '<', '>', '"' must not appear
assert "<" not in data["viaUrl"]
assert "<" not in data["extensionUrl"]
expected_frag = "#annotations:group:%3Cscript%3Ealert%28%22xss%22%29%3C%2Fscript%3E"
assert data["viaUrl"].endswith(expected_frag)
assert data["extensionUrl"].endswith(expected_frag)

def test_it_percent_encodes_group_with_angle_brackets_and_quotes(self):
request = mock_request()
request.GET["url"] = "https://example.com/article.html"
request.GET["group"] = '<"group">'

ctx = views.goto_url(request)

data = json.loads(ctx["data"])
expected_frag = "#annotations:group:%3C%22group%22%3E"
assert data["viaUrl"].endswith(expected_frag)
assert data["extensionUrl"].endswith(expected_frag)

def test_it_percent_encodes_group_with_ampersand_and_equals(self):
request = mock_request()
request.GET["url"] = "https://example.com/article.html"
request.GET["group"] = "group&id=1"

ctx = views.goto_url(request)

data = json.loads(ctx["data"])
expected_frag = "#annotations:group:group%26id%3D1"
assert data["viaUrl"].endswith(expected_frag)
assert data["extensionUrl"].endswith(expected_frag)

def test_it_rejects_invalid_or_missing_urls(self):
invalid_urls = [
None,
Expand Down