Thanks for taking the time to look at Synapse's security posture. This document is the authoritative reference for how we handle vulnerability reports, what the in-scope surface looks like, and what protections are already in place.
Email: security@synapse.ai
Encryption: PGP key on request.
Please include:
- a clear description of the issue and impact
- minimal reproduction steps (PoC, request/response samples, or a script)
- the affected version (commit SHA or release tag), environment, and any configuration knobs that matter
- your name + handle (or "anonymous") for the post-fix advisory
We commit to:
| Step | Target |
|---|---|
| Acknowledge receipt | 24 h |
| Initial triage / severity | 72 h |
Fix or mitigation in main |
14 d for high/critical, 30 d for medium, 90 d for low |
| Public advisory + credit | After fix is rolled out and downstream tenants notified |
We will not pursue legal action for good-faith research that:
- doesn't pivot beyond your own tenant or test data,
- doesn't degrade availability for other users,
- doesn't access, alter, or exfiltrate data you're not authorised to read,
- gives us a reasonable window to fix before public disclosure.
The main branch is the only supported tag while we are pre-1.0. Once 1.0
ships we will publish an LTS schedule here.
- The Synapse API (
apps/api) — including authentication, tenant isolation, audit log, and the module/event runtime. - The Synapse Web app (
apps/web) — XSS, CSP regressions, auth flow gaps, open-redirects. - The shared contracts package (
packages/contracts) — type drift that would weaken authorisation guarantees. - The Docker images and the migration pipeline.
- Third-party services (OpenAI, Stripe, Telegram). Report those upstream.
- Issues that require a malicious admin (OWNER-or-ADMIN) — by design, those roles can mutate their own tenant. File a usability bug instead.
- Self-XSS or social engineering against staff.
- Volumetric DoS without a logic vector — handled at the LB / WAF layer in production deployments.
- Vulnerabilities in
node:20-alpineupstream that are mitigated by ourapk upgrade --no-cachebaseline (seeapps/api/Dockerfile).
The platform is multi-tenant. The non-negotiable invariants are:
- Tenant isolation. No row, queue job, file, log line, or audit event from tenant A may reach tenant B's session, code path, or metric.
- Permission integrity. Authorization decisions go through the
@Permissions(...)decorator in NestJS guards — never role string comparisons in controllers, never client-side gating as a security boundary. - Append-only audit. Every authentication and high-privilege action
produces an
AuditEventrow. The model has no UPDATE or DELETE entry point inAuditService. - Secret hygiene. No log line, response, or stack trace ever contains
a password, token, cookie, or API key in clear text. Enforced centrally
via
apps/api/src/common/logging/redact.ts.
helmetmiddleware on the API (HSTS in production, X-Content-Type-Options, Referrer-Policy, X-Frame-Options, COOP, noX-Powered-By).- Strict CSP on Next.js (
apps/web/next.config.ts), HSTS, Permissions-Policy denying camera/microphone/geolocation/etc. by default. - CORS allowlist is explicit; empty/unset is "deny" in production, "reflect" in development.
app.set('trust proxy', 1)to honourX-Forwarded-Forfrom the LB.
- Passwords hashed with argon2 (default
argon2.hashparameters; revisit before going to GA). - JWTs bound to
iss=synapse-api+aud=synapse-web. Rotating either env var invalidates every existing session. - Per-IP throttling on
/auth/*(@Throttle({auth: 5/min})) plus a Redis-backed per-(IP, email) lockout (10 failures → 15 min lock). - Login responses use a generic message — never leak whether the email exists.
- All authentication outcomes (success / failure / lockout) hit the audit log.
- RBAC: 4 roles (OWNER / ADMIN / OPERATOR / VIEWER) → permission map in
@synapse/contracts. Backend is the only authority; UI gating via<Can>is for usability, not security. - Global
JwtAuthGuard+TenantGuard+PermissionsGuardregistered viaAPP_GUARD— every route is protected by default; opt-out requires@Public(). - Tenant context cross-checked between JWT and
X-Tenant-Idheader.
redact()walks every log payload — sensitive keys (password, token, authorization, cookie, secret, …) and Bearer-pattern strings are scrubbed.- Request IDs propagate from incoming
X-Request-Id(or a generated UUID) into every log line and the response header for debugging.
- Container images run as a non-root user (
nestjs/nextjs). - Postgres / Redis volumes are named so backups can be wired without hitting ephemeral mounts.
- Migrations are immutable on
main(seeapps/api/prisma/MIGRATIONS.md).
- HttpOnly + double-submit-token CSRF flow on the web app — currently the bearer token is JS-readable.
- Refresh-token rotation. The 15-minute access token is not yet paired with a refresh token.
- Dependency-scan gate in CI (
npm audit --omit=devon every PR). - Per-tenant rate-limit overrides for the throttler.
- Audit-event UI / export.
If your finding overlaps with this list, please still report it — knowing the impact you have in mind helps us prioritise.