Skip to content

Security: suave-tech/mcp-email-vector

Security

docs/SECURITY.md

Security Policy

Reporting a vulnerability

Do not open a public GitHub issue. Email security@rebarlabs.io with:

  • A description of the issue and its impact.
  • Steps to reproduce (or a proof-of-concept).
  • Affected commit / version.
  • Whether the issue is already public anywhere.

You should get an acknowledgement within 5 business days. If the report is in scope and reproducible, we'll work with you on a fix and coordinate disclosure.

Scope

In scope:

  • The API server (src/), MCP wrapper (mcp/), and helper scripts (scripts/).
  • Anything that handles OAuth tokens, JWTs, or could leak email content across user namespaces.

Out of scope:

  • Self-inflicted misconfiguration (e.g. running with JWT_SECRET=replace-me).
  • Findings in third-party dependencies — please report those upstream.
  • DoS via unbounded ingestion (cap is documented and tunable).

What this project handles that you should know about

  • OAuth refresh tokens for Gmail/Outlook — encrypted at rest with AES-256-GCM using TOKEN_ENCRYPTION_KEY. If that key leaks, every stored refresh token is compromised; rotate the env and force every user to reconnect.
  • Long-lived JWTs — minted via pnpm run mint-token for the MCP integration, 7-day expiry. Treat them like passwords.
  • Per-user namespace isolation — enforced server-side from the JWT sub, never from request bodies. Any path that lets a user influence the namespace is a high-severity bug.
  • Raw email text is never persisted in Postgres — only metadata + the vector in Pinecone. A bug that writes raw bodies to the DB or logs is a bug.
  • All SQL queries are parameterized via the pg library — the codebase never uses string interpolation to assemble queries.
  • Structured logs with redaction: pino is configured to drop access_token, refresh_token, and authorization keys before serialization (src/logger.ts). Adding a new sensitive field? Add its path to the redact list.

Provider-specific notes

  • Gmail cleanup moves messages to Trash via batchModify (addLabelIds: TRASH); Gmail auto-empties Trash after 30 days.
  • Yahoo / IMAP cleanup moves messages to the Trash folder via IMAP UID MOVE (RFC 6851). Retention depends on the user's Yahoo account setting — unlike Gmail, there's no fixed 30-day window.
  • XOAUTH2 is always over TLS (port 993, secure: true); the IMAP adapter refuses to connect without it.
  • Yahoo refresh tokens expire after roughly 60 days of inactivity. When refresh fails, the account is flagged needs_reauth = true; re-run the OAuth flow for that account to mint fresh tokens.

Assumptions for self-hosted deployments

This server is designed to run behind a trusted boundary — your own localhost, a VPN, or a reverse proxy you control. It deliberately does not implement:

  • Rate limiting. The project assumes a small number of trusted users; if you expose it to the public internet, run it behind a proxy (nginx, Caddy, Cloudflare) with per-IP/per-token rate limits.
  • Multi-tenant defense in depth. Namespace isolation is enforced in code, but the server trusts any valid JWT. A leaked JWT = full access to that user's namespace until expiry.
  • HTTPS termination. The server speaks plain HTTP on :3000 by default. OAuth redirect URIs for production must be https:// — terminate TLS at your proxy.

Input size limits that are enforced:

  • express.json({ limit: "1mb" }) on all JSON bodies.
  • Search query ≤ 500 chars, top_k ≤ 50, account_ids ≤ 20 (src/routes/search.ts).
  • Cleanup rule arrays capped (senders ≤ 100, labels ≤ 50, subjectMatches ≤ 50), maxMessages ≤ 5000 (src/cleanup/rules.ts).

What we do not promise

This is a hobbyist / self-hosted project. There is no bug bounty, no SLA, and no guarantee of a fix timeline. Responsible disclosure is appreciated but not contractually rewarded.

There aren't any published security advisories