[SECURITY] Fix checkout validation bypass (CVE-2026-101266, Z#23245008)

This commit is contained in:
Raphael Michel
2026-09-29 13:38:58 +02:00
parent 2bd0a341d6
commit 11ac2cc91c
2 changed files with 22 additions and 2 deletions
+11 -2
View File
@@ -21,6 +21,7 @@
#
from urllib.parse import quote, urlencode
from django.conf import settings
from django.contrib import messages
from django.http import Http404
from django.utils.decorators import method_decorator
@@ -29,6 +30,7 @@ from django.views.generic import View
from pretix.base.services.cart import CartError
from pretix.base.signals import validate_cart
from pretix.base.views.tasks import AsyncAction
from pretix.helpers.http import redirect_to_url
from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.checkoutflow import get_checkout_flow
@@ -51,7 +53,12 @@ class CheckoutView(View):
def dispatch(self, request, *args, **kwargs):
self.request = request
if not cart_exists(request) and "async_id" not in request.GET:
is_asyncaction_call = (
request.method == "GET" and
'async_id' in request.GET and
settings.HAS_CELERY
)
if not cart_exists(request) and not is_asyncaction_call:
messages.error(request, _("Your cart is empty"))
return self.redirect(self.get_index_url(self.request))
@@ -78,7 +85,9 @@ class CheckoutView(View):
utm_params = {k: v for k, v in request.GET.items() if k.startswith("utm_")}
return self.redirect(step.get_step_url(request) + '?' + urlencode(utm_params))
is_selected = (step.identifier == kwargs.get('step', ''))
if "async_id" not in request.GET and not is_selected and not step.is_completed(request, warn=not is_selected):
is_valid_asyncaction_call = is_asyncaction_call and isinstance(step, AsyncAction)
if not is_valid_asyncaction_call and not is_selected and not step.is_completed(request, warn=not is_selected):
return self.redirect(step.get_step_url(request))
if is_selected:
if request.method.lower() in self.http_method_names:
+11
View File
@@ -5959,3 +5959,14 @@ class CustomerCheckoutTestCase(BaseCheckoutTestCase, TestCase):
response = self.client.get('/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug), follow=True)
assert 'Gift card' in response.content.decode()
assert '(1 available)' in response.content.decode()
def test_validation_bypass_error_async_id_is_fixed(self):
with scopes_disabled():
CartPosition.objects.create(
event=self.event, cart_id=self.session_key, item=self.ticket,
price=0, listed_price=0, price_after_voucher=0, expires=now() + timedelta(minutes=10)
)
response = self.client.post('/%s/%s/checkout/confirm/?async_id=1' % (self.orga.slug, self.event.slug), follow=False)
self.assertRedirects(response, '/%s/%s/checkout/customer/' % (self.orga.slug, self.event.slug),
target_status_code=200)