Repository navigation
Conversation
…kies stringToUTF8 masked the high-surrogate contribution to 16 bits, dropping the top bits of every codepoint at or above U+20000. Session cookies written with the default base64url encoding then decode to a different character, e.g. 𠮷 (U+20BB7) comes back as (U+10BB7), so names from user_metadata in CJK Extension B and later are silently corrupted.
Member
|
I've allowed the workflow run as the change looks sensible and scoped. I'll leave review approval to someone else on our team who has a closer working knowledge of this area. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What kind of change does this PR introduce?
Bug fix.
What is the current behavior?
stringToUTF8insrc/utils/base64url.tscombines a UTF-16 surrogate pair like this:The high surrogate holds the top 10 of the 20 bits, so
(hi - 0xd800) * 0x400can be as large as0xffc00. The& 0xffffmask drops bits 16–19. The result is wrong for every codepoint at or above U+20000. That covers CJK Extension B and later, which appear in Japanese and Chinese personal names.Because
base64urlis the defaultcookieEncoding, the session JSON is written through this function, souser_metadatachanges on every cookie round trip. For example,𠮷(U+20BB7, as in 𠮷田) comes back as(U+10BB7). The decoder is correct, so there is no error. The name is just wrong, both in the server client'sgetSession()and in the browser client after a reload. Emoji are not affected: they sit below U+20000, so the dropped bits are always zero for them.I reproduced this through
createServerClient. I stored a session whoseuser_metadata.full_nameis𠮷田 太郎via the cookie storage and read it back with a second server client. Onmainthe name read back is田 太郎. With this change it is𠮷田 太郎.What is the new behavior?
The high surrogate is no longer masked to 16 bits, so
stringToBase64URLmatchesBuffer.from(str).toString("base64url")for the whole Unicode range. The low-surrogate mask and the lone-surrogate behavior are unchanged.Tests: I added two examples to the existing
EXAMPLEStable insrc/utils/base64url.test.ts, which compares against Node'sBuffer. One is a string of U+20000+ characters (𠮷野家 𩸽 𪚲). The other isU+10FFFF, the highest codepoint. Bothencodecases fail onmain(for example,expected '8J-_vw' to deeply equal '9I-_vw') and pass with this change.decodealready passed, since the decoder was correct.pnpm test: 124/124 pass.pnpm build(both tsc configs),pnpm exec eslintandpnpm exec prettier --check .are clean.Additional context
This line has been unchanged since the
getAll/setAllrewrite (#1).@supabase/auth-jshas the same helper, but there it only encodes ASCII JWT segments, so nothing user-facing goes through the affected branch.AI disclosure: an AI agent working from the
breken-aiaccount found this bug and wrote the fix and the tests. The red/green results above come from running the repository's own test suite.