Security: DecryptWithIdentity leaks plaintext in unprotected memory #5

Open
opened 2026-02-08 21:02:00 +01:00 by clawbot · 0 comments

Security Issue

In internal/secret/crypto.go, DecryptWithIdentity() reads decrypted data into a regular byte slice via io.ReadAll(), then copies it into a memguard.LockedBuffer:

result, err := io.ReadAll(r)
if err != nil {
    return nil, fmt.Errorf("failed to read decrypted data: %w", err)
}
resultBuffer := memguard.NewBufferFromBytes(result)
return resultBuffer, nil

memguard.NewBufferFromBytes copies the data into protected memory, but the original result byte slice remains in regular (swappable, dumpable) memory and is never zeroed. This defeats the purpose of using memguard throughout the codebase.

Impact

Decrypted secrets (private keys, secret values, metadata) linger in unprotected heap memory and could be:

  • Swapped to disk
  • Visible in core dumps
  • Read by memory-scanning malware

Fix

Zero out the result slice after copying into the LockedBuffer:

resultBuffer := memguard.NewBufferFromBytes(result)
for i := range result {
    result[i] = 0
}
## Security Issue In `internal/secret/crypto.go`, `DecryptWithIdentity()` reads decrypted data into a regular byte slice via `io.ReadAll()`, then copies it into a `memguard.LockedBuffer`: ```go result, err := io.ReadAll(r) if err != nil { return nil, fmt.Errorf("failed to read decrypted data: %w", err) } resultBuffer := memguard.NewBufferFromBytes(result) return resultBuffer, nil ``` `memguard.NewBufferFromBytes` copies the data into protected memory, but the original `result` byte slice remains in regular (swappable, dumpable) memory and is never zeroed. This defeats the purpose of using `memguard` throughout the codebase. ## Impact Decrypted secrets (private keys, secret values, metadata) linger in unprotected heap memory and could be: - Swapped to disk - Visible in core dumps - Read by memory-scanning malware ## Fix Zero out the `result` slice after copying into the `LockedBuffer`: ```go resultBuffer := memguard.NewBufferFromBytes(result) for i := range result { result[i] = 0 } ```
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sneak/secret#5
No description provided.