What's wrong
WindowsCredentialStore.Save (CredentialCache/Storage/WindowsCredentialStore.cs, ~lines 75–108) serializes the credential into byte[] blob and then checks its size before entering the try { … } finally { CredentialSerialization.Zero(blob); } block:
byte[] blob = CredentialSerialization.Serialize(credential);
if (blob.Length > NativeMethods.CRED_MAX_CREDENTIAL_BLOB_SIZE)
{
throw new CredentialStoreException(...); // blob is never zeroed on this path
}
try
{
...
}
finally
{
CredentialSerialization.Zero(blob);
}
When the size check throws, the full plaintext JSON (username/password or token) stays on the managed heap until the GC reclaims it, and it is never zeroed.
Why it matters
The recent scrubbing work (e.g. 97a49ab, "scrub Linux Secret Service plaintext like Windows and macOS") commits the library to zeroing every managed plaintext copy on every path, including when an exception is thrown. The macOS Save and the Linux path both honour this. The Windows over-limit path is the only one that doesn't.
Concrete scenario: someone stores a CredentialWithToken holding a long JWT or PAT whose serialized form is over 2560 bytes. Save throws CredentialStoreException as it should, but the serialized secret is left in memory unscrubbed. It is then exposed to memory dumps, crash dumps and swap, which is exactly what the scrubbing is meant to prevent.
Suggested fix
Move the size check inside the try, so the finally covers it:
byte[] blob = CredentialSerialization.Serialize(credential);
try
{
if (blob.Length > NativeMethods.CRED_MAX_CREDENTIAL_BLOB_SIZE)
{
throw new CredentialStoreException(...);
}
...
}
finally
{
CredentialSerialization.Zero(blob);
}
Acceptance criteria
blob is zeroed on every exit path of WindowsCredentialStore.Save, including the over-limit throw.
- If the shared-layer tests can observe it, add a test that saves an oversize credential and asserts the buffer was scrubbed. At minimum, add a test asserting the throw still happens.
What's wrong
WindowsCredentialStore.Save(CredentialCache/Storage/WindowsCredentialStore.cs, ~lines 75–108) serializes the credential intobyte[] bloband then checks its size before entering thetry { … } finally { CredentialSerialization.Zero(blob); }block:When the size check throws, the full plaintext JSON (username/password or token) stays on the managed heap until the GC reclaims it, and it is never zeroed.
Why it matters
The recent scrubbing work (e.g. 97a49ab, "scrub Linux Secret Service plaintext like Windows and macOS") commits the library to zeroing every managed plaintext copy on every path, including when an exception is thrown. The macOS
Saveand the Linux path both honour this. The Windows over-limit path is the only one that doesn't.Concrete scenario: someone stores a
CredentialWithTokenholding a long JWT or PAT whose serialized form is over 2560 bytes.SavethrowsCredentialStoreExceptionas it should, but the serialized secret is left in memory unscrubbed. It is then exposed to memory dumps, crash dumps and swap, which is exactly what the scrubbing is meant to prevent.Suggested fix
Move the size check inside the
try, so thefinallycovers it:Acceptance criteria
blobis zeroed on every exit path ofWindowsCredentialStore.Save, including the over-limit throw.