Repository navigation
Conversation
|
9eb0a30 to
9e6efe3
Compare
9e6efe3 to
1ea0399
Compare
1ea0399 to
4962d46
Compare
b23f99f to
a931b1c
Compare
a931b1c to
9120624
Compare
There was a problem hiding this comment.
3 issues found across 22 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="reflex/session_middleware.py">
<violation number="1" location="reflex/session_middleware.py:285">
P2: Deeply nested JSON can make `json.loads` raise `RecursionError`, bypassing this handler and turning an invalid session request into a 500. Catch it with the parse errors so the endpoint returns its 400 response.</violation>
</file>
<file name="packages/reflex-base/src/reflex_base/event/context.py">
<violation number="1" location="packages/reflex-base/src/reflex_base/event/context.py:136">
P2: An empty client token bypasses enforcement even when `session_token` is `None`, but state access can still use that token as its state key. Reject or otherwise guard state access from anonymous tokenless contexts; only explicitly trusted `SessionToken.SYSTEM` work should bypass authorization.</violation>
</file>
<file name="reflex/app.py">
<violation number="1" location="reflex/app.py:2278">
P2: A failed connect can leave `_sessions` and `_session_refresh_at` entries behind because they are populated before `link_token_to_sid`, while cleanup only covers boot-event failures. Wrap the link/state initialization in the same cleanup path and remove the token mapping before re-raising; rejected connects do not receive `on_disconnect`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| ) | ||
|
|
||
| try: | ||
| payload = json.loads(await request.body() or b"{}") |
There was a problem hiding this comment.
P2: Deeply nested JSON can make json.loads raise RecursionError, bypassing this handler and turning an invalid session request into a 500. Catch it with the parse errors so the endpoint returns its 400 response.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At reflex/session_middleware.py, line 285:
<comment>Deeply nested JSON can make `json.loads` raise `RecursionError`, bypassing this handler and turning an invalid session request into a 500. Catch it with the parse errors so the endpoint returns its 400 response.</comment>
<file context>
@@ -0,0 +1,330 @@
+ )
+
+ try:
+ payload = json.loads(await request.body() or b"{}")
+ except (ValueError, UnicodeError):
+ payload = None
</file context>
| payload = json.loads(await request.body() or b"{}") | |
| except (ValueError, UnicodeError, RecursionError): |
| Raises: | ||
| SessionAuthorizationError: If enforcement rejects the client token. | ||
| """ | ||
| if not self.token or ( |
There was a problem hiding this comment.
P2: An empty client token bypasses enforcement even when session_token is None, but state access can still use that token as its state key. Reject or otherwise guard state access from anonymous tokenless contexts; only explicitly trusted SessionToken.SYSTEM work should bypass authorization.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/reflex-base/src/reflex_base/event/context.py, line 136:
<comment>An empty client token bypasses enforcement even when `session_token` is `None`, but state access can still use that token as its state key. Reject or otherwise guard state access from anonymous tokenless contexts; only explicitly trusted `SessionToken.SYSTEM` work should bypass authorization.</comment>
<file context>
@@ -119,20 +127,55 @@ class EventContext(BaseContext):
+ Raises:
+ SessionAuthorizationError: If enforcement rejects the client token.
+ """
+ if not self.token or (
+ self.session_token is not None and self.session_token.authorizes(self.token)
+ ):
</file context>
| issue_session = session is None | ||
| if session is None: | ||
| session = manager.create() | ||
| self._sessions[sid] = session |
There was a problem hiding this comment.
P2: A failed connect can leave _sessions and _session_refresh_at entries behind because they are populated before link_token_to_sid, while cleanup only covers boot-event failures. Wrap the link/state initialization in the same cleanup path and remove the token mapping before re-raising; rejected connects do not receive on_disconnect.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At reflex/app.py, line 2278:
<comment>A failed connect can leave `_sessions` and `_session_refresh_at` entries behind because they are populated before `link_token_to_sid`, while cleanup only covers boot-event failures. Wrap the link/state initialization in the same cleanup path and remove the token mapping before re-raising; rejected connects do not receive `on_disconnect`.</comment>
<file context>
@@ -2199,14 +2263,36 @@ async def on_connect(self, sid: str, environ: dict, auth: Any = None):
+ issue_session = session is None
+ if session is None:
+ session = manager.create()
+ self._sessions[sid] = session
+ self._session_refresh_at[sid] = session.issued_at + manager.refresh_interval
+ if not token or (mode == "enforce" and not session.authorizes(token)):
</file context>
Bind HTTP requests, Reflex socket events, custom WebSocket routes, and uploads to verified browser sessions before accessing state. Cookie-less sockets receive session/client tokens in-band so hydration can start immediately. Fresh clients in
offmode also receive a routing token.POST /_reflex/sessioninstalls an HttpOnly cookie only from a trusted Origin when exchanging an in-band credential; wildcard CORS alone cannot authorize that exchange. Existing valid cookies take precedence. Cookies use Secure, SameSite=None, and Partitioned with the documented development fallback. Enterprise's/_reflex/auth/tokencontract is unchanged.Event contexts inherit browser authority, explicit
Nonemeans anonymous, and trusted server work can useSessionToken.SYSTEM. Custom WebSockets no longer inherit SYSTEM accidentally. Authenticated live sockets can renew their own credential after cookie expiry while preserving owner and tab bindings. HTTP responses advertise the refresh interval. Streamed uploads use a distinct transaction context.The internal transport module is named
reflex.session_middlewareto preserve the publicrx.session()database helper across import orders. Dependency floors and deprecation versions match current main.Depends on #7361; followed by #7363 and #7364. Rebased on main
d02ba5409.Validation: 390 transport/context/upload tests against an isolated real Redis server, two fresh-process database import regressions, full stack unit tests (12,147 passed; 80.15% coverage), and clean
pyright reflex tests. Regressions reproduce the custom-WebSocket authority, off-mode token, and idle-renewal failures before their fixes. Real Uvicorn/Granian/browser coverage is in #7364.Related to ENG-12911, ENG-12912, ENG-12913, ENG-12914, ENG-12915, ENG-9088.