What's wrong
WindowsCredentialStore.cs and MacOsCredentialStore.cs both load the stored secret into a native/pinned byte buffer and deserialize it via CredentialSerialization.DeserializeAndScrub(blob) (both at line 60), which per CredentialSerialization.cs zeroes the plaintext bytes after use.
LinuxSecretServiceCredentialStore.cs does not do this:
TryLoad (lines 58-67): converts the native secret straight into a managed string via Marshal.PtrToStringUTF8(passwordPtr), then calls CredentialSerialization.DeserializeFromString(value) — the non-scrubbing string-based path, not DeserializeAndScrub.
Save (lines 75-93): builds the plaintext JSON as a managed string via CredentialSerialization.SerializeToString(credential) and passes it directly to secret_password_store_sync.
Why it matters
.NET strings are immutable, so once the plaintext credential exists as a managed string, it cannot be actively zeroed — it just sits on the GC heap until eventually collected (and may be copied during compaction in the meantime). This is precisely the exposure CredentialSerialization.cs (lines ~68-75) documents as the reason DeserializeAndScrub/NativeSecretBuffer exist in the first place, and it's the same class of issue previously fixed for Windows in issue #144 (Windows buffer scrubbing). That fix was never applied to the Linux store, so on Linux specifically, every credential loaded from or saved to the Secret Service passes through plaintext managed strings that linger in memory (visible to crash dumps, memory-scanning tools, or a GC heap dump) for an unbounded time after use.
Suggested fix
Route the Linux store through the same byte-array + scrub discipline as Windows/macOS:
TryLoad: use secret_password_lookup_binary_sync (or marshal the UTF-8 bytes directly rather than via PtrToStringUTF8) into a byte[]/native buffer, then call CredentialSerialization.DeserializeAndScrub.
Save: serialize via CredentialSerialization.Serialize to a byte[], pass that to the native call, and call CredentialSerialization.Zero (or equivalent) on the buffer afterward.
If libsecret's synchronous API genuinely offers no byte-array entry point and a managed string is unavoidable, at minimum document that limitation explicitly in the store's doc comment (it currently reads as if scrubbing were platform-uniform) so it's a documented, deliberate gap rather than an inconsistency discovered by inspection.
What's wrong
WindowsCredentialStore.csandMacOsCredentialStore.csboth load the stored secret into a native/pinned byte buffer and deserialize it viaCredentialSerialization.DeserializeAndScrub(blob)(both at line 60), which perCredentialSerialization.cszeroes the plaintext bytes after use.LinuxSecretServiceCredentialStore.csdoes not do this:TryLoad(lines 58-67): converts the native secret straight into a managedstringviaMarshal.PtrToStringUTF8(passwordPtr), then callsCredentialSerialization.DeserializeFromString(value)— the non-scrubbing string-based path, notDeserializeAndScrub.Save(lines 75-93): builds the plaintext JSON as a managedstringviaCredentialSerialization.SerializeToString(credential)and passes it directly tosecret_password_store_sync.Why it matters
.NET strings are immutable, so once the plaintext credential exists as a managed
string, it cannot be actively zeroed — it just sits on the GC heap until eventually collected (and may be copied during compaction in the meantime). This is precisely the exposureCredentialSerialization.cs(lines ~68-75) documents as the reasonDeserializeAndScrub/NativeSecretBufferexist in the first place, and it's the same class of issue previously fixed for Windows in issue #144 (Windows buffer scrubbing). That fix was never applied to the Linux store, so on Linux specifically, every credential loaded from or saved to the Secret Service passes through plaintext managed strings that linger in memory (visible to crash dumps, memory-scanning tools, or a GC heap dump) for an unbounded time after use.Suggested fix
Route the Linux store through the same byte-array + scrub discipline as Windows/macOS:
TryLoad: usesecret_password_lookup_binary_sync(or marshal the UTF-8 bytes directly rather than viaPtrToStringUTF8) into abyte[]/native buffer, then callCredentialSerialization.DeserializeAndScrub.Save: serialize viaCredentialSerialization.Serializeto abyte[], pass that to the native call, and callCredentialSerialization.Zero(or equivalent) on the buffer afterward.If libsecret's synchronous API genuinely offers no byte-array entry point and a managed string is unavoidable, at minimum document that limitation explicitly in the store's doc comment (it currently reads as if scrubbing were platform-uniform) so it's a documented, deliberate gap rather than an inconsistency discovered by inspection.