131 lines
3.7 KiB
Go
131 lines
3.7 KiB
Go
package secretstore
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestBitwardenCacheMemoryHit(t *testing.T) {
|
|
cache := newBitwardenCache(bitwardenCacheOptions{
|
|
ServiceName: "email-mcp",
|
|
Session: "session-v1",
|
|
TTL: 10 * time.Minute,
|
|
Now: func() time.Time { return time.Date(2026, 5, 2, 10, 0, 0, 0, time.UTC) },
|
|
CacheDir: t.TempDir(),
|
|
Enabled: true,
|
|
})
|
|
|
|
cache.store("api-token", "email-mcp/api-token", "secret-v1")
|
|
got, ok := cache.loadMemory("api-token", "email-mcp/api-token")
|
|
if !ok {
|
|
t.Fatal("memory cache miss, want hit")
|
|
}
|
|
if got != "secret-v1" {
|
|
t.Fatalf("memory cache value = %q, want secret-v1", got)
|
|
}
|
|
}
|
|
|
|
func TestBitwardenCacheDiskRoundTripIsEncrypted(t *testing.T) {
|
|
dir := t.TempDir()
|
|
now := time.Date(2026, 5, 2, 10, 0, 0, 0, time.UTC)
|
|
cache := newBitwardenCache(bitwardenCacheOptions{
|
|
ServiceName: "email-mcp",
|
|
Session: "session-v1",
|
|
TTL: 10 * time.Minute,
|
|
Now: func() time.Time { return now },
|
|
CacheDir: dir,
|
|
Enabled: true,
|
|
})
|
|
|
|
cache.store("api-token", "email-mcp/api-token", "secret-v1")
|
|
|
|
reopened := newBitwardenCache(bitwardenCacheOptions{
|
|
ServiceName: "email-mcp",
|
|
Session: "session-v1",
|
|
TTL: 10 * time.Minute,
|
|
Now: func() time.Time { return now.Add(time.Minute) },
|
|
CacheDir: dir,
|
|
Enabled: true,
|
|
})
|
|
got, ok := reopened.loadDisk("api-token", "email-mcp/api-token")
|
|
if !ok {
|
|
t.Fatal("disk cache miss, want hit")
|
|
}
|
|
if got != "secret-v1" {
|
|
t.Fatalf("disk cache value = %q, want secret-v1", got)
|
|
}
|
|
|
|
entries, err := os.ReadDir(dir)
|
|
if err != nil {
|
|
t.Fatalf("ReadDir cache dir: %v", err)
|
|
}
|
|
if len(entries) != 1 {
|
|
t.Fatalf("cache file count = %d, want 1", len(entries))
|
|
}
|
|
data, err := os.ReadFile(filepath.Join(dir, entries[0].Name()))
|
|
if err != nil {
|
|
t.Fatalf("ReadFile cache file: %v", err)
|
|
}
|
|
if bytes.Contains(data, []byte("secret-v1")) {
|
|
t.Fatalf("cache file contains plaintext secret: %s", data)
|
|
}
|
|
if strings.Contains(entries[0].Name(), "api-token") {
|
|
t.Fatalf("cache file name exposes secret name: %s", entries[0].Name())
|
|
}
|
|
}
|
|
|
|
func TestBitwardenCacheRejectsChangedSession(t *testing.T) {
|
|
dir := t.TempDir()
|
|
now := time.Date(2026, 5, 2, 10, 0, 0, 0, time.UTC)
|
|
cache := newBitwardenCache(bitwardenCacheOptions{
|
|
ServiceName: "email-mcp",
|
|
Session: "session-v1",
|
|
TTL: 10 * time.Minute,
|
|
Now: func() time.Time { return now },
|
|
CacheDir: dir,
|
|
Enabled: true,
|
|
})
|
|
cache.store("api-token", "email-mcp/api-token", "secret-v1")
|
|
|
|
changed := newBitwardenCache(bitwardenCacheOptions{
|
|
ServiceName: "email-mcp",
|
|
Session: "session-v2",
|
|
TTL: 10 * time.Minute,
|
|
Now: func() time.Time { return now.Add(time.Minute) },
|
|
CacheDir: dir,
|
|
Enabled: true,
|
|
})
|
|
if got, ok := changed.loadDisk("api-token", "email-mcp/api-token"); ok {
|
|
t.Fatalf("disk cache hit with changed session = %q, want miss", got)
|
|
}
|
|
}
|
|
|
|
func TestBitwardenCacheExpiresEntries(t *testing.T) {
|
|
dir := t.TempDir()
|
|
now := time.Date(2026, 5, 2, 10, 0, 0, 0, time.UTC)
|
|
cache := newBitwardenCache(bitwardenCacheOptions{
|
|
ServiceName: "email-mcp",
|
|
Session: "session-v1",
|
|
TTL: time.Minute,
|
|
Now: func() time.Time { return now },
|
|
CacheDir: dir,
|
|
Enabled: true,
|
|
})
|
|
cache.store("api-token", "email-mcp/api-token", "secret-v1")
|
|
|
|
expired := newBitwardenCache(bitwardenCacheOptions{
|
|
ServiceName: "email-mcp",
|
|
Session: "session-v1",
|
|
TTL: time.Minute,
|
|
Now: func() time.Time { return now.Add(2 * time.Minute) },
|
|
CacheDir: dir,
|
|
Enabled: true,
|
|
})
|
|
if got, ok := expired.load("api-token", "email-mcp/api-token"); ok {
|
|
t.Fatalf("expired cache hit = %q, want miss", got)
|
|
}
|
|
}
|