
A security review process for a startup is three things: a one-page threat model template you fill out per surface, a pre-merge checklist gate that blocks risky PRs, and a calendar of recurring audits (monthly dependency scans, quarterly pentests at Series A+). If you wait until SOC 2 to write one, you've already accumulated months of preventable risk. The goal is not perfection. It is making sure no new external endpoint, vendor, or auth path ships without someone explicitly thinking about it.
Most early-stage teams skip security review until a customer sends a SIG questionnaire, then panic-bolt a process in two weeks. That works once. This post is the version we'd set up on day one for a 3-to-15-engineer team.
Two things changed in the last 24 months that pushed security review from "nice to have" up the stack.
First, AI code generation made it trivial to add an endpoint. Cursor and Claude Code will happily scaffold a new API route in 90 seconds, including the database query, the response shape, and a half-decent test. They will not, by default, add authorization checks, validate input sizes, or rate-limit the route. The volume of code shipped per engineer has roughly doubled, and the share of that code with explicit security review has not.
Second, the dependency surface exploded. A standard Next.js + Drizzle + Stripe stack now pulls in 800 to 1,200 transitive npm packages. The xz-utils backdoor in 2024 and the eslint-config-prettier compromise in early 2026 made it clear that "vetted" upstream maintainers are not actually a defense. You need to assume any week, one of your dependencies will turn hostile, and you need to find out within hours, not months.
The good news: the tooling caught up. GitHub Advanced Security, Snyk, Semgrep, and Trivy all became materially better, and most have a free tier that covers a startup until $1M ARR. The hard part is the process, not the tools.
Every security review starts with a one-page threat model. We use a three-axis grid that fits on a Notion page:
Data sensitivity × Attacker × Surface.
You pick one cell. You spend 20 minutes. You write down what could go wrong.
| Axis | Categories | What to ask |
|---|---|---|
| Data sensitivity | Public, Internal, Customer PII, Customer payment, Customer health (PHI), Auth tokens | What's the worst leak? Who do we have to notify? |
| Attacker | Bored script kiddie, Competitor doing recon, Disgruntled ex-employee, Targeted nation-state, Malicious dependency | What can they reasonably afford to spend? |
| Surface | Public web endpoint, Authenticated API, Admin panel, CI/CD pipeline, Vendor webhook, Local dev | What's the blast radius if they get in? |
For most startup features, you only care about three or four cells. A new public webhook that receives Stripe events lives in (Customer payment × Bored kiddie × Vendor webhook). The questions are: is the signature verified, can the endpoint be replayed, what happens if it's flooded with bogus events.
You do not need to be an OWASP expert to do this. You need to write the cell down before you ship. If you want a deeper checklist of what to mitigate per cell, our OWASP Top 10 implementation guide maps the 10 categories to concrete Node and TypeScript fixes.
Three triggers force a new threat model:
This is the gate. No exceptions, no "we'll do it next sprint." If the PR touches any of these, the threat model lands in the PR description before review.
The threat model is the thinking step. The checklist is the doing step. It runs as a PR template that the engineer fills in (and a CI job that enforces the automated parts).
## Security checklist
- [ ] Auth: new routes require authentication unless explicitly public
- [ ] Authorization: RLS or middleware enforces row-level access per tenant
- [ ] Input validation: zod / valibot schemas on all request bodies, query, params
- [ ] Output encoding: no raw SQL string interpolation, no innerHTML with user data
- [ ] Secrets: no hardcoded keys; new env vars added to .env.example and the secret manager
- [ ] CORS: explicit allowlist on new public endpoints, not wildcard
- [ ] Rate limiting: new public endpoints have a rate limit middleware
- [ ] Dep audit: `pnpm audit` clean, or exceptions justified in the PR
- [ ] Logging: no PII / tokens / passwords in log lines
- [ ] Threat model: filled out for new external endpoints, vendors, or auth paths
Ten boxes. Three minutes per PR if you have your tooling wired up. Block merge if any unchecked box doesn't have an explicit "N/A: [reason]" written next to it.
The two highest-ROI items are auth and RLS. Most data leaks at startups are not zero-days. They are an endpoint that forgot to check the tenant ID, or a query that joined across tenants because the developer forgot the where org_id = ? clause. If your stack uses Postgres, lean hard on Postgres Row-Level Security policies; getting RLS right means your application code can no longer leak across tenants even if the developer forgets. Our multi-tenant Postgres schema guide covers the RLS patterns that scale beyond 10 tenants.
Security work that only happens on PRs misses entire categories of risk. You also need calendar-driven audits.
| Cadence | What runs | Who owns it | Cost |
|---|---|---|---|
| Per PR | Checklist gate + Semgrep + secret scanner | Engineer who opens PR | Free |
| Daily | Dependabot / Snyk alerts triaged | On-call engineer | Free to $98/mo |
| Weekly | Review failed auth attempts, anomalous traffic | Security-paranoid engineer | Internal time |
| Monthly | Full dep audit, rotate stale secrets, review IAM | Security-paranoid engineer | Half a day |
| Quarterly | Pentest (Series A+) or peer review (pre-A) | External firm or peer CTO | $8k-25k per pentest |
| Annually | Tabletop exercise, full threat model refresh | CTO + on-call rotation | One day |
The monthly dep audit deserves a sentence of its own. Run pnpm audit and npm audit against production lockfiles. Run trivy fs . for the container layer. Triage every High or Critical. The 30-minute version: most are noise. The dangerous ones are usually in transitive dependencies of build tooling, which is exactly where supply-chain attacks land.
The quarterly pentest only makes sense after Series A, when you have customers writing security questionnaires asking for a SOC 2 Type II report. Before that, the ROI is bad: a $15k pentest on a 20-engineer codebase will find issues your own Semgrep run would have caught for free. Spend the money on tooling and process first.
There is no single right stack. There is the right stack for your stage. Here's the honest comparison.
| Stage | Team size | Primary tools | Monthly cost | What it catches |
|---|---|---|---|---|
| Pre-seed | 1-5 | GitHub Dependabot, gitleaks pre-commit, manual Semgrep CLI | $0 | Known CVEs, hardcoded secrets, basic patterns |
| Seed | 5-15 | GitHub Advanced Security OR Snyk Free, Semgrep CI, Trivy in build | $0-200 | Above + SAST, container CVEs, IaC misconfigs |
| Series A | 15-40 | Snyk Team or GitHub Advanced Security paid, Semgrep Pro, Trivy + Wiz/Orca | $1k-4k | Above + custom rules, runtime, cloud posture |
| Series B+ | 40+ | Snyk Enterprise + GHAS + Wiz + dedicated AppSec hire | $10k+ | Above + threat intel, secrets rotation, custom detections |
A few honest notes about each tool:
GitHub Advanced Security ships with secret scanning, code scanning (CodeQL), and dependency review. If you live in GitHub already (which most startups do), it integrates cleanest. The downside: per-committer pricing gets expensive past 20 engineers, and CodeQL is slower than Semgrep for incremental scans.
Snyk has the best dependency-vulnerability database, full stop. It also has the most aggressive sales motion, and the free tier limits get tight fast. If you're paying anyway, Snyk Team is roughly $98 per developer per month and covers SCA, container, and IaC.
Semgrep is the SAST tool engineers don't hate. The OSS rules catch real issues, custom rules are writable in YAML in 10 minutes, and the CI runtime is fast enough to run on every PR. It does not do dependency scanning; pair it with Dependabot or Snyk.
Trivy is the container scanner you reach for. Open source, runs locally and in CI, scans images, filesystems, IaC, and SBOMs. Pair with a paid runtime tool only after Series A.
Whichever you pick, the tooling is only as good as the response process. A queue of 400 unresolved Snyk alerts is worse than no scanner at all, because it teaches the team to ignore the dashboard.
Most startups make the security hire at the wrong time. The two failure modes are symmetric: hiring a full-time AppSec engineer at 8 engineers (they have nothing to do for 4 hours a day), or waiting until 50 engineers to hire one (you've been one breach away from a board call for two years).
The right framing is: at every stage, someone needs the explicit security hat. That person does not have to be a dedicated hire.
If you're between stages and need the security-paranoid hat covered fast, this is one of the cleanest fits for booking through Cadence. A senior engineer ($1,500/week) with a security background can stand up the checklist, configure Semgrep + Snyk + Trivy, run the first month of audits, and hand off a maintainable process to your team in 3 to 4 weeks. Every engineer on Cadence is AI-native by default (Cursor / Claude / Copilot fluency vetted in voice interview), which matters here because most modern SAST and dependency triage runs faster when you can prompt-generate the fix candidates and let a human approve.
You can audit your current security posture using our ship-or-skip stack audit tool, which scores your repo against a 30-point checklist including security and gives a senior-eng take in 90 seconds.
Best practices have ROI curves. If you're 2 founders, pre-revenue, building a CRUD app with Stripe and Supabase, you do not need a quarterly pentest. You need:
That is the minimum. Everything else in this post scales in as you cross meaningful thresholds: first enterprise customer, first SIG questionnaire, first regulated data type, Series A funding, 10th engineer.
The reason to write the process down now, even if you only execute the minimum, is that the cost of bolting it on later is roughly 5x. Teams that retrofit security review at 30 engineers spend a full quarter doing it. Teams that grew the process from day one barely notice.
Pair this with a tested incident response playbook so you know how to handle the day something does go wrong; our how to write a postmortem after an incident covers the format we recommend.
Setting up your first real security review process and need an extra senior engineer to drive it for 3 to 4 weeks? Cadence shortlists vetted senior engineers ($1,500/week) in 2 minutes, with a 48-hour free trial. Book your first engineer and have a checklist gate, monthly dep audit, and threat model template live by next sprint.
A working version (PR template, Dependabot, Semgrep in CI, threat model doc) takes one senior engineer roughly 3 to 5 days end-to-end. Adding Snyk or GHAS, writing the monthly audit runbook, and tuning rules to your codebase adds another 2 weeks of part-time work. Plan for one full month before the process feels routine.
At seed stage, $0 to $200 per month covers it: free tiers of Dependabot, Semgrep, Trivy, and gitleaks, plus optionally Snyk Free or GitHub Advanced Security. At Series A, budget $1k to $4k per month as you add paid SAST, container runtime, and cloud posture management. Pentest costs land separately at $8k to $25k per engagement.
For SOC 2 Type II, a pentest is not strictly required by the AICPA, but most auditors (and every enterprise customer reviewing your report) will expect one annually. Time your first pentest for 60 to 90 days before your audit window opens, so you have time to remediate findings before the auditor pulls evidence.
The CTO, with 25% of one senior engineer's time as the operational owner. A dedicated AppSec hire does not pencil out below roughly 25 engineers unless you're in healthcare, fintech, or another regulated vertical. Before that, a Cadence senior engineer on a 4-week booking can stand up the process and hand it off.
Postgres Row-Level Security, if your stack uses Postgres. It catches the most common startup data leak (cross-tenant query bugs) at the database layer, where application code can't override it. After that, gitleaks as a pre-commit hook is the cheapest insurance against the most common deploy-day disaster (leaked API keys).
Backend developer at withRemote. Writes on API design, observability, and database trade-offs.