From b7c1baf5b6912a74f2d836cec8d5dfd04da22743 Mon Sep 17 00:00:00 2001 From: Philipp Wagner Date: Thu, 29 Oct 2026 15:24:39 +0100 Subject: [PATCH] fix: /connect ohne Login erreichbar (Anmeldung direkt als technisches Konto), offene Anfragen begrenzt Claude-Session: https://claude.ai/code/session_01SqzF6qT3TQ8ej3wigEuMSu --- index.html | 5 ++--- oauth.py | 3 +++ tests/test_oauth.py | 7 +++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 719dc11..3dd8da4 100644 --- a/index.html +++ b/index.html @@ -19,9 +19,8 @@ {% endif %} {% else %} -

The collection mailbox is not connected yet. In a private window: open this page, sign in with - your own account, click “Connect mailbox”, then sign in as - {{ email_address }} (technical account).

+

The collection mailbox is not connected yet. Open /connect of this site in a private + window and sign in as {{ email_address }} (technical account).

diff --git a/oauth.py b/oauth.py index 670a192..6f5f4a8 100644 --- a/oauth.py +++ b/oauth.py @@ -21,6 +21,7 @@ from cryptography.fernet import Fernet, InvalidToken SCOPES = "openid profile email offline_access" REFRESH_MARGIN_SEC = 60 PENDING_TTL_SEC = 600 +MAX_PENDING = 20 # /connect is reachable without login: bound the open requests class OAuthError(Exception): @@ -108,6 +109,8 @@ class MailboxOAuth: now = self.clock() with self._lock: self._pending = {k: v for k, v in self._pending.items() if now - v[1] < PENDING_TTL_SEC} + while len(self._pending) >= MAX_PENDING: + del self._pending[min(self._pending, key=lambda k: self._pending[k][1])] self._pending[state] = (verifier, now) query = { "response_type": "code", diff --git a/tests/test_oauth.py b/tests/test_oauth.py index d00212f..ff8c0a9 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -166,3 +166,10 @@ def test_callback_error_is_shown(monkeypatch): monkeypatch.setattr(unsub, "OAUTH", FakeOAuth()) r = unsub.app.test_client().get("/oidc/callback?state=s&code=c") assert r.status_code == 400 and "expected unsubscribe-bot" in r.get_data(as_text=True) + + +def test_pending_requests_are_bounded(tmp_path): + mb = _mb(tmp_path, []) + for _ in range(oauth.MAX_PENDING + 15): + mb.start() + assert len(mb._pending) == oauth.MAX_PENDING