5 Layers of Security: How TopFlow Mitigates OWASP Top 10
As a former CISO, I don't just talk about security—I implement it. Here's TopFlow's 5-layer defense-in-depth model, how it maps to the OWASP Top 10 (2021), and the gaps that remain.
Updated June 17, 2026 — corrections
We audited this post against the code. Changes: categories now use the OWASP Top 10 (2021) numbering (the original mixed 2017 and 2021 names); we removed claims of Zod validation at the API boundary and a Content Security Policy — neither existed then — and corrected a claim that every connection uses TLS 1.3 (the site accepts TLS 1.2 and 1.3). A CSP now runs in report-only mode, a new A09 section covers logging, and A03 describes a serious issue we found and contained during this audit: workflow code could reach server secrets. Gaps that remain are named in the text.
Security as a Showcase Priority
As a former CISO, I built TopFlow not just as a functional AI workflow builder, but as a demonstration of production-grade security architecture. Every line of code reflects 15 years of security leadership experience.
This post covers TopFlow's 5-layer defense-in-depth model and how it maps to the OWASP Top 10. All referenced source files are in the public GitHub repo.
The 5-Layer Security Model
TopFlow uses a defense-in-depth approach with five distinct security layers. Each layer addresses specific threats, and together they provide comprehensive protection:
Layer 1: Client-Side
React output escaping, Content Security Policy (report-only; enforcement next), AES-256-GCM encryption of API keys at rest, condition tests in an isolated sandbox
Layer 2: Transport
HTTPS only (TLS 1.2 and 1.3), HSTS
Layer 3: API Gateway
Sliding-window rate limiting (in-memory ↔ Upstash Redis), DDoS protection
Layer 4: Execution
SSRF prevention (provenance-aware), cycle detection, timeout enforcement, custom JavaScript disabled on the hosted service (built-in template code only) until a real isolate ships
Layer 5: External APIs
HTTPS-only, user-held credentials (BYOK), no platform-managed secrets
OWASP Top 10 Coverage
Here's how TopFlow addresses key categories of the OWASP Top 10 (2021), with implementation details and the gaps that remain:
A02:2021 — Cryptographic Failures (formerly Sensitive Data Exposure)
Risk: PII leakage, API key exposure, data breaches
Mitigation:
- No server-side storage — workflow data and keys are sent only to run a workflow, used in memory, and never stored or logged
- API keys encrypted at rest with AES-256-GCM (Web Crypto API) before being written to localStorage — see lib/security/encryption.ts
- HTTPS for all connections (TLS 1.2 and 1.3); HSTS enforces HTTPS
- Honest limitation: a client-held key is not XSS-proof — a script running in the page can read both the ciphertext and the key from localStorage. The encryption protects against plaintext-at-rest inspection and casual exfiltration, not against a script-injection attacker.
A03:2021 — Injection
Risk: SQL injection, command injection, XSS attacks
Mitigation:
- No database, so no database queries — SQL injection has nothing to target
- React auto-escapes JSX output by default
- The execution route strips
<and>from string inputs and validates workflow structure (required nodes, configuration, cycles) before anything runs - The visual condition builder emits user values as JSON string literals, so a value can't break out into code (fixed in this update)
- Honest limitations: there is no schema validation (e.g. Zod) of the request at the API boundary yet. And until this update, JavaScript, Tool and condition code ran on the server via
new Function()— which could reach server secrets. We found and contained that: the hosted service now runs only built-in template code, and conditions are interpreted by a safe parser instead of being evaluated. A real isolate is next.
// app/api/execute-workflow/route.ts — string inputs are stripped of < and >
function sanitizeInput(input: any, skipKeys: string[] = []): any {
if (typeof input === "string") return input.replace(/[<>]/g, "")
if (Array.isArray(input)) return input.map((item) => sanitizeInput(item, skipKeys))
// …objects are sanitized key by key (code/schema/output fields skipped)
}A05:2021 — Security Misconfiguration
Risk: Exposed endpoints, verbose errors, default credentials
Mitigation:
- Security headers from one source (
lib/security/security-headers.cjs): X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and a Content Security Policy in report-only mode while violation reports are reviewed - Error messages don't leak stack traces in production
- No default credentials — BYOK model means no platform-managed secrets exist
typescript.ignoreBuildErrorsremoved fromnext.config.mjs; CI enforces type-check on every PR, plus tests with blocking coverage thresholds on the security modules
A09:2021 — Security Logging and Monitoring Failures
Risk: too little logging to notice attacks — or logs that themselves leak sensitive data
Mitigation:
- Execution logs record counts, IDs and error types — never workflow content, inputs or keys. A test plants a canary in every user-controlled field and fails if it reaches the logs. (Until this update the logs did contain user input.)
- CSP violation reports are logged as directive + blocked origin only, size-capped and rate limited
- Honest limitation: there is no alerting or security dashboard yet — logs are reviewed manually.
A10:2021 — Server-Side Request Forgery (SSRF)
Risk: Internal network access, cloud metadata credential theft
Mitigation:
- HTTPS/HTTP-only scheme allowlist — file://, ftp://, and all other schemes are rejected
- Private IP blocklist: 10.x, 172.16–31.x, 192.168.x, 127.x, 169.254.x, CGNAT, multicast/reserved
- Cloud metadata blocking: 169.254.169.254, metadata.google.internal, *.internal, *.local
- IPv4-mapped IPv6 blocked in any spelling — tests call the guard through the URL parser, the way production does (a hex-form bypass was found and fixed during this audit)
- The builder's validation panel calls the same guard, so what it shows matches what the server enforces
- Provenance-aware exemption: engine-generated routes (e.g.
/api/scan/github) bypass the check; only user-supplied URLs are validated — see lib/security/ssrf.ts
Production-Grade Controls
Beyond OWASP, TopFlow implements additional security controls:
- Durable Rate Limiting: Sliding-window limiter (10 req/min per IP) backed by a pluggable store interface:
MemoryRateLimitStorein dev/test (injectable clock for deterministic unit tests),UpstashRateLimitStorein production when Upstash is configured (Redis-backed, durable across serverless instances; client IPs are HMAC-hashed before they reach Redis) - Timeout Enforcement: 30-second maximum execution time prevents resource exhaustion
- Cycle Detection: DFS pre-execution check rejects cyclic graphs before any node runs — prevents infinite-loop resource exhaustion
- Structural Validation: every workflow is checked for required nodes, valid configuration and SSRF-safe URLs before execution (schema validation of the raw request is a planned addition)
Conclusion
Building secure applications isn't about adding security as an afterthought — it's about designing security into every layer from the start. TopFlow demonstrates that former CISOs can still code, and that security expertise translates directly into better architecture decisions.
Go Deeper
The AI Security Tutorial series covers each of these controls in full — threat models, attack trees, design trade-offs, and hands-on labs. Tutorials live in the GitHub repo alongside the code they document.
Leadership perspective: turn these controls into an evidence-led AI security review (charliesu.com)