Skip to content

Security: neverinfamous/postgres-mcp

SECURITY.md

πŸ”’ Security Policy

The postgres-mcp PostgreSQL MCP server implements comprehensive security measures to protect your databases across stdio, HTTP, and SSE transports.

πŸ›‘οΈ Database Security

SQL Injection Prevention

Identifier Sanitization (src/utils/identifiers.ts)

  • βœ… Comprehensive coverage β€” all table, column, schema, and index names validated and quoted across every tool group (admin, backup, core, jsonb, monitoring, partitioning, performance, postgis, schema, stats, text, vector)
  • βœ… PostgreSQL identifier rules enforced β€” start with letter/underscore, contain only alphanumerics, underscores, or $ signs
  • βœ… 63-character limit enforced (PostgreSQL maximum)
  • βœ… Invalid identifiers throw InvalidIdentifierError

Key functions:

  • sanitizeIdentifier(name) β€” Validates and double-quotes an identifier
  • sanitizeTableName(table, schema?) β€” Handles schema-qualified table references
  • sanitizeColumnRef(column, table?) β€” Handles column references with optional table qualifier
  • sanitizeIdentifiers(names[]) β€” Batch sanitization for column lists

Parameterized Queries

  • βœ… All user-provided values use parameterized queries via pg library
  • βœ… Identifier sanitization complements parameterized values β€” defense in depth

Structured Error Handling

Every tool returns structured error responses β€” never raw exceptions or internal details:

{
  "success": false,
  "error": "Descriptive message with context",
  "code": "MODULE_ERROR_CODE",
  "category": "VALIDATION_ERROR",
  "suggestion": "Actionable remediation hint",
  "recoverable": true
}

Error codes are module-prefixed (e.g., PG_CONNECTION_FAILED, SCHEMA_NOT_FOUND). Internal stack traces are logged server-side but never exposed to clients.

πŸ” Input Validation

  • βœ… Zod schemas β€” all tool inputs validated at tool boundaries before database operations
  • βœ… Parameterized queries used throughout β€” never string interpolation
  • βœ… Identifier sanitization β€” table, column, schema, and index names validated against injection

πŸ§ͺ Code Mode Sandbox Security

Code Mode executes user-provided JavaScript in a Node.js vm context or a true V8 isolate via worker_threads (recommended). The standard vm module provides script isolation, not security isolation β€” it is not designed to resist a determined attacker with direct access. However, isolating workloads into worker threads with strict resource limits, combined with the following defense-in-depth mitigations, significantly reduces risk within the intended trusted AI agent threat model:

Sandbox Restrictions

  • βœ… Blocked globals β€” require, process, global, globalThis, module, exports, setTimeout, setInterval, setImmediate, Proxy set to undefined
  • βœ… Blocked patterns β€” 17 static regex rules reject code containing require(), import(), eval(), Function(), __proto__, constructor.constructor, Reflect.*, Symbol.*, new Proxy(), and filesystem/network/child_process references
  • βœ… Execution timeout β€” 30s hard limit (configurable)
  • βœ… Input limits β€” 50KB code input, 10MB result output
  • βœ… Rate limiting β€” 60 executions per minute per client
  • βœ… Audit logging β€” every execution logged with UUID, client ID, metrics, and code preview (truncated to 200 chars)
  • βœ… Admin scope β€” Code Mode requires admin scope when OAuth is enabled

⚠️ Threat Model: Code Mode is designed for use by trusted AI agents, not for executing arbitrary untrusted code from end users. The vm module does not provide a true security boundary β€” a sufficiently determined attacker with direct access could potentially escape the sandbox (e.g., via fragmented constructor chain access on exposed built-in Error types). Static pattern blocking catches the known literal forms (constructor.constructor) but not dynamically constructed variants.

For untrusted input deployments: Use process-level sandboxing such as running the container with --cap-drop=ALL, or replace vm with isolated-vm for V8 isolate-level separation.

🌐 HTTP Transport Security

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

Security Headers & Protections

  • βœ… DNS Rebinding Protection β€” validateHostHeader() strictly validates Host headers
  • βœ… 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 data
  • βœ… Referrer-Policy: no-referrer β€” prevents referrer leakage
  • βœ… Permissions-Policy: camera=(), microphone=(), geolocation=() β€” restricts browser APIs

HSTS Support

  • βœ… Strict-Transport-Security header for HTTPS deployments
  • βœ… Enable via enableHSTS: true configuration

CORS Configuration

  • βœ… Origin whitelist with Vary: Origin header for caching
  • βœ… Optional credentials support (corsAllowCredentials)
  • βœ… MCP-specific headers allowed (X-Session-ID, mcp-session-id)

Rate Limiting & Timeouts

  • βœ… Built-in Rate Limiting β€” 100 requests/minute per IP
  • βœ… Health Endpoint Bypass β€” /health bypasses limits to ensure reliable load balancer checks
  • βœ… Returns 429 Too Many Requests with proper Retry-After headers when limits are exceeded
  • βœ… Slowloris DoS Protection β€” configurable read timeouts via MCP_REQUEST_TIMEOUT and MCP_HEADERS_TIMEOUT

Reverse Proxy Note: Rate limiting uses req.socket.remoteAddress. Behind a reverse proxy (e.g., nginx, Cloudflare Tunnel), all requests may share the same source IP. Ensure your proxy forwards distinct client IPs, or apply rate limiting at the proxy layer instead.

Request Size Limits

  • βœ… Configurable body limit via maxBodySize (default: 1 MB) β€” prevents memory exhaustion DoS

πŸ”‘ Authentication (OAuth 2.1)

Full OAuth 2.1 for production multi-tenant deployments:

  • βœ… RFC 9728 Protected Resource Metadata (/.well-known/oauth-protected-resource)
  • βœ… RFC 8414 Authorization Server Discovery with caching
  • βœ… JWT validation with JWKS support (TTL: 1 hour, configurable)
  • βœ… PostgreSQL-specific scopes: read, write, admin, full, db:{name}, schema:{name}, table:{schema}:{table}
  • βœ… Per-tool scope enforcement via AsyncLocalStorage context threading

⚠️ HTTP without OAuth: When OAuth is not configured, all scope checks are bypassed. If you expose the HTTP transport without enabling OAuth, any client has full unrestricted access. Always enable OAuth for production HTTP deployments.

🐳 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:24-alpine
  • βœ… Multi-stage build: Build dependencies not in production image
  • βœ… Production pruning: npm prune --omit=dev after build
  • βœ… Health check: Built-in HEALTHCHECK instruction (transport-aware for HTTP/SSE/stdio)
  • βœ… Process isolation from host system

Dependency Patching

The Dockerfile patches npm-bundled transitive dependencies for Docker Scout compliance:

  • βœ… diff@8.0.3 β€” GHSA-73rr-hh4g-fpgx
  • βœ… @isaacs/brace-expansion@5.0.1 β€” CVE-2026-25547
  • βœ… tar@7.5.11 β€” CVE-2026-23950, CVE-2026-24842
  • βœ… minimatch@10.2.4 β€” CVE-2026-27904, CVE-2026-27903

Volume Mounting Security

# Secure volume mounting
docker run -v ./data:/app/data:rw,noexec,nosuid,nodev neverinfamous/postgres-mcp:latest

Resource Limits

# Apply resource limits
docker run --memory=1g --cpus=1 neverinfamous/postgres-mcp:latest

πŸ” Logging Security

Audit Subsystem

  • βœ… Full JSONL Audit Trails β€” comprehensive logging array capturing mutations, Code Mode executions, and system events
  • βœ… Session Token Estimates β€” robust burn-rate tracking appended to log entries
  • βœ… Pre-Mutation Snapshots β€” interceptor captures table states before destructive administration operations

Credential Redaction

  • βœ… Sensitive fields automatically redacted in logs: password, secret, token, apikey, issuer, audience, jwksUri, credentials, etc.
  • βœ… Recursive sanitization for nested objects

Log Injection Prevention

  • βœ… Control character sanitization (ASCII 0x00-0x1F except tab/newline, 0x7F, C1 characters)
  • βœ… Prevents log forging and escape sequence attacks

πŸ”„ CI/CD Security

  • βœ… CodeQL analysis β€” automated static analysis on push/PR
  • βœ… npm audit β€” dependency vulnerability checking (audit-level: moderate)
  • βœ… Dependabot β€” automated dependency update PRs (weekly for npm and GitHub Actions)
  • βœ… Secrets scanning β€” dedicated workflow for leaked credential detection
  • βœ… E2E transport parity β€” Playwright suite validates HTTP/SSE security behavior

🚨 Security Best Practices

For Users

  1. Never commit database credentials to version control β€” use environment variables
  2. Use OAuth 2.1 authentication for HTTP transport in production β€” never expose HTTP transport without OAuth
  3. Restrict database user permissions to minimum required
  4. Enable SSL for database connections in production (--ssl or ssl=true in connection string)
  5. Enable HSTS when running over HTTPS (--enableHSTS)
  6. Configure CORS origins explicitly β€” avoid wildcards
  7. Use resource limits β€” apply Docker --memory and --cpus limits
  8. Apply rate limiting at the proxy layer when deploying behind a reverse proxy
  9. For cloud-managed databases with IAM authentication (e.g., AWS RDS), set POSTGRES_POOL_MIN=2 to reduce connection establishment latency
  10. Consider SHA-pinning critical GitHub Actions in CI workflows for supply-chain defense-in-depth

For Developers

  1. Parameterized queries only β€” never interpolate user input into SQL strings
  2. Zod validation β€” all tool inputs validated via schemas at tool boundaries
  3. No secrets in code β€” use environment variables (.env files are gitignored)
  4. Typed error classes β€” descriptive messages with context; don't expose internals
  5. Regular updates β€” keep Node.js and npm dependencies updated
  6. Security scanning β€” regularly scan Docker images for vulnerabilities

πŸ“‹ Security Checklist

  • Parameterized SQL queries throughout
  • Identifier sanitization (table, column, schema, index names)
  • Input validation via Zod schemas
  • Code Mode sandbox isolation (vm or worker_threads V8 isolate)
  • Code Mode execution timeout (30s hard limit)
  • Code Mode rate limiting (60 executions/min)
  • Code Mode audit logging
  • HTTP body size limit (configurable, default 1 MB)
  • Configurable CORS with origin whitelist
  • Rate limiting (100 req/min per IP)
  • Slowloris DoS timeouts (MCP_REQUEST_TIMEOUT, MCP_HEADERS_TIMEOUT)
  • DNS rebinding protection via Host header validation
  • Security headers (CSP, X-Content-Type-Options, X-Frame-Options, Cache-Control, Referrer-Policy, Permissions-Policy)
  • HSTS (opt-in)
  • OAuth 2.1 with JWT/JWKS validation (RFC 9728, RFC 8414)
  • PostgreSQL-specific scope enforcement (read, write, admin, full, db:*, schema:*, table:*:*:*)
  • Per-tool scope enforcement via AsyncLocalStorage
  • Credential redaction in logs
  • Log injection prevention
  • Non-root Docker user
  • Multi-stage Docker build with production pruning
  • Transitive dependency CVE patching in Dockerfile
  • CI/CD security pipeline (CodeQL, npm audit, secrets scanning)
  • Structured error responses (no internal details leaked)
  • Comprehensive security documentation

🚨 Reporting Security Issues

Version Supported
2.x.x βœ…
1.x.x βœ…
< 1.0 ❌

If you discover a security vulnerability:

  1. Do not open a public GitHub issue
  2. Email security concerns to: admin@adamic.tech
  3. Include detailed reproduction steps and potential impact
  4. Allow reasonable time for a fix before public disclosure

Response Timeline

  • Initial Response: Within 48 hours
  • Status Update: Within 7 days
  • Fix Timeline: Depends on severity

We appreciate responsible disclosure and will acknowledge your contribution in our release notes (unless you prefer to remain anonymous).

πŸ”„ Security Updates

  • Container updates: Rebuild Docker images when base images are updated
  • Dependency updates: Keep npm packages updated via npm audit and Dependabot
  • Database maintenance: Run ANALYZE and VACUUM regularly for optimal performance
  • Security patches: Apply host system security updates

The postgres-mcp PostgreSQL MCP server is designed with security-first principles to protect your databases while maintaining excellent performance and full PostgreSQL capability.

There aren’t any published security advisories