Skip to content

Write credential JSON with relaxed escaping so Windows' blob cap counts real bytes [patch] - #203

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/175-relaxed-json-escaping
Nov 11, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/175-relaxed-json-escaping

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #175

What was wrong

CredentialSerialization.BuildOptions used System.Text.Json's default JavaScriptEncoder. That encoder writes +, <, >, &, ' and every non-ASCII character as a six-byte \uXXXX. A 2400-character base64 token came out at 2662 bytes, so WindowsCredentialStore.Save rejected it against the 2560-byte cap, while Linux and macOS stored the same credential.

Change

  • BuildOptions sets Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping. The payload is never embedded in HTML or script, so HTML-safe escaping does nothing for it. The reader accepts both forms, so credentials already stored still load.
  • Adds an explicit System.Text.Encodings.Web 10.0.14 reference, which KTSU0006 requires for direct use of a transitive package. It is the version System.Text.Json 10.0.14 already brings in.
  • The README's Windows note now says the cap counts the serialized UTF-8 JSON.

Tests

New SerializationEscapingTests:

  • Serialize and SerializeToString write +, <>&', Cyrillic and CJK as raw UTF-8, with no \u.
  • A base64 token containing + serializes to no more than its own length plus the envelope.
  • A blob written with the old escapes still deserializes through both Deserialize and DeserializeFromString.

Without the fix, the first three fail and the backward-compatibility test passes. The full suite on net10.0 (Linux): 89 tests, 84 passed, 0 failed, 5 skipped. The skips are the existing native-store tests that need libsecret or Windows.


Generated by Claude Code

…ts real bytes

System.Text.Json's default encoder wrote '+', '<', '>', '&', '\'' and every
non-ASCII character as a six-byte \uXXXX escape, so a 2400-character base64
token serialized to 2662 bytes and Windows Credential Manager rejected it
against its 2560-byte cap. Serialize with UnsafeRelaxedJsonEscaping instead;
the payload is never embedded in HTML or script, and blobs written with the
old escapes still deserialize.

Fixes #175

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW3kaZKRNvfNtvM4CX6
@sonarqubecloud

sonarqubecloud Bot commented Nov 6, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 306fb41 into main Nov 11, 2026
18 checks passed
@matt-edmondson
matt-edmondson deleted the fix/175-relaxed-json-escaping branch November 11, 2026 00:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows rejects a 2400-char base64 token as over the 2560-byte limit: JSON escaping writes every '+' and non-ASCII char as \uXXXX (6 bytes)

2 participants