encadre

Encadre, petit framework web Python (Flask) : contrôleurs en greffons, validation, tests
git clone https://git.schurger.org/encadre.git
Log | Files | Refs

commit 617304f7cf65b98d89e8d9b07f0dd4f0fad315d3
parent 38668bb4e59540d028ae0d273fc5eb395ed4d248
Author: Jean Schurger <jean@schurger.org>
Date:   Fri, 18 Sep 2026 08:42:10 -0400

flask: keep pending Set-Cookie on flask.g, not on the shared framework.

With uwsgi threads=2 the framework singleton's _cookies list was shared
by concurrent requests: a /auth/refresh rotating user B's refresh cookie
could be flushed into the response of an unrelated request from user A,
whose device then refreshed as B (drivers seeing another driver's
orders, tablets 'changing user' without any login).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mencadre_flask/__init__.py | 13+++++++++----
Mpyproject.toml | 2+-
2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/encadre_flask/__init__.py b/encadre_flask/__init__.py @@ -2,7 +2,7 @@ import os import sys import re import logging -from flask import Flask, request, send_file, abort +from flask import Flask, request, send_file, abort, g from flask_cors import CORS from flask_jwt_extended import JWTManager from encadre import Framework @@ -72,7 +72,7 @@ class FlaskFramework(Framework): "method: '%s', version: '%s', verb: '%s'" % (ctrl_name, method_name, v, verb)) flask.abort(405) - self._cookies = [] + flask.g._encadre_cookies = [] try: ret = getattr(cls(), m)(**args) except Exception as e: @@ -149,7 +149,10 @@ class FlaskFramework(Framework): return self.flask_app def _set_cookies(self, response): - for args, kwargs in getattr(self, '_cookies', {}): + # Pending cookies live on flask.g (request-local): a framework + # attribute is shared by every thread of the worker, so a + # concurrent request would flush another user's Set-Cookie. + for args, kwargs in getattr(g, '_encadre_cookies', ()): response.set_cookie(*args, **kwargs) return response @@ -175,7 +178,9 @@ class FlaskFramework(Framework): return request.cookies.get(key) def set_cookie(self, *args, **kwargs): - self._cookies.append((args, kwargs)) + if not hasattr(g, '_encadre_cookies'): + g._encadre_cookies = [] + g._encadre_cookies.append((args, kwargs)) def send_bytes(self, bytes_like, **kwargs): return send_file(bytes_like, **kwargs) diff --git a/pyproject.toml b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "encadre" -version = "0.3.0" +version = "0.3.1" description = "Encadre framework" authors = [ {name = "Jean Schurger", email = "jean@schurger.org"}