
Loading

Loading
We use strictly necessary cookies to run this site, and analytics cookies to understand how it's used. See our Privacy Policy for details.
A GitHub-reviewed security advisory, not necessarily under active exploitation the way our CISA KEV advisories are, but a real, disclosed vulnerability in an open-source dependency.
Affected packages
Vulnerable: <= 12.4
Details
In Kiwi TCMS version 16.1, the fix for CVE-2026-54724 (open redirect via unvalidated next parameter) in the account confirmation endpoint is incomplete. The validation uses startswith("/") to check if the redirect URL is local, but this is bypassed by protocol-relative URLs like //evil.com which start with / but redirect the browser to an external attacker-controlled domain. 16.1’s incomplete mitigation still allows protocol-relative destinations and earlier releases lack the mitigation.
In tcms/kiwi_auth/views.py, the Confirm view (line ~113):
def get_redirect_url(self, *args, **kwargs):
self.url = self.request.GET.get("next", reverse("core-views-index"))
if not self.url.startswith("/"):
self.url = reverse("core-views-index")
The check startswith("/") passes for //evil.com because it begins with /, but browsers interpret //evil.com as a protocol-relative URL that redirects to https://evil.com (or http://evil.com depending on the current page's protocol).
GET /confirm/<activation_key>/?next=//evil.com/phishing HTTP/1.1
Host: kiwi.example.com
Response:
HTTP/1.1 302 Found
Location: //evil.com/phishing
The browser follows the redirect to https://evil.com/phishing.
Verification:
>>> url = "//evil.com/phishing"
>>> url.startswith("/")
True # passes the check!
An attacker can craft a confirmation link that, after the user confirms their account, redirects them to a phishing page. This is useful for:
The attack requires sending a crafted activation link to a user who is registering, or intercepting/predicting the activation key.
Use Django's url_has_allowed_host_and_scheme() utility instead of a simple startswith check:
from django.utils.http import url_has_allowed_host_and_scheme
def get_redirect_url(self, *args, **kwargs):
self.url = self.request.GET.get("next", reverse("core-views-index"))
if not url_has_allowed_host_and_scheme(
self.url,
allowed_hosts={self.request.get_host()},
require_https=self.request.is_secure(),
):
self.url = reverse("core-views-index")
This properly rejects protocol-relative URLs, absolute URLs to other hosts, and other bypass patterns.
Fixed in Version 16.2
Talk to us about this
Find out whether this dependency (or others like it) is actually reachable in your codebase.
Get help patching, or building dependency-scanning into your pipeline going forward.
A secure code review or supply chain review finds this before an advisory does.