| Version | Supported |
|---|---|
| 2.x.x | ✅ |
| 1.x.x | ✅ |
| < 1.0 | ❌ |
If you discover a security vulnerability, please report it by emailing admin@adamic.tech.
Please do NOT report security vulnerabilities through public GitHub issues.
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Any suggested fixes (optional)
- Initial Response: Within 48 hours
- Status Update: Within 7 days
- Resolution: Depends on severity and complexity
- Acknowledgment of your report
- Assessment of the vulnerability
- Development of a fix
- Coordinated disclosure (if applicable)
- Credit in the release notes (unless you prefer anonymity)
Identifier Sanitization (src/utils/identifiers.ts)
- All table, column, schema, and index names are validated and quoted across all tool groups (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
- Maximum 63-character limit enforced
- Invalid identifiers throw
InvalidIdentifierError
Key functions:
sanitizeIdentifier(name)— Validates and double-quotes an identifiersanitizeTableName(table, schema?)— Handles schema-qualified table referencessanitizeColumnRef(column, table?)— Handles column references with optional table qualifiersanitizeIdentifiers(names[])— Batch sanitization for column lists
Parameterized Queries
- All user-provided values use parameterized queries via
pglibrary - Identifier sanitization complements parameterized values
Rate Limiting (enabled by default)
- 100 requests per minute per IP address
- Configurable via
rateLimitMaxRequestsandrateLimitWindowMs - Returns
429 Too Many Requestswhen exceeded
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 Body Limits
- Maximum 1MB request body (configurable via
maxBodySize) - Prevents memory exhaustion attacks
Security Headers
X-Content-Type-Options: nosniffX-Frame-Options: DENYCache-Control: no-store, no-cache, must-revalidateContent-Security-Policy: default-src 'none'; frame-ancestors 'none'Permissions-Policy: camera=(), microphone=(), geolocation=()Referrer-Policy: no-referrer
HSTS Support
- Optional
Strict-Transport-Securityheader for HTTPS deployments - Enable via
enableHSTS: trueconfiguration
CORS Configuration
- Origin whitelist with
Vary: Originheader for caching - Optional credentials support (
corsAllowCredentials) - MCP-specific headers allowed (
X-Session-ID,mcp-session-id)
- RFC 9728 Protected Resource Metadata at
/.well-known/oauth-protected-resource - RFC 8414 Authorization Server Metadata discovery
- JWT token validation with JWKS caching (TTL: 1 hour, configurable)
- PostgreSQL-specific scopes:
read,write,admin,full,db:{name},schema:{name},table:{schema}:{table} - Per-tool scope enforcement via
AsyncLocalStoragecontext 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.
Code Mode executes user-provided JavaScript in a Node.js vm context. The vm module provides script isolation, not security isolation — it is not designed to resist a determined attacker with direct access. The following defense-in-depth mitigations significantly reduce risk within the intended trusted AI agent threat model:
- Blocked globals —
require,process,global,globalThis,module,exports,setTimeout,setInterval,setImmediate,Proxyset toundefined - 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 limits — 30s timeout (configurable), 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
adminscope 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. Thevmmodule does not provide a true security boundary — a sufficiently determined attacker with direct access could potentially escape the sandbox (e.g., via fragmentedconstructorchain 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 replacevmwithisolated-vmfor V8 isolate-level separation.
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
When using postgres-mcp:
- Never commit database credentials to version control
- Use environment variables for sensitive configuration
- Restrict database user permissions to minimum required
- Keep dependencies updated (Dependabot is configured for weekly npm and GitHub Actions updates)
- Enable SSL for database connections in production (
--sslorssl=truein connection string) - Use OAuth 2.1 authentication for HTTP transport in production — never expose HTTP transport without OAuth
- Enable HSTS when running over HTTPS (
--enableHSTS) - Configure CORS origins explicitly (avoid wildcards)
- For cloud-managed databases with IAM authentication (e.g., AWS RDS), set
POSTGRES_POOL_MIN=2to reduce connection establishment latency - Consider SHA-pinning critical GitHub Actions in CI workflows for supply-chain defense-in-depth
- When deploying behind a reverse proxy, apply rate limiting at the proxy layer rather than relying solely on the built-in per-IP rate limiter