From 311f4a2c0c905126e14fad243d2d7836373454e8 Mon Sep 17 00:00:00 2001 From: Thor Whalen <1906276+thorwhalen@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:46:54 +0000 Subject: [PATCH] Fix OAuth authorize 500: encode login `next` URL as JS, not HTML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sign-in page threads the post-login destination back through inline JS (`const NEXT = "..."`), but built that literal with `html.escape()`. Inside a ``` breakout) while the JS engine + decodes them back to the original characters at runtime. Returns the literal + *including* its surrounding quotes. + """ + return ( + json.dumps(value) + .replace("&", "\\u0026") + .replace("<", "\\u003c") + .replace(">", "\\u003e") + .replace("\u2028", "\\u2028") + .replace("\u2029", "\\u2029") + ) + # -------------------------------------------------------------------------- # Shared chrome # -------------------------------------------------------------------------- @@ -191,7 +215,7 @@ def render_login_page( error: optional error banner (e.g. a stale-session note). show_register_hint: whether to show the "ask an admin" footnote. """ - next_js = escape(next_url, quote=True) + next_js = _js_string(next_url) hint = ( '

No account? Accounts are created by the platform admin.

' if show_register_hint @@ -218,7 +242,7 @@ def render_login_page( &c=1") + assert "" not in lit and "<" not in lit and ">" not in lit and "&" not in lit + # ...but the runtime value is exactly the original, untouched. + assert _js_runtime_value(lit) == "/x?a=&c=1" + + +def test_login_page_does_not_html_escape_next_into_js(): + page = render_login_page(next_url=AUTHZ) + # The bug's fingerprint: an HTML entity where a raw ampersand must be. + assert "&client_id" not in page + # The fix's fingerprint: query separators carried as JS unicode escapes. + assert "\\u0026client_id" in page + + +def test_shared_login_page_does_not_html_escape_next_into_js(): + page = render_shared_login_page(app="demo", next_url=AUTHZ) + assert "&client_id" not in page + assert "\\u0026client_id" in page