What's wrong
CredentialSerialization.BuildOptions (CredentialCache/Storage/CredentialSerialization.cs:~239-249) creates JsonSerializerOptions without setting Encoder, so System.Text.Json uses JavaScriptEncoder.Default. That encoder escapes these characters as \uXXXX, which is 6 bytes each:
+, <, >, & and '
- every non-ASCII character
WindowsCredentialStore.Save (Storage/WindowsCredentialStore.cs:75-81) checks the escaped blob against CRED_MAX_CREDENTIAL_BLOB_SIZE (2560). The README's platform notes say the cap applies to "tokens larger than that". In practice the usable size is much smaller, and it depends on what the secret contains.
Failure scenario
Measured on net10.0:
- A standard base64 token (alphabet includes
+) of 2400 characters serializes to 2662 bytes. Save throws CredentialStoreException on Windows, while Linux and macOS accept the same credential. With UnsafeRelaxedJsonEscaping it is 2442 bytes and fits.
- A 2000-char base64 token containing 36
+ becomes 2222 bytes, and the JSON contains + where the + was.
{"Username":"пользователь","Password":"密码密码密码"} becomes 178 bytes, with every character written as \u. Cyrillic grows from 2 to 6 bytes per character and CJK from 3 to 6, so non-ASCII secrets reach the limit 2–3× sooner.
Suggested fix
- Set
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping in BuildOptions. The payload is never embedded in HTML or script, so HTML-safe escaping buys nothing.
- This is backward compatible: the deserializer reads both escaped and unescaped forms, so credentials already stored still load.
- Optionally reword the README note to say the limit counts the serialized (UTF-8 JSON) size.
Acceptance criteria
- A test asserts that serializing a credential whose password contains
+ and non-ASCII characters produces raw UTF-8 rather than \u escapes.
- A test asserts that a previously serialized, escaped blob still deserializes.
Related, not a duplicate: #165 covers scrubbing on the over-limit path.
What's wrong
CredentialSerialization.BuildOptions(CredentialCache/Storage/CredentialSerialization.cs:~239-249) createsJsonSerializerOptionswithout settingEncoder, so System.Text.Json usesJavaScriptEncoder.Default. That encoder escapes these characters as\uXXXX, which is 6 bytes each:+,<,>,&and'WindowsCredentialStore.Save(Storage/WindowsCredentialStore.cs:75-81) checks the escaped blob againstCRED_MAX_CREDENTIAL_BLOB_SIZE(2560). The README's platform notes say the cap applies to "tokens larger than that". In practice the usable size is much smaller, and it depends on what the secret contains.Failure scenario
Measured on net10.0:
+) of 2400 characters serializes to 2662 bytes.SavethrowsCredentialStoreExceptionon Windows, while Linux and macOS accept the same credential. WithUnsafeRelaxedJsonEscapingit is 2442 bytes and fits.+becomes 2222 bytes, and the JSON contains+where the+was.{"Username":"пользователь","Password":"密码密码密码"}becomes 178 bytes, with every character written as\u. Cyrillic grows from 2 to 6 bytes per character and CJK from 3 to 6, so non-ASCII secrets reach the limit 2–3× sooner.Suggested fix
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscapinginBuildOptions. The payload is never embedded in HTML or script, so HTML-safe escaping buys nothing.Acceptance criteria
+and non-ASCII characters produces raw UTF-8 rather than\uescapes.Related, not a duplicate: #165 covers scrubbing on the over-limit path.