Zudo Works

Security model

Zudo Works was red-teamed (17 attacks) and hardened in seven layers; the full report is reports/SECURITY_AUDIT.md. The parts that matter to users and integrators:

LayerWhat you get
Edge & transportHTTPS with HSTS (2 years, preload); CORS allow-list with credentials only for your app origin; Cloudflare WAF ruleset available
Headers & CSPStrict CSP (no inline scripts), X-Frame-Options DENY, nosniff, Referrer-Policy, Permissions-Policy; 1 MiB body limit (8 MiB on the feedback route)
AuthenticationTwo-factor on every new sign-in (e-mail code for everyone; optional TOTP app RFC 6238 ±1 step, secret AES-256-GCM at rest, reuse blocked; argon2id-hashed single-use backup codes); per-method attempt caps, server-timed holds before a fallback (15 s / 30 s), 30-minute lockout with e-mail, owner notice and audit; 10-minute pre-auth challenge (signed token + row), session created only after MFA with a fresh id; trusted sessions 60 days absolute, device rows required by the guard (sign-out per device / everywhere; revoked on password / role / status / MFA change); argon2id passwords (≥ 10 chars); sign-in throttle 5/min per IP+account, 50/min per IP, 15-minute lockout; OTP burns after 3 wrong codes; sessions regenerated at login and destroyed on role / status / password change; HttpOnly + Secure + SameSite=Strict cookies; CSRF = Origin guard + SameSite
AuthorisationTenant resolved from the session, never from the request; every table carries tenant_id and every query filters on it (lint-enforced); strict schemas reject tenant_id / role in bodies; permission-based guard; field-level RBAC; privilege-escalation rules (§17)
API & business logicAPI-wide flood limit (600 req/min per IP by default); SSRF guard on workflow URLs; row locks before idempotency checks (no double posting under concurrency)
E-mail senderMailbox credentials encrypted at rest (AES-256-GCM, key from SHOP_MAIL_TOKEN_KEY), never returned or logged; OAuth with PKCE and a single-use 10-minute state (the callback has no session); only send scopes, never read; SMTP hosts pass the same SSRF guard as workflow URLs with 10 s time-outs; document links are random ≥ 32-byte tokens stored hashed, expiring, revocable and rate-limited; Resend webhooks verified (Svix signature, 5-minute window); every templated value HTML-escaped; only settings.manage manages the sender
Zudo AIA model endpoint you configure (Fireworks AI Chat Completions — no data retention — or self-hosted Ollama) over HTTPS with an API key; read-only tools scoped to the caller's tenant, permissions and field-level RBAC; no SQL from the model; results marked untrusted; every change is a proposal the user approves, re-validated server-side, audited; secrets, hashes, tokens and mailbox credentials never leave the server; per-user rate limit and monthly plan quota
Data & storageServer-side HTML sanitiser for rich text and detail-view HTML; uploads verified by magic bytes (no SVG for photos; SVG logos screened for scripts); random, unguessable file names; parameterised SQL only
MonitoringGeneric error bodies (no framework internals); server-side logging of 500s; pnpm audit clean; red-team suite runs in CI

What you should do: keep API keys scoped and rotated (revoke on leak), verify webhook signatures, give people the smallest role that fits, review the audit log, and use strong passwords.

Last updated