feat(security): add security hardening — auth, encryption, validation - #175
feat(security): add security hardening — auth, encryption, validation#175jessecurry wants to merge 8 commits into
Conversation
… hashing - Create mempalace/security.py centralizing all security primitives - Replace MD5 with SHA-256 for ID generation across 5 call sites - Add secure_file (0o600) and secure_dir (0o700) permission helpers - Apply restrictive permissions to config dir, config files, and SQLite DB - Add localhost-only bind address validation for future HTTP transport - Add security config section (auth_enabled, encryption_enabled, max_content_size) - Support env var overrides (MEMPALACE_AUTH_ENABLED, MEMPALACE_ENCRYPTION_ENABLED) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Validate required fields are present before dispatching tool calls - Reject unknown fields not defined in tool input_schema - Enforce configurable content size limits on write operations - Strip _meta from tool_args before handler dispatch (reserved for auth) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Auth tokens stored in OS keychain via keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service) with file fallback - Constant-time token verification via hmac.compare_digest - Auth check on tools/list and tools/call, initialize always open - initialize response includes authRequired: true when auth is enabled - All auth features opt-in via config security.auth_enabled Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Encryption keys stored in OS keychain via keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service) with file fallback - Encrypted content stored in ChromaDB metadata field encrypted_content - Documents field keeps plaintext for embedding/search to work - Transparent decrypt on retrieval in MCP tools and searcher - Knowledge graph entity properties encrypted when enabled - Backward compatible: unencrypted drawers still readable - cryptography and keyring added as optional [security] dependency Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- start_http_server() validates bind address is localhost before binding - Rejects 0.0.0.0, public IPs, and non-localhost addresses - HTTP transport config support (transport: "http" in security config) - Currently raises NotImplementedError — stdio remains the only transport - Defensive scaffolding to prevent accidental network exposure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add .env, *.key, auth_token to prevent secret leaks - Add *.sqlite3, entities.json (may contain PII) - Add venv/, .venv/, IDE configs, test coverage artifacts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused variable in test_search_returns_decrypted_content - Apply ruff formatter to searcher.py, test_mcp_server.py, test_security.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ng, timing Critical: - Store content hash placeholder in ChromaDB documents field when encryption is enabled instead of plaintext — encrypted_content in metadata is now the only copy of the real content High: - Add logging to keyring helpers — separate ImportError from other exceptions so keyring failures leave a diagnostic trail instead of silently falling back - Secure config directory (0o700) in token/key file fallback paths - Remove file paths from warning log messages Medium: - Decrypt failures now return clear error markers instead of silently falling back to raw documents/empty data - Knowledge graph _decrypt_props returns empty JSON on failure, not ciphertext Low: - Remove timing leak: eliminate short-circuit before hmac.compare_digest in auth token check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PR Review: feat(security): add security hardening — auth, encryption, validationExecutive Summary
Affected Areas: Business Impact: Encrypted content becomes unsearchable via semantic search. Re-mining after upgrade creates duplicate drawers. Flow Changes: All drawer writes now use SHA-256 IDs (was MD5). MCP server optionally encrypts content at rest and requires auth tokens. Search path gains decryption layer. Ratings
PR Health
High Priority Issues(Must fix before merge) 🐛 #1: Encryption destroys semantic search qualityLocation: When encryption is enabled, The test Fix: Store the plaintext in - doc_content = f"[encrypted:{content_hash(content, length=32)}]"
+ # ChromaDB needs plaintext for embedding generation.
+ # Encrypt in metadata only; embeddings are not reversible to plaintext.
+ doc_content = content
🐛 #2:
|
|
Thanks for the security work. This conflicts with main and the core security hardening landed in #387. If there are specific pieces not covered, happy to look at a focused follow-up PR. |
Summary
Adds opt-in security hardening to MemPalace based on a comprehensive security audit. All features are backward-compatible — MemPalace works exactly as before unless security is explicitly enabled in config.
What's included
keyring, with file fallbackdocumentsfield stores only a content-hash placeholder to prevent plaintext persistence on disk0o600on sensitive files,0o700on config directoriesstart_http_server()rejects non-localhost bind addresses.env,*.key,auth_token,*.sqlite3,entities.json, IDE configsImportant: Semantic search and encryption
When encryption is disabled (the default), semantic search works exactly as it does today — no behavior change whatsoever.
When encryption is enabled, the ChromaDB
documentsfield stores a[encrypted:<hash>]placeholder instead of plaintext, so content is not persisted unencrypted on disk. The trade-off is that semantic search will not return meaningful results for encrypted drawers, since ChromaDB cannot compute useful embeddings on the placeholder. This is an honest, unavoidable trade-off — you cannot have encryption at rest and semantic search over plaintext simultaneously without an external embeddings service. The authoritative content lives only in theencrypted_contentmetadata field.Users who need both search and encryption should pair this with OS-level disk encryption (FileVault, LUKS, BitLocker) and leave application-level encryption disabled.
Configuration
All security features live under a
securityblock in~/.mempalace/config.json:{ "security": { "auth_enabled": true, "encryption_enabled": true, "max_content_size": 1048576 } }Environment variable overrides:
MEMPALACE_AUTH_ENABLED,MEMPALACE_ENCRYPTION_ENABLED.Dependencies
cryptographyandkeyringadded as optional[security]extra:Core mempalace continues to work with zero extra dependencies.
Test plan
🤖 Generated with Claude Code