Compare commits

...
Author SHA1 Message Date
dependabot[bot] cbfc24dd64 Update css-inline requirement from ==0.22.* to ==0.23.* (#6683)
Updates the requirements on [css-inline](https://github.com/Stranger6667/css-inline) to permit the latest version.
- [Release notes](https://github.com/Stranger6667/css-inline/releases)
- [Changelog](https://github.com/Stranger6667/css-inline/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stranger6667/css-inline/compare/c-v0.22.0...c-v0.23.0)

---
updated-dependencies:
- dependency-name: css-inline
  dependency-version: 0.23.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-03 17:21:28 +01:00
dependabot[bot] 6e0c2d69a5 Bump pycparser from 3.11 to 3.12 (#6701)
Bumps [pycparser](https://github.com/eliben/pycparser) from 3.11 to 3.12.
- [Release notes](https://github.com/eliben/pycparser/releases)
- [Commits](https://github.com/eliben/pycparser/compare/release_v3.11...release_v3.12)

---
updated-dependencies:
- dependency-name: pycparser
  dependency-version: '3.12'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-03 17:19:46 +01:00
dependabot[bot]andRaphael Michel 34ed175abe Update dnspython requirement from ==2.8.* to ==2.9.* (#6671)
* Update dnspython requirement from ==2.8.* to ==2.9.*

Updates the requirements on [dnspython](https://github.com/rthalley/dnspython) to permit the latest version.
- [Release notes](https://github.com/rthalley/dnspython/releases)
- [Changelog](https://github.com/rthalley/dnspython/blob/main/doc/whatsnew.rst)
- [Commits](https://github.com/rthalley/dnspython/compare/v2.8.0rc1...v2.9.0)

---
updated-dependencies:
- dependency-name: dnspython
  dependency-version: 2.9.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update pyproject.toml

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Raphael Michel <michel@pretix.eu>
2026-11-03 17:19:17 +01:00
dependabot[bot] a9aaf8638e Update pytest-mock requirement from ==3.16.* to ==3.17.* (#6715)
Updates the requirements on [pytest-mock](https://github.com/pytest-dev/pytest-mock) to permit the latest version.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.16.0...v3.17.0)

---
updated-dependencies:
- dependency-name: pytest-mock
  dependency-version: 3.17.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-03 17:18:21 +01:00
Raphael MichelandRaphael Michel a613affa05 Sentry: Scrub secrets from event details (#6218)
* Sentry: Scrub secrets from event details

* Apply suggestions from code review

Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
2026-11-03 16:45:35 +01:00
Raphael Michel cd0ed78b76 Tests: Do not rely on actual DNS for peppol validation (#6711) 2026-11-03 16:44:31 +01:00
Raphael Michel 4889abd75c PDF editor: Warn about missing scannable QR code (#6726)
* PDF editor: Warn about missing scannable QR code

* Fix
2026-11-03 16:43:59 +01:00
10 changed files with 97 additions and 36 deletions

No files matched your search

+5 -7
View File
@@ -34,9 +34,9 @@ dependencies = [
"celery==5.6.*",
"chardet==5.2.*",
"cryptography>=50.0.2",
"css-inline==0.22.*",
"css-inline==0.23.*",
"defusedcsv>=3.0.0",
"dnspython==2.*",
"dnspython==2.9.*",
"Django[argon2]==5.2.*,>=5.2.17",
"django-bootstrap3==26.3",
"django-compressor==4.6.0",
@@ -58,7 +58,6 @@ dependencies = [
"django-scopes==2.1.*",
"django-statici18n==2.8.*",
"djangorestframework>=3.18.3",
"dnspython==2.8.*",
"drf_ujson2==1.7.*",
"geoip2==5.*",
"importlib_metadata==9.*", # Polyfill, we can probably drop this once we require Python 3.10+
@@ -67,8 +66,7 @@ dependencies = [
"kombu==5.6.*",
"libsass==0.23.*",
"lxml",
"markdown==3.11.2", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
# We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
"markdown==3.11.2",
"mt-940==4.30.*",
"oauthlib==3.3.*",
"openpyxl==3.1.*",
@@ -82,7 +80,7 @@ dependencies = [
"protobuf==7.36.*",
"psycopg2-binary",
"pycountry",
"pycparser==3.11",
"pycparser==3.12",
"pycryptodome==3.24.*",
"pypdf==6.20.*",
"python-bidi==0.6.*", # Support for Arabic in reportlab
@@ -122,7 +120,7 @@ dev = [
"pytest-cache",
"pytest-cov",
"pytest-django==4.*",
"pytest-mock==3.16.*",
"pytest-mock==3.17.*",
"pytest-sugar",
"pytest-xdist==3.8.*",
"pytest-playwright",
+22 -19
View File
@@ -131,6 +131,27 @@ class PeppolIdValidator:
def __init__(self, validate_online=False):
self.validate_online = validate_online
def _validate_peppol_id_online(self, value):
base_hostnames = ['participant.sml.prod.tech.peppol.org', 'participant.sml.test.tech.peppol.org']
smp_id = base64.b32encode(hashlib.sha256(value.lower().encode()).digest()).decode().rstrip("=")
for base_hostname in base_hostnames:
smp_domain = f'{smp_id}.iso6523-actorid-upis.{base_hostname}'
resolver = dns.resolver.Resolver()
try:
answers = resolver.resolve(smp_domain, 'NAPTR', lifetime=1.0)
if answers:
return value
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
# ID not registered, do not set found=True
pass
except Exception: # noqa
# Error likely on our end or infrastructure is down, allow user to proceed
return value
raise ValidationError(
_("The Peppol participant ID is not registered on the Peppol network."),
)
def __call__(self, value):
if ":" not in value:
raise ValidationError(_("A Peppol participant ID always starts with a prefix, followed by a colon (:)."))
@@ -146,25 +167,7 @@ class PeppolIdValidator:
params={"number": prefix})
if self.validate_online:
base_hostnames = ['participant.sml.prod.tech.peppol.org', 'participant.sml.test.tech.peppol.org']
smp_id = base64.b32encode(hashlib.sha256(value.lower().encode()).digest()).decode().rstrip("=")
for base_hostname in base_hostnames:
smp_domain = f'{smp_id}.iso6523-actorid-upis.{base_hostname}'
resolver = dns.resolver.Resolver()
try:
answers = resolver.resolve(smp_domain, 'NAPTR', lifetime=1.0)
if answers:
return value
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
# ID not registered, do not set found=True
pass
except Exception: # noqa
# Error likely on our end or infrastructure is down, allow user to proceed
return value
raise ValidationError(
_("The Peppol participant ID is not registered on the Peppol network."),
)
value = self._validate_peppol_id_online(value)
return value
@@ -225,6 +225,14 @@
</div>
</div>
<div class="col-md-3" id="editor-toolbox-area">
{% if checkin_qr_warning %}
<div class="alert alert-warning" id="nocheckin-notice">
{% blocktrans trimmed %}
This layout does not contain a QR code that can be used to scan tickets at check-in.
{% endblocktrans %}
</div>
{% endif %}
<div class="panel panel-default" id="toolbox">
<div class="panel-heading">
<span id="toolbox-heading">
+2
View File
@@ -65,6 +65,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
)
maxfilesize = settings.FILE_UPLOAD_MAX_SIZE_IMAGE
minfilesize = 10
checkin_qr_warning = False
title = None
def get(self, request, *args, **kwargs):
@@ -292,6 +293,7 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
ctx['title'] = self.title
ctx['locales'] = [p for p in settings.LANGUAGES if p[0] in self.request.event.settings.locales]
ctx['maxfilesize'] = self.maxfilesize
ctx['checkin_qr_warning'] = self.checkin_qr_warning
return ctx
@@ -235,6 +235,7 @@ class LayoutGetDefault(EventPermissionRequiredMixin, View):
class LayoutEditorView(BaseEditorView):
checkin_qr_warning = True
@cached_property
def layout(self):
+35 -1
View File
@@ -19,12 +19,18 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import re
import typing
import weakref
from celery.exceptions import Retry
from sentry_sdk import Hub
from sentry_sdk.integrations import django as djangosentry
from sentry_sdk.utils import capture_internal_exceptions
from sentry_sdk.scrubber import EventScrubber
from sentry_sdk.utils import AnnotatedValue, capture_internal_exceptions
if typing.TYPE_CHECKING:
from sentry_sdk._types import Event
def _make_event_processor(weak_request, integration):
@@ -96,3 +102,31 @@ def setup_custom_filters():
hub = Hub.current
with hub.configure_scope() as scope:
scope.add_event_processor(ignore_retry)
class PretixEventScrubber(EventScrubber):
secret_re = re.compile(
'.*(pretixsecret|ramiiosecret|PRIVATE KEY|://[^/@:]*:[^/@:]+@).*',
re.IGNORECASE
)
def scrub_dict(self, d: object) -> None:
if not isinstance(d, dict):
return
super().scrub_dict(d)
self._scrub_dict_for_known_secrets(d)
def _scrub_dict_for_known_secrets(self, d: dict):
for k, v in d.items():
if isinstance(v, str) and self.secret_re.match(v):
d[k] = AnnotatedValue.substituted_because_contains_sensitive_data()
def scrub_exception(self, event: "Event") -> None:
with capture_internal_exceptions():
if "exception" in event:
self.scrub_dict(event["exception"])
def scrub_event(self, event: "Event") -> None:
super().scrub_event(event)
self.scrub_exception(event)
+3 -3
View File
@@ -721,9 +721,9 @@ if config.has_option('sentry', 'dsn') and not any(c in sys.argv for c in ('shell
from sentry_sdk.integrations.logging import (
LoggingIntegration, ignore_logger,
)
from sentry_sdk.scrubber import DEFAULT_DENYLIST, EventScrubber
from sentry_sdk.scrubber import DEFAULT_DENYLIST
from .sentry import PretixSentryIntegration, setup_custom_filters
from .sentry import PretixSentryIntegration, PretixEventScrubber, setup_custom_filters
SENTRY_TOKEN = config.get('sentry', 'traces_sample_token', fallback='')
SENTRY_ENABLE_LOGS = config.getboolean('sentry', 'enable_logs', fallback=False)
@@ -759,7 +759,7 @@ if config.has_option('sentry', 'dsn') and not any(c in sys.argv for c in ('shell
traces_sampler=traces_sampler,
environment=urlparse(SITE_URL).netloc,
release=__version__,
event_scrubber=EventScrubber(denylist=pretix_denylist, recursive=True),
event_scrubber=PretixEventScrubber(denylist=pretix_denylist, recursive=True),
send_default_pii=False,
propagate_traces=False, # see https://github.com/getsentry/sentry-python/issues/1717
)
@@ -618,18 +618,21 @@ var editor = {
$('#loading-initial').remove()
})
}
editor._update_version_notice()
editor._update_notices()
},
_update_version_notice: function () {
_update_notices: function () {
$('#version-notice').toggle(
editor._other_page_objects.some((o) => o.type === 'textcontainer')
|| editor.fabric.getObjects().some((o) => o.type === 'textcontainer')
)
$('#nocheckin-notice').toggle(
!editor.fabric.getObjects().some((o) => o.type === 'barcodearea' && (!o.content || o.content === 'secret'))
)
},
_update_toolbox_values: function () {
editor._update_version_notice()
editor._update_notices()
let o = editor.fabric.getActiveObject()
if (!o) {
return
@@ -898,6 +901,7 @@ var editor = {
o.setCoords()
editor.fabric.renderAll()
editor._update_notices()
},
_update_toolbox: function () {
@@ -103,3 +103,6 @@ body {
.panel-pdf-editor .panel-heading form > .btn-group {
margin-right: 1em;
}
#nocheckin-notice {
display: none;
}
+11 -3
View File
@@ -26,6 +26,7 @@ from unittest import mock
import pytest
from django.core import mail as djmail
from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.utils.timezone import now
from django_countries.fields import Country
@@ -248,7 +249,14 @@ def test_order_update_state_validation(token_client, organizer, event, order):
@pytest.mark.django_db
def test_order_update_transmission_validation(token_client, organizer, event, order):
def test_order_update_transmission_validation(token_client, organizer, event, order, monkeypatch):
def mock_validate(self, value):
if value == "9930:DE111111111":
return value
raise ValidationError("Invalid")
monkeypatch.setattr("pretix.base.invoicing.peppol.PeppolIdValidator._validate_peppol_id_online", mock_validate)
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/orders/{}/'.format(
organizer.slug, event.slug, order.code
@@ -374,7 +382,7 @@ def test_order_update_transmission_validation(token_client, organizer, event, or
"vat_id": "",
"transmission_type": "peppol",
"transmission_info": {
"transmission_peppol_participant_id": "9930:DE811569869",
"transmission_peppol_participant_id": "9930:DE111111111",
"ignored": "parameter",
},
}
@@ -384,7 +392,7 @@ def test_order_update_transmission_validation(token_client, organizer, event, or
order.invoice_address.refresh_from_db()
assert order.invoice_address.transmission_type == "peppol"
assert order.invoice_address.transmission_info == {
"transmission_peppol_participant_id": "9930:DE811569869",
"transmission_peppol_participant_id": "9930:DE111111111",
}