Skip to content

Refuse an unregistered Credential subclass in every store's Save, not only AddOrReplace [patch] - #207

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/206-reject-unregistered-type-in-every-store
Nov 10, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/206-reject-unregistered-type-in-every-store

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #206

What was wrong

#194 added CredentialSerialization.ThrowIfUnknownType, but only CredentialCache.AddOrReplace called it. A store used directly, through CredentialStoreFactory.CreateDefault(...) or CredentialCache.Instance.Store, skipped the check:

  • Each platform store's Save let System.Text.Json's raw NotSupportedException escape from Serialize.
  • InMemoryCredentialStore.Save accepted the credential.

As a result, tests against the in-memory store passed while production threw an exception type that nothing around a store call expects.

What changed

  • CredentialSerialization.Serialize and SerializeToString now call ThrowIfUnknownType first. The Windows, macOS and Linux stores, and any custom store built on these helpers, now refuse an unregistered subclass with the same CredentialStoreException and message as AddOrReplace.
  • InMemoryCredentialStore.Save calls it too, and keeps nothing.
  • The README says so, next to the existing AddOrReplace paragraph.

Tests

  • EveryStoreRefusesAnUnregisteredSubclassWithTheSameException: AddOrReplace, InMemoryCredentialStore.Save and the default platform store's Save all throw CredentialStoreException with the same message, and the in-memory store keeps nothing. The test compares messages, which separates the type refusal from a keyring that isn't available.
  • TheSerializationHelpersRefuseAnUnregisteredSubclass: Serialize and SerializeToString throw CredentialStoreException naming the type.

Both tests fail on main: the in-memory Save throws nothing, and Serialize throws NotSupportedException. Both pass with the fix. Full suite on Linux: 82 passed, plus 5 inconclusive that need libsecret or Windows, the same as on main.

PR #203 also edits Serialize (relaxed escaping), so whichever of the two lands second may need a trivial rebase.


Generated by Claude Code

… only AddOrReplace [patch]

#194 added ThrowIfUnknownType, but only CredentialCache.AddOrReplace
called it. A store used directly, through CredentialStoreFactory or
CredentialCache.Instance.Store, skipped the check: each platform store's
Save let System.Text.Json's raw NotSupportedException escape from
Serialize, while InMemoryCredentialStore.Save accepted the credential.
Tests against the in-memory store passed and production threw an
exception type nothing around a store call expects.

CredentialSerialization.Serialize and SerializeToString now call
ThrowIfUnknownType first, so the Windows, macOS and Linux stores and any
custom store built on those helpers refuse an unregistered subclass with
the same CredentialStoreException and message as AddOrReplace.
InMemoryCredentialStore.Save calls it too, and keeps nothing.

Fixes #206

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

sonarqubecloud Bot commented Nov 8, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit bff59fd into main Nov 10, 2026
18 checks passed
@matt-edmondson
matt-edmondson deleted the fix/206-reject-unregistered-type-in-every-store branch November 10, 2026 23:52
matt-edmondson pushed a commit that referenced this pull request Nov 11, 2026
Resolve the conflict with #207 in Serialize and SerializeToString by keeping
both checks: the unknown-type check runs first, then a JsonException from a
null non-nullable property is reported as an ArgumentException.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW3kaZKRNvfNtvM4CX6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants