Skip to content

safeStorage claimed in SECURITY.md but not implemented — API keys stored in plaintext #532

@michael-ford

Description

@michael-ford

Summary

SECURITY.md states:

"Credential storage -- Sensitive credentials are stored using Electron's safeStorage API where available."

However, safeStorage is not used anywhere in the codebase. A code search for safeStorage returns only the SECURITY.md file itself — zero matches in source code.

Current behavior

API keys (Groq, OpenAI, Anthropic, etc.) are stored in two plaintext locations:

  1. Electron localStorage — via settingsStore.ts (e.g., localStorage.setItem("groqApiKey", key)) — which is an unencrypted SQLite file on disk
  2. .env file — via EnvironmentManager in src/helpers/environment.js, written to the Electron userData directory

Both are readable by any process running as the current OS user.

Additionally, debug logging in audioManager.js logs the first 8 characters of API keys (keyPreview), which for keys with short prefixes (e.g., Groq's gsk_) leaks part of the actual secret.

Expected behavior

Use Electron's safeStorage API as documented, which encrypts credentials using the platform's native keychain (macOS Keychain, Windows DPAPI, libsecret on Linux):

// Store
const encrypted = safeStorage.encryptString(apiKey);
fs.writeFileSync(keyPath, encrypted);

// Retrieve
const encrypted = fs.readFileSync(keyPath);
const apiKey = safeStorage.decryptString(encrypted);

Impact

Users providing their own API keys (BYOK) trust that credential storage follows the documented security model. The current implementation doesn't match the documented claims.

Environment

  • Tested against: current main branch (v1.6.6)
  • Platform: macOS

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingworking on itThis issue or feature is actively being worked on.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions