Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ Next.js 15 · React 19 · TypeScript · TailwindCSS v4 · ReactFlow · Vercel AI
- [Architecture Overview](docs/architecture/architecture-overview.md) — System design & security model
- [Security Docs](https://topflow.dev/docs/security) — Threat model & controls
- [Node Reference](https://topflow.dev/docs/build/nodes) — All 12 node types
- [AI Security Tutorials](docs/AI-Security/osv-scanner/README.md) — Hands-on case studies from real hardening work (SSRF, encryption, rate limiting, LLM constraints); published at [topflow.dev/blog](https://topflow.dev/blog)

---

Expand Down
5 changes: 5 additions & 0 deletions app/api/execute-workflow/__tests__/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jest.mock('@/lib/demo-data', () => ({
hasDemoData: jest.fn(),
}))

// Prevent @upstash/redis (ESM) from being loaded in jsdom environment
jest.mock('@/lib/security/upstash-rate-limit-store', () => ({
createUpstashStore: () => null,
}))

import { TopFlowExecutionEngine } from '@/lib/topflow-execution-engine'
import { validateWorkflow, validateApiKeys } from '@charliesu/workflow-core'
import { shouldUseDemoMode } from '@/lib/demo-mode'
Expand Down
9 changes: 7 additions & 2 deletions app/api/execute-workflow/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import { shouldUseDemoMode, hasDemoData as hasNewDemoData, resolveScanModes, typ
import { getDemoWorkflowResult, hasDemoData as hasLegacyDemoData } from "@/lib/demo-data"
import { RateLimiter, rateLimitKey } from "@/lib/security/rate-limit"
import { detectWorkflowCycle } from "@/lib/security/workflow-graph"
import { createUpstashStore } from "@/lib/security/upstash-rate-limit-store"

export const maxDuration = 30

// ============================================================================
// Rate Limiting (sliding window; in-memory store, pluggable for Redis/KV)
// Rate Limiting (sliding window; Upstash Redis when env vars present, else in-memory)
// ============================================================================

const limiter = new RateLimiter({ limit: 10, windowMs: 60_000 }) // 10 requests / minute / client
const limiter = new RateLimiter({
limit: 10,
windowMs: 60_000,
store: createUpstashStore() ?? undefined, // null → MemoryRateLimitStore default
})

// ============================================================================
// Input Sanitization
Expand Down
26 changes: 24 additions & 2 deletions components/blog/articles/ciso-to-fullstack-developer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Shield, Code, CheckCircle2 } from "lucide-react"
import { Shield, Code, CheckCircle2, ExternalLink } from "lucide-react"

export function CISOToFullStackContent() {
return (
Expand Down Expand Up @@ -26,7 +26,7 @@ export function CISOToFullStackContent() {
<h3 className="text-xl font-semibold text-foreground mb-3">Week 2: Core Workflow Engine</h3>
<ul className="space-y-2 list-disc list-inside ml-4 text-sm">
<li>Built drag-and-drop canvas with React Flow</li>
<li>Implemented 10 node types</li>
<li>Implemented 12 node types</li>
<li>Added cycle detection and validation</li>
</ul>
</div>
Expand Down Expand Up @@ -57,6 +57,28 @@ export function CISOToFullStackContent() {
Building TopFlow proved that security expertise makes you a better developer, and that hands-on development
makes you a better security leader.
</p>
<p className="mt-4">
The full source is open on{" "}
<a
href="https://github.com/csupenn/topflow"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
github.com/csupenn/topflow
<ExternalLink className="w-3 h-3" />
</a>
. The AI Security Tutorial series documents the real hardening work in detail:{" "}
<a
href="https://topflow.dev/blog"
className="text-primary hover:underline"
target="_blank"
rel="noopener noreferrer"
>
topflow.dev/blog
</a>
.
</p>
</div>
)
}
336 changes: 336 additions & 0 deletions components/blog/articles/encryption-bug-aes-gcm-ephemeral-key.tsx

Large diffs are not rendered by default.

194 changes: 146 additions & 48 deletions components/blog/articles/five-layers-of-security-owasp-top-10.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Shield, Lock, AlertTriangle, CheckCircle2 } from "lucide-react"
import { Shield, Lock, AlertTriangle, CheckCircle2, ExternalLink } from "lucide-react"

export function SecurityLayersBlogContent() {
return (
Expand All @@ -9,8 +9,18 @@ export function SecurityLayersBlogContent() {
production-grade security architecture. Every line of code reflects 15 years of security leadership experience.
</p>
<p>
This blog post is aimed at hiring managers, VCs, and security professionals evaluating technical talent. I want
to show that I don't just understand security theory—I implement it at every layer.
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{" "}
<a
href="https://github.com/csupenn/topflow"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
public GitHub repo
<ExternalLink className="w-3 h-3" />
</a>
.
</p>

<h2 className="text-3xl font-bold text-foreground mt-12 mb-4">The 5-Layer Security Model</h2>
Expand All @@ -24,23 +34,23 @@ export function SecurityLayersBlogContent() {
{
layer: "Layer 1: Client-Side",
icon: Shield,
controls: "Input sanitization, XSS prevention, CSP headers",
controls: "Input sanitization, XSS prevention, CSP headers, AES-256-GCM encryption of API keys at rest",
},
{ layer: "Layer 2: Transport", icon: Lock, controls: "TLS 1.3, HSTS, secure headers" },
{
layer: "Layer 3: API Gateway",
icon: AlertTriangle,
controls: "Rate limiting, DDoS protection, authentication",
controls: "Sliding-window rate limiting (in-memory ↔ Upstash Redis), DDoS protection",
},
{
layer: "Layer 4: Execution",
icon: CheckCircle2,
controls: "SSRF prevention, cycle detection, timeout enforcement",
controls: "SSRF prevention (provenance-aware), cycle detection, timeout enforcement, sandboxed JS",
},
{
layer: "Layer 5: External APIs",
icon: Lock,
controls: "HTTPS-only, user credentials, API validation",
controls: "HTTPS-only, user-held credentials (BYOK), no platform-managed secrets",
},
].map((item, idx) => (
<div key={idx} className="bg-card border border-border rounded-lg p-4 flex items-start gap-4">
Expand All @@ -54,7 +64,7 @@ export function SecurityLayersBlogContent() {
</div>

<h2 className="text-3xl font-bold text-foreground mt-12 mb-4">OWASP Top 10 Coverage</h2>
<p>Here's how TopFlow addresses each of the OWASP Top 10 vulnerabilities with specific implementation details:</p>
<p>Here's how TopFlow addresses key OWASP Top 10 vulnerabilities with specific implementation details:</p>

<div className="space-y-6 my-8">
<div className="bg-card border border-border rounded-lg p-6">
Expand All @@ -69,23 +79,25 @@ export function SecurityLayersBlogContent() {
<strong className="text-foreground">Mitigation:</strong>
</p>
<ul className="space-y-2 list-disc list-inside ml-4">
<li>Zod schemas validate all user inputs</li>
<li>DOMPurify sanitizes HTML content</li>
<li>No direct database queries (stateless architecture)</li>
<li>React auto-escapes JSX by default</li>
<li>Zod schemas validate all workflow inputs at the API boundary</li>
<li>No direct database queries (stateless architecture eliminates SQL injection entirely)</li>
<li>React auto-escapes JSX output by default</li>
<li>JavaScript nodes use <code className="text-primary text-sm bg-muted px-1 rounded">new Function()</code> instead of <code className="text-primary text-sm bg-muted px-1 rounded">eval()</code> with a limited scope</li>
</ul>
<pre className="bg-panel border border-border rounded-lg p-4 overflow-x-auto text-sm mt-4">
<code>{`import { z } from 'zod'
import DOMPurify from 'dompurify'

const NodeSchema = z.object({
id: z.string().uuid(),
type: z.enum(['llm', 'http', 'transform']),
config: z.record(z.string(), z.any()),
})

// Sanitize user-provided HTML
const clean = DOMPurify.sanitize(userInput)`}</code>
const WorkflowSchema = z.object({
nodes: z.array(z.object({
id: z.string(),
type: z.enum(['textModel', 'httpRequest', 'javascript', 'conditional', ...]),
data: z.record(z.string(), z.unknown()),
})),
edges: z.array(z.object({
source: z.string(),
target: z.string(),
})),
})`}</code>
</pre>
</div>

Expand All @@ -101,10 +113,27 @@ const clean = DOMPurify.sanitize(userInput)`}</code>
<strong className="text-foreground">Mitigation:</strong>
</p>
<ul className="space-y-2 list-disc list-inside ml-4">
<li>No PII storage (privacy-first architecture)</li>
<li>API keys stored client-side only (localStorage)</li>
<li>TLS 1.3 for all connections</li>
<li>HSTS headers enforce HTTPS</li>
<li>No PII storage — the platform never receives or stores user workflow data</li>
<li>
API keys encrypted at rest with AES-256-GCM (Web Crypto API) before being written to
localStorage — see{" "}
<a
href="https://github.com/csupenn/topflow/blob/main/lib/security/encryption.ts"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
lib/security/encryption.ts
<ExternalLink className="w-3 h-3" />
</a>
</li>
<li>TLS 1.3 for all connections; HSTS headers enforce HTTPS</li>
<li>
<strong className="text-foreground">Honest limitation:</strong> 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.
</li>
</ul>
</div>

Expand All @@ -122,8 +151,12 @@ const clean = DOMPurify.sanitize(userInput)`}</code>
<ul className="space-y-2 list-disc list-inside ml-4">
<li>Security headers (CSP, X-Frame-Options, X-Content-Type-Options)</li>
<li>Error messages don't leak stack traces in production</li>
<li>No default credentials (BYOK model)</li>
<li>Regular npm audit and Snyk scanning</li>
<li>No default credentials — BYOK model means no platform-managed secrets exist</li>
<li>
<code className="text-primary text-sm bg-muted px-1 rounded">typescript.ignoreBuildErrors</code> removed from{" "}
<code className="text-primary text-sm bg-muted px-1 rounded">next.config.mjs</code>; CI enforces
type-check on every PR
</li>
</ul>
</div>

Expand All @@ -133,16 +166,29 @@ const clean = DOMPurify.sanitize(userInput)`}</code>
A10: Server-Side Request Forgery (SSRF)
</h3>
<p className="mb-4">
<strong className="text-foreground">Risk:</strong> Internal network access, cloud metadata exposure
<strong className="text-foreground">Risk:</strong> Internal network access, cloud metadata credential theft
</p>
<p className="mb-4">
<strong className="text-foreground">Mitigation:</strong>
</p>
<ul className="space-y-2 list-disc list-inside ml-4">
<li>HTTPS-only URL validation</li>
<li>Private IP blocking (10.x, 172.16.x, 192.168.x, 169.254.x)</li>
<li>Cloud metadata endpoint blocking</li>
<li>Allowlist approach for external APIs</li>
<li>HTTPS/HTTP-only scheme allowlist — file://, ftp://, and all other schemes are rejected</li>
<li>Private IP blocklist: 10.x, 172.16–31.x, 192.168.x, 127.x, 169.254.x, CGNAT, multicast/reserved</li>
<li>Cloud metadata blocking: 169.254.169.254, metadata.google.internal, *.internal, *.local</li>
<li>
Provenance-aware exemption: engine-generated routes (e.g.{" "}
<code className="text-primary text-sm bg-muted px-1 rounded">/api/scan/github</code>) bypass
the check; only user-supplied URLs are validated — see{" "}
<a
href="https://github.com/csupenn/topflow/blob/main/lib/security/ssrf.ts"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
lib/security/ssrf.ts
<ExternalLink className="w-3 h-3" />
</a>
</li>
</ul>
</div>
</div>
Expand All @@ -154,46 +200,98 @@ const clean = DOMPurify.sanitize(userInput)`}</code>
<li className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-chart-3 flex-shrink-0 mt-0.5" />
<div>
<strong className="text-foreground">Rate Limiting:</strong> 10 requests/minute per IP using Redis-backed
distributed counters
<strong className="text-foreground">Durable Rate Limiting:</strong> Sliding-window limiter
(10 req/min per IP) backed by{" "}
<a
href="https://github.com/csupenn/topflow/blob/main/lib/security/rate-limit.ts"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
a pluggable store interface
<ExternalLink className="w-3 h-3" />
</a>
: <code className="text-primary text-sm bg-muted px-1 rounded">MemoryRateLimitStore</code> in
dev/test (injectable clock for deterministic unit tests),{" "}
<code className="text-primary text-sm bg-muted px-1 rounded">UpstashRateLimitStore</code> in
production (Redis-backed, durable across serverless instances)
</div>
</li>
<li className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-chart-3 flex-shrink-0 mt-0.5" />
<div>
<strong className="text-foreground">Timeout Enforcement:</strong> 30-second maximum execution time prevents
resource exhaustion
<strong className="text-foreground">Timeout Enforcement:</strong> 30-second maximum execution
time prevents resource exhaustion
</div>
</li>
<li className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-chart-3 flex-shrink-0 mt-0.5" />
<div>
<strong className="text-foreground">Cycle Detection:</strong> Graph analysis prevents infinite loops in
workflows
<strong className="text-foreground">Cycle Detection:</strong> DFS pre-execution check rejects
cyclic graphs before any node runs — prevents infinite-loop resource exhaustion
</div>
</li>
<li className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-chart-3 flex-shrink-0 mt-0.5" />
<div>
<strong className="text-foreground">Input Validation:</strong> Zod schemas enforce type safety and
constraints
<strong className="text-foreground">Input Validation:</strong> Zod schemas enforce type safety
and constraints at every API boundary
</div>
</li>
</ul>

<h2 className="text-3xl font-bold text-foreground mt-12 mb-4">Conclusion</h2>
<p>
Building secure applications isn't about adding security as an afterthought—it's about designing security into
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.
</p>
<p>
Want to see these security controls in action? Try building a workflow at{" "}
<a href="https://topflow.dev" className="text-primary hover:underline">
topflow.dev
</a>
.
</p>

<div className="bg-card border border-border rounded-lg p-6 my-6 space-y-3">
<h3 className="text-lg font-semibold text-foreground">Go Deeper</h3>
<p className="text-sm">
The AI Security Tutorial series covers each of these controls in detail — threat models,
attack trees, design trade-offs, and hands-on labs.
</p>
<div className="flex flex-wrap gap-x-6 gap-y-2 text-sm">
<a
href="https://topflow.dev/blog/01-ssrf-cycle-detection-rate-limiting"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
Tutorial 01 — SSRF, Cycle Detection & Rate Limiting
<ExternalLink className="w-3 h-3" />
</a>
<a
href="https://topflow.dev/blog/02-secrets-at-rest-byok-encryption"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
Tutorial 02 — Secrets at Rest (AES-256-GCM)
<ExternalLink className="w-3 h-3" />
</a>
<a
href="https://topflow.dev/blog/04-durable-rate-limiting-upstash-redis"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
Tutorial 04 — Durable Rate Limiting
<ExternalLink className="w-3 h-3" />
</a>
<a
href="https://github.com/csupenn/topflow"
className="text-primary hover:underline inline-flex items-center gap-1"
target="_blank"
rel="noopener noreferrer"
>
GitHub — csupenn/topflow
<ExternalLink className="w-3 h-3" />
</a>
</div>
</div>
</div>
)
}
Loading
Loading