Email: your@email.com (or open a private security advisory on the repo). Please include:
- Affected version / commit
- Steps to reproduce
- Impact assessment (data exposure / privilege escalation / DoS / …)
We aim to acknowledge within 48h and ship a fix within 7 days for high-severity issues.
- JWT (
HS256) signed withSECRET_KEY. Access token TTL =ACCESS_TOKEN_EXPIRE_MINUTES(default 30 min). Refresh token TTL =REFRESH_TOKEN_EXPIRE_MINUTES(default 7 days). - Password hashing: bcrypt via
passlib. Plain passwords never persisted. - OAuth 2.0 (Google) — auth-code flow. Token validated server-side, internal user record looked up/created by email.
- Session management — DB-backed sessions with revocation. Each refresh-token issuance creates a session row;
/sessionsendpoint lets users see + revoke devices. - Admin API key — static
settings.API_KEYmatched viaX-API-Keyheader for service-to-service calls. Constant-time compared withsecrets.compare_digest().
- Role-based via
RoleCheckerdep (UserRole.USER/UserRole.ADMIN). - Admin pages require
role=admin. Sensitive ops (impersonate user, system-health) gated separately. - Workspace scope — every authenticated request resolves an
ActiveOrg(default = personal org). Resources scoped byorganization_idforeign key. - Org roles:
OWNER/ADMIN/MEMBER. Owner can transfer ownership + delete org.
- CORS — origin list from
settings.CORS_ORIGINS. Restrict to your domains in production. - HTTPS — enforce via reverse proxy (Nginx / Traefik / ALB). Strict-Transport-Security header set in middleware when
ENVIRONMENT=production. - CSP — frontend sets
frame-ancestors 'none'by default to prevent click-jacking. Seefrontend/next.config.tsheaders block.
- Secrets — read from environment via
pydantic-settings. Never committed. See.env.example+ENV_VARS.md. - Audit log — admin-mutating actions (user updates, deletes, impersonations, role changes) recorded in
app_admin_audit_logtable with actor + IP + payload snapshot. - Stripe webhooks — signature verified via
stripe.Webhook.construct_event(secret=STRIPE_WEBHOOK_SECRET). Idempotency table prevents replay.
- Rotate
SECRET_KEYandAPI_KEYfrom generated defaults. - Set
DEBUG=falseandENVIRONMENT=production. - Restrict
CORS_ORIGINSto your domain(s). - Tune
RATE_LIMIT_REQUESTS/RATE_LIMIT_PERIODin.env. - Set
PROMETHEUS_AUTH_TOKENif/metricsis exposed on a public endpoint. - Set
SENTRY_DSNto ship errors. Verify PII scrubbing rules incore/sentry.py. - Enforce HTTPS at the proxy layer.
- Run
pip-audit/bun auditin CI for dependency vulnerabilities. - Configure database backups + restore test schedule.
- Subscribe Stripe webhook to all relevant events; verify endpoint via Stripe CLI.
- No 2FA / MFA out of the box. Plan to add TOTP via
pyotp— seenotes/thingstofix.md§A.13. - No SAML / OIDC beyond Google OAuth. Enterprise SSO needs custom IdP integration.
- No automatic PII redaction in logs — be careful what you log.