Skip to content

Security: neverinfamous/memory-journal-mcp

SECURITY.md

🔒 Security Guide

The Memory Journal MCP server implements comprehensive security measures to protect your personal journal data.

🛡️ Database Security

Native SQLite Architecture

The server uses the native better-sqlite3 driver with sqlite-vec for vector operations, running directly against the filesystem.

  • PRAGMA foreign_keys = ON — enforces referential integrity and ON DELETE CASCADE
  • Parameterized queries — all user input bound via ? placeholders
  • WAL journal mode — high concurrency with non-blocking reads (PRAGMA journal_mode = WAL)
  • Synchronous Normal — optimized durability and performance (PRAGMA synchronous = NORMAL)

File Permissions (Docker)

  • Data directory: 700 (full access for owner only) in Docker
  • Non-root user (appuser:appgroup) owns data directory

🔐 Input Validation

Content Limits

  • Journal entries: 50,000 characters maximum
  • Tags: 100 characters maximum
  • Entry types: 50 characters maximum
  • Significance types: 50 characters maximum
  • HTTP request body: 1MB maximum (prevents memory exhaustion)

Character Handling

Tags are stored as-is via parameterized queries. Special characters in tags are safely handled by the database layer and do not pose injection risks.

SQL Injection Prevention

  • No Raw SQL Execution — All raw SQL methods (_executeRawQueryUnsafe) have been removed. All handlers must use strict, parameterized methods defined by IDatabaseAdapter.
  • Parameterized queries used throughout
  • Input validation via Zod schemas before database operations
  • Warning system for potentially dangerous content patterns
  • FTS5 / LIKE pattern sanitization (escapes %, _, \ wildcards and handles FTS5 syntax errors gracefully)
  • Date format whitelisting (prevents strftime injection)

Path Traversal & Filesystem Boundaries

  • Explicit Filesystem Boundaries — Filesystem access MUST be explicitly granted via ALLOWED_IO_ROOTS. The server operates in a fail-closed mode (ALLOWED_IO_ROOTS=[]) by default if not explicitly configured, preventing all filesystem interaction.
  • Implicit Authority Disabled — The server no longer derives ambient filesystem authority from the location of database files.
  • Backup filenames validated - rejects /, \, .. in paths
  • Typed security errors with consistent error codes

🌐 HTTP Transport Security

When running in HTTP mode (--transport http), the following security measures apply:

CORS Configuration

  • Configurable multiple origins via comma-separated --cors-origin flag or MCP_CORS_ORIGIN environment variable
  • Exact-match verification (no wildcard matching for custom domains)
  • 🔒 Default: None (no external origins allowed by default). You MUST explicitly configure CORS for browsers.
  • 🔒 Required: Set specific origins for production deployments, or wildcard (*) will only be accepted if HTTP Authentication is enabled.
# Restrict CORS to specific origins
memory-journal-mcp --transport http --cors-origin "http://localhost:3000,https://my-app.com"

# Or via environment variable
export MCP_CORS_ORIGIN="http://localhost:3000,https://my-app.com"

Security Headers & Protections

  • DNS Rebinding ProtectionhostHeaderValidation middleware prevents CVE-2025-66414
  • Strict-Transport-Security (HSTS) — max-age=31536000; includeSubDomains (opt-in via --enable-hsts). Note: Only effective when deployed behind a reverse proxy handling HTTPS.
  • X-Content-Type-Options: nosniff — prevents MIME sniffing
  • X-Frame-Options: DENY — prevents clickjacking
  • Content-Security-Policy: default-src 'none'; frame-ancestors 'none' — prevents XSS and framing
  • Cache-Control: no-store, no-cache, must-revalidate — prevents caching of sensitive journal data
  • Referrer-Policy: no-referrer — prevents referrer leakage
  • ⚠️ CORS wildcard warning — server logs a warning when CORS origin is *

Rate Limiting & Timeouts

  • Built-in Rate Limiting — 100 requests/minute per IP (sliding window with Retry-After header)
  • HTTP Timeouts — Request timeout (120s), keep-alive timeout (65s), headers timeout (66s)

Session Management (Stateful Mode)

  • UUID-based session IDs (cryptographically random)
  • 30-minute session timeout - idle sessions automatically expired
  • 5-minute sweep interval - periodic cleanup of abandoned sessions
  • Explicit session termination via DELETE /mcp

Request Size Limits

  • 1MB body limit on JSON requests (prevents memory exhaustion DoS)

🔐 OAuth 2.1 Authentication

For production HTTP deployments, Memory Journal supports OAuth 2.1 authentication (opt-in):

Component Status Description
Protected Resource Metadata RFC 9728 /.well-known/oauth-protected-resource
Auth Server Discovery RFC 8414 metadata discovery with caching
Token Validation JWT validation with JWKS support (via jose)
Scope Enforcement Granular read, write, admin scopes
HTTP Middleware Bearer token extraction and validation

Supported Scopes

Scope Tool Groups Description
read core, search, analytics, relationships, export Read-only operations
write github, team (+ all read groups) Read + write operations
admin admin, backup, codemode (+ all write/read groups) Full administrative access

Enabling OAuth

# CLI flags
memory-journal-mcp --transport http --port 3000 \
  --oauth-enabled \
  --oauth-issuer https://auth.example.com/realms/mcp \
  --oauth-audience memory-journal-mcp \
  --oauth-jwks-uri https://auth.example.com/realms/mcp/protocol/openid-connect/certs

# Or via environment variables
export OAUTH_ENABLED=true
export OAUTH_ISSUER=https://auth.example.com/realms/mcp
export OAUTH_AUDIENCE=memory-journal-mcp
export OAUTH_JWKS_URI=https://auth.example.com/realms/mcp/protocol/openid-connect/certs

How It Works

  1. Client sends Authorization: Bearer <token> header
  2. Server validates JWT signature against JWKS endpoint
  3. Server checks issuer, audience, and expiration claims
  4. Server extracts scopes from token and enforces tool-level access
  5. Invalid/missing tokens receive 401 with WWW-Authenticate header

Fallback Behavior

When OAuth is not enabled, authentication falls back to:

  1. Simple token auth — if MCP_AUTH_TOKEN env var is set, requests must include Authorization: Bearer <token> matching the configured value
  2. No auth — if no auth mechanism is configured, all requests are accepted (suitable for local/development use)

🐙 GitHub Token Security

Token Handling

  • Environment variables only - tokens never stored in config files
  • Error message scrubbing - Authorization headers stripped from error logs
  • Optional integration - server works fully offline without GitHub token
  • Minimal scopes - only requires repo, project, read:org

Environment Variables

# Required for Authentication (Must not be the placeholder token)
MCP_AUTH_TOKEN="your-secure-token-here"

# Required for Filesystem Tools (Export, Backup)
ALLOWED_IO_ROOTS="/path/to/allow1,/path/to/allow2"

# Required for GitHub features
GITHUB_TOKEN=ghp_...            # GitHub personal access token

# Optional
GITHUB_ORG_TOKEN=ghp_...        # For organization projects
PROJECT_REGISTRY='{"my-repo":{"path":"/path/to/repo","project_number":1}}'  # Multi-project routing
DEFAULT_PROJECT_NUMBER=1         # Default project for issue assignment
MCP_CORS_ORIGIN=                 # CORS origin (default: none)
MCP_HOST=localhost               # Server bind host
AUTO_REBUILD_INDEX=true          # Rebuild vector index on startup

🐳 Docker Security

Non-Root User

  • Dedicated user: appuser (UID 1001) with minimal privileges
  • Restricted group: appgroup (GID 1001)
  • Restricted data directory: 700 permissions

Container Hardening

  • Minimal base image: node:26-alpine
  • Multi-stage build: Build dependencies not in production image
  • Process isolation from host system
  • No shell access needed for production

Volume Mounting Security

# Secure volume mounting
docker run -v ./data:/app/data:rw,noexec,nosuid,nodev memory-journal-mcp

Resource Limits

# Apply resource limits
docker run --memory=1g --cpus=1 memory-journal-mcp

⛓️ Supply Chain Security

SHA-Pinned Images

Recommended (highest security):

# Use SHA-256 digest for immutable reference
docker pull writenotenow/memory-journal-mcp@sha256:abc123...

# Find SHA digests at:
# https://hub.docker.com/r/writenotenow/memory-journal-mcp/tags

Benefits:

  • Immutable image reference
  • Protection against tag hijacking
  • Reproducible deployments
  • Supply chain verification

Tag-based (convenience):

docker pull writenotenow/memory-journal-mcp:latest

🔒 Code Mode Sandbox Security

Code Mode (mj_execute_code) executes user-provided JavaScript in a hardened worker_threads + vm.createContext sandbox:

Engine-Level Restrictions

  • V8 code generation disabledcodeGeneration: { strings: false, wasm: false } prevents eval() and Function() construction from strings at the V8 engine level
  • Separate V8 isolate — each worker thread runs in its own V8 instance with enforced heap limits (maxOldGenerationSizeMb, maxYoungGenerationSizeMb)
  • Frozen prototypes — all built-in prototypes (Object, Function, Array, Error, Map, Set, Promise, etc.) frozen inside the vm context to prevent dynamic constructor chain escapes
  • Proxy constructor nullifiedProxy: undefined in the sandbox context prevents meta-object protocol abuse

Static Code Validation

  • 18 blocked patterns — regex rules blocking require(), import(), process.*, global.*, eval(), Function(), __proto__, constructor.constructor, ['constructor'], Reflect.*, Symbol.*, new Proxy(), child_process, fs.*, net.*, http.*, https.*
  • 50KB code size limit — prevents payload-based resource exhaustion

Runtime Protection

  • RPC allowlist — host-side validation prevents workers from invoking unauthorized API methods
  • Rate limiting — 60 executions per minute per client
  • Hard timeouts — configurable execution limit (default 30s) with forced worker termination
  • Egress boundary enforcement — result serialization capped at configurable limit (default 100KB) to prevent OOM via oversized payloads
  • Readonly Proxy traps — when readonly: true, stripped mutation methods throw clear error messages listing available methods
  • Schema Introspection Scope.schema() properties are strictly bound to the permitted methods in the RPC allowlist, preventing unintended internal structure leakage
  • Audit logging — all executions logged with code preview, metrics, and readonly mode

🔍 Data Privacy

Architecture Characteristics

  • Full data ownership: SQLite database stays on your machine
  • No telemetry: No data sent to external telemetry endpoints
  • ⚠️ External Services: If configured, communicates with GitHub API and fetches OAuth discovery (JWKS) endpoints.
  • ⚠️ Semantic search: ML models are executed locally via @huggingface/transformers, but the model weights are downloaded from the Hugging Face registry on first run if not cached locally.

Context Security

  • Git context: Only reads local repository information
  • No sensitive data: Doesn't access private keys or credentials
  • Optional GitHub integration: Only if explicitly configured with token

🗑️ Soft Delete Security

Recoverable Deletes

// Soft delete (default)
delete_entry({ entry_id: 42, permanent: false })
// Sets deleted_at timestamp, entry remains in DB

// Permanent delete (explicit)
delete_entry({ entry_id: 42, permanent: true })
// Removes from database completely

Benefits:

  • Accidental deletion recovery
  • Audit trail
  • No data loss

Soft-deleted entries:

  • Excluded from searches
  • Not visible in lists
  • Still in database (for recovery)

🔑 Access Control

MCP Client Authentication

Security handled at multiple levels:

  • OAuth 2.1 (HTTP transport, opt-in) - RFC-compliant JWT validation with scope enforcement
  • Simple token auth (HTTP transport, via MCP_AUTH_TOKEN) - Basic Bearer token verification
  • MCP client (stdio transport) - Desktop app security (Cursor IDE, Claude Desktop)
  • User controls which servers run

File System Permissions

npm installation:

# Database created with user permissions
chmod 600 ~/.memory-journal/memory_journal.db

Docker installation:

# Volume mount with appropriate permissions
mkdir -m 755 data

Team Database Security

If using team collaboration, the TEAM_DB_PATH dictates where the shared database is located.

Security Warning: Because TEAM_DB_PATH is accessed by multiple users or processes, it must be placed on a secure shared volume with strict access controls to prevent unauthorized data access or modification.

# Set secure permissions on the team database directory
chmod 700 /shared/team-data

⚙️ Secure Configuration

Environment Variables

Docker environment variables:

{
  "args": [
    "-e",
    "DB_PATH=/app/data/custom.db"
    // NO sensitive data in env vars
    // NO API keys
    // NO credentials
  ]
}

Secrets Management

No secrets required:

  • No API keys
  • No passwords
  • No tokens
  • Optional: GitHub CLI (user manages gh auth)

🔄 CI/CD Security

  • CodeQL analysis - automated static analysis on push/PR
  • Trivy container scanning - Docker image vulnerability detection
  • TruffleHog - secret scanning on push/PR
  • npm audit - dependency vulnerability checking

🚨 Security Best Practices

For Users

  1. Set a CORS origin when exposing the HTTP transport on a network
  2. Keep Node.js updated: Use Node.js 26+ (LTS)
  3. Secure host system: Ensure your host machine is secure
  4. Regular backups: Use the backup_journal tool or back up your .db file
  5. Limit network access: Don't expose the HTTP transport to untrusted networks
  6. Use resource limits: Apply Docker --memory and --cpus limits

For Developers

  1. Regular updates: Keep Node.js and npm dependencies updated
  2. Security scanning: Regularly scan Docker images for vulnerabilities
  3. Code review: All database operations use parameterized queries
  4. Input validation: All tool inputs validated via Zod schemas

📋 Security Checklist

  • Foreign key enforcement (PRAGMA foreign_keys = ON)
  • Input validation and length limits (Zod schemas)
  • Parameterized SQL queries
  • SQL injection detection heuristics (defense-in-depth)
  • Path traversal protection (assertNoPathTraversal)
  • Explicit Filesystem Boundaries (ALLOWED_IO_ROOTS enforcement)
  • Strict Authentication Tokens (Placeholder rejection)
  • FTS5 / LIKE pattern sanitization (sanitizeSearchQuery)
  • Date format whitelisting (validateDateFormatPattern)
  • HTTP body size limit (1MB)
  • Configurable CORS multi-origin with exact-match enforcement
  • HTTP timeouts and built-in rate limiter (100 req/min)
  • DNS rebinding protection and strict HSTS
  • Security headers (CSP, X-Content-Type-Options, X-Frame-Options, Cache-Control, Referrer-Policy, Permissions-Policy)
  • Session timeout (30 minutes)
  • Non-root Docker user
  • Multi-stage Docker build
  • Local-first data architecture
  • GitHub token error scrubbing
  • Code Mode sandbox isolation (worker_threads V8 isolate + vm.createContext)
  • Code Mode V8 codeGeneration restrictions (eval/Function disabled at engine level)
  • Code Mode frozen built-in prototypes (constructor chain escape prevention)
  • Code Mode blocked patterns (18 static regex rules)
  • Code Mode Proxy constructor nullified in sandbox context
  • Code Mode RPC allowlist validation (host-side method authorization)
  • Code Mode readonly Proxy traps (structured errors for stripped methods)
  • CI/CD security pipeline (CodeQL, Trivy, secret scanning)
  • Comprehensive security documentation

🎯 Threat Model

In Scope

Protected against:

  • SQL injection
  • Resource exhaustion (size limits)
  • Accidental data loss (soft delete)
  • Database integrity (transactions)
  • Container escape (non-root, no privileges)

Out of Scope

Not protected against:

  • Malicious MCP client
  • Compromised host system
  • Physical access to database file
  • User deleting database file

Why:

  • Local-first design trusts the host
  • User has full control
  • Desktop application security model

📜 Compliance

Data Protection

GDPR-friendly:

  • No data collection
  • No data transmission
  • Local storage only
  • User controls all data
  • Easy data export/deletion

No PII collection:

  • No names
  • No email addresses
  • No tracking
  • No analytics

📦 Supported Versions

Component Supported Versions Notes
Node.js 26.x (LTS) Minimum required version for worker_threads and isolation features.
MCP Protocol 2024-11-05 Full compatibility with the official Model Context Protocol.
Docker Base node:26-alpine Alpine-based images receive regular vulnerability patches.

🚨 Reporting Security Issues

If you discover a security vulnerability, please:

  1. Do not open a public GitHub issue
  2. Contact the maintainers privately
  3. Provide detailed information about the vulnerability
  4. Allow time for the issue to be addressed before public disclosure

🔄 Security Updates

  • Container updates: Rebuild Docker images when base images are updated
  • Dependency updates: Keep npm packages updated via manual update workflows
  • Database maintenance: Run ANALYZE and PRAGMA optimize regularly
  • Security patches: Apply host system security updates

The Memory Journal MCP server is designed with security-first principles to protect your personal journal data while maintaining excellent performance and usability.

There aren't any published security advisories