How We Keep AI-Written Code Secure — Defense in Depth for the Age of Generated Code
Table of Contents
Behind the speed and volume, the holes multiply too—so security goes into the machinery, not into human vigilance
Hand the work to an AI and the code grows fast, and in volume. But the more code there is, the more little holes there are too—a permission left open, a secret left in, a missing input check. Finding all of those by eye, alone, isn't realistic.
So we lean security on machinery, not on human attention. Not a single wall, but many nets layered together—defense in depth. Here we'll introduce a part of how PentaTrail implements security, from the outside in—what each layer does, and what it defends against.
1. The perimeter — catch it before the app
The outermost layer is an edge defense we deploy in front of the app (Cloudflare). Before traffic ever reaches the app, it sifts out bots and automated, suspicious access. What this defends against is indiscriminate scanning that roams looking for vulnerabilities, and automated high-volume access. Most suspicious traffic dies at the edge before a single line of app code runs.
On forms like sign-up, we implement Turnstile. Bot-driven mass account creation and automated abuse of the forms get turned away behind the scenes. Before you patch holes, you keep the suspicious stuff out in the first place. That's the first layer.
2. Don't build auth yourself
Next is authentication, and here the policy is firm: don't implement it yourself.
The login layer is where accidents happen most. So the core of authentication is delegated to Supabase, keeping our own code to a minimum. What we do, and what it defends:
- Passkeys (passwordless) are bound to the domain by public-key cryptography. Lure the user to a fake site and there's no key to hand over—phishing simply doesn't work structurally. With no password at all, there's nothing to leak or reuse.
- Signing in with Google (OAuth) delegates identity verification to Google. We hold no password ourselves, and we inherit the protections on the Google account side—suspicious-login detection and two-factor included.
- Delegating session and token verification too means the session-fixation and token-forgery mistakes common in homegrown implementations never get written in the first place.
Not letting an AI write auth from scratch was one of the most important lines to draw in an age of mass-produced code.
3. The data boundary — RLS and the rules for functions
Past authentication, what holds is the data boundary.
The foundation is Row Level Security (RLS): binding, row by row, who owns each row, so each tenant only ever sees their own data. What this defends against is tenant crossing—one customer's data becoming visible to another. Even if a permission check is forgotten one level up, in the API, at the database layer that row only ever returns to its owner. It's the idea of placing the guard at the deepest point. Enabling it the moment a table is created is a rule we keep.
And database functions come with rules of their own. Functions that run with elevated privilege (SECURITY DEFINER) can reach data across RLS, so a lapse turns them into a hole. The tricky part: a newly created function or table tends to start out set so that anyone who is logged in can touch it. The database automatically hands privileges on a new object to every logged-in user. So if you do nothing, a logged-in ordinary user can directly call an elevated-privilege function they were never meant to (privilege escalation). So we tighten it with discipline: explicitly close what's open by default; pin the function's search path (defending against an attack that slips in a malicious schema to hijack the elevated privilege); keep least privilege.
The customer-facing API layers on an API key, plus source-IP restrictions and rate limiting. Even if a key leaks, the source is constrained (IP restriction), and brute force and abuse are throttled (rate limiting). And the strongest key—the admin-privilege one that bypasses RLS entirely—never goes near the frontend. That structurally rules out the accident where the most powerful key is exposed in a browser and all data leaks.
4. The code itself — standards and shifting left
The layering starts before a line is even written. What we do, and what it defends:
- Inputs are validated against an allowlist, and queries are parameterized — defending against SQL injection and malformed values.
- Values shown on screen are escaped — defending against cross-site scripting (XSS).
- Secrets aren't written into code; they live in a key vault — so even if the source leaks, the keys don't.
- File uploads are checked twice, by extension and by content (MIME) — defending against disguised file uploads.
We keep these as rules the AI reads every time—correcting later isn't the point; you tip toward safe before writing. That's shifting left. And while code is being written, a security check runs in the background, stopping a risky pattern the moment it tries to go in. The earlier you catch it, the cheaper the fix.
5. CI as the gatekeeper — a battery of mechanical checks
Whatever gets written must pass through CI. Every time you open a PR, a host of checks runs automatically—the linchpin of the mass-production age. What gets checked, and what it defends:
- Secret scanning (gitleaks): stops keys or tokens committed by mistake (defending against intrusion via a leaked key).
- Permission gates: check whether elevated-privilege functions or anonymous access leave any hole that shouldn't be open (stopping privilege-escalation and anonymous-access holes from being merged).
- Permission tests (pgTAP, a permission matrix): pin down "this feature must not be executable under a logged-in customer's privileges" as a test (defending against the "regression" where a hole you once closed reopens in a later change).
- Integration tests: verify the API behaves exactly as decided, authentication and permissions included (defending against getting the authorization logic wrong).
- Type, build, lint: stop drift from the standards.
- A new-dependency gate: don't let dependencies grow on a whim (defending against supply-chain attacks—an automatic update pulling in a malicious version published moments ago).
Nothing merges until everything is green. gitleaks is just one in this battery. A human may think "it's probably fine," but if the machine shows red, it stops right there.
And CI watches more than code. The information-security management system itself—our ISMS, based on ISO 27001—is built into CI too. The controls and their evidence are held as documents (a single source of truth), and the machine watches for, and stops, the "compliance drift" where controls and implementation diverge. Security's rules of governance aren't a binder on a shelf; they're something the machine watches. Only at this point can you really say security lives "in the machinery."
6. The two-track eye, and where it lands
Finally, we overlay human and AI eyes.
A hole like a leftover-open permission can slip past same-lineage AIs in unison. In fact, a permission leak that several distinct-lens reviewers all missed was caught by an AI of a different lineage (we wrote about this in Two-Track Review with Claude and Codex). Add an eye from a different family, and let a human check last.
Honestly, there's no perfect defense. Every layer leaves something that slips through. Which is exactly why you don't bet on a single wall—you stack layers. One gets past; the next stops it.
And this isn't unique to AI. Traditional systems, built around human review, have no perfect defense either. The difference is whether you place the guard on human attention or on machinery. Human-centered review rises and falls with a reviewer's focus and experience, and as volume grows, things slip. The layers above, by contrast—the perimeter, delegated auth, RLS and the function rules, coding standards, the CI gatekeeper, two-track review—don't tire, don't forget, and run with the same severity on every single PR.
So here is our conclusion: a system mass-produced with AI, run through this defense in depth, is no less secure than a traditional system built around human review. If anything, the mechanical net doesn't waver—it guards evenly, without fatigue or lapse. The speed of mass production and the solidity of the defense can coexist—that was the conviction we arrived at over this year.
How we assembled this stack over a year is in How a Micro-SaaS Tech Stack Changed in a Single Year, and other posts on how we build are gathered in the dev category.
And we point the same lens at ourselves—continuously inspecting our own externally visible attack surface with AI. The product that came out of that is PentaTrail: CTEM, a service that keeps grasping your attack surface, continuously. If you're curious, take a look at your own company's "externally visible attack surface."
Visualize your attack surface with PentaTrail/CTEM
From discovery to vulnerability validation and remediation — all powered by the CTEM framework.
Get Started