
How We Keep AI-Written Code Secure: Defense in Depth for the Age of Generated Code
Contents
You can't keep AI-written code safe by "being careful."
Hand the work to an AI and the code grows several times faster than a person writes it. As it grows, the holes grow at the same pace: a permission left unclosed, a secret left sitting in the code, an input check that never got written. One person can't catch all of that by eye. Once you've taken the speed, review by eye no longer keeps up.
So we moved the defense from attention to machinery. Not one thicker wall, but several nets of different kinds layered together: defense in depth. I'll open up the layers running in PentaTrail today, from the outside in, by what each does and what it stops.
1. The perimeter: drop it before a line of code runs
The outermost layer is the edge (Cloudflare). Before traffic reaches the app, it sifts out bots and suspicious automated access. The target is indiscriminate scanning that roams looking for vulnerabilities, and automated high-volume access. Most of it dies here, before a single line of our code runs.
The sign-up form runs Turnstile behind it, to turn away bot-driven mass account creation and automated form abuse. Before you patch a hole, you keep the suspicious traffic out to begin with. Shrinking the attack surface is the first job.
2. Don't build auth yourself
Authentication is where accidents happen most. So we don't implement the core ourselves; we delegate it to Supabase and keep our own code to a minimum. Here's what we hand off, and what it stops.
- Passkeys (passwordless): public-key cryptography binds the key to the domain. Lure the user to a fake site and there's no key to hand over, so phishing doesn't work structurally. With no password at all, there's nothing to leak or reuse.
- Signing in with Google (OAuth): identity verification goes to Google. We hold no password, and we borrow Google's own protections, suspicious-login detection and two-factor included, as they are.
- Delegated session and token verification: the session-fixation and token-forgery mistakes common in homegrown code never get the room to be written in the first place.
Auth is the one thing we don't let an AI write from scratch. It 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
Even past authentication, there's a data boundary beyond it.
The foundation is Row Level Security (RLS): row by row, it binds who owns each row, so a tenant only ever sees its own data. What it stops is tenant crossing, the accident where one customer's data becomes 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. The idea is to put the guard at the deepest point. So our rule is to enable it the moment a table is created.
Functions have their own rules. A function that runs with elevated privilege (SECURITY DEFINER) reaches across RLS, so a lapse turns it into a hole. The tricky part is the default state: a newly created function or table tends to start out as "anyone logged in can touch it." The database hands privileges on a new object to every logged-in user on its own. Leave it alone and an ordinary user can directly call an elevated-privilege function they were never meant to reach (privilege escalation). We tighten this with discipline: explicitly close what's open by default, pin the function's search path (to stop an attack that slips in a malicious schema to hijack the elevated privilege), and keep least privilege.
Customers reach us over MCP, from a tool they have approved themselves. No credential sits on their side, so a key can neither be lost nor reused, approval can be revoked at any time from the admin console, and calls are capped to hold down brute force and abuse. The strongest key, the admin-privilege one that bypasses RLS entirely, never goes near the frontend. That makes the accident where the most powerful key is exposed in a browser and all the data leaks structurally impossible.
4. The code itself: safe before a line is written
The defense starts before any code is written. Some of the rules we set before writing:
- Inputs are validated against an allowlist, and queries are parameterized: to stop SQL injection and malformed values.
- Values shown on screen are escaped: to stop cross-site scripting (XSS).
- Secrets aren't written into code; they live in a key vault: so that even if the source leaks, the keys don't.
- File uploads are checked twice, by extension and by content (MIME): to stop disguised files getting in.
We keep these as rules the AI reads every time. Not fixing things after the fact, but tipping toward safe before writing: that's what shifting left means. While the code is being written, a security check runs in the background and stops a risky pattern the moment it tries to get in. The earlier you catch it, the cheaper the fix.
5. CI as the gatekeeper: a bundle of mechanical checks
Whatever gets written passes through the CI gate. Every time we open a PR, a lot of checks run automatically. This is the linchpin of the mass-production age. Here's what gets checked, and what it stops.
- Secret scanning (gitleaks): stops keys or tokens committed by mistake, to prevent intrusion through 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 before they merge.
- Permission tests (pgTAP, a permission matrix): pin down "this feature must not be executable with a logged-in customer's privileges" as a test, to stop the regression where a hole you once closed reopens in a later change.
- Integration tests: verify the API behaves exactly as specified, authentication and permissions included, to stop getting the authorization logic wrong.
- Type, build, lint: stop drift from the standards.
- A new-dependency gate: no adding dependencies on a whim, to stop supply-chain attacks where an automatic update pulls in a malicious version published moments ago.
Nothing merges until every check is green. gitleaks is just one item in the bundle. A person may think "it's probably fine," but if the machine shows red, it stops right there.
CI watches more than code. The information-security management system itself, our ISO 27001-based ISMS, is built into CI too. We hold the controls and their evidence as documents (a single source of truth), and the machine watches for and stops the "compliance drift" where controls and implementation pull apart. Security's rules aren't a binder on a shelf; they're something the machine watches. Only here can we really say the defense lives in the machinery.
6. The two-track eye, and where it lands
Finally, we layer human and AI eyes together.
A hole like a permission left open can get missed in unison by AIs of the same lineage. We've had a defect that several reviews with different lenses all slipped past, caught by an AI of a different lineage (we go into this in Two-Track Review with Claude and Codex). We add eyes from a different family, and a human checks last.
There's no perfect defense. Every layer leaves something that slips through. That's exactly why we don't rely on a single wall; we stack layers, so when one is passed, the next one stops it.
This isn't unique to AI. Traditional systems built around human review have no perfect defense either. The difference is whether you put 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 run on every PR with the same severity, never tiring, never forgetting: the perimeter, delegated auth, RLS and the function rules, coding standards, the CI gatekeeper, and two-track review.
And so, a system mass-produced with AI, run through this defense in depth, is no less secure than a traditional system built around human review. The mechanical net doesn't tire or drop its guard the way a person does, so it protects evenly. The speed of mass production and a solid defense can go together. That's the sense of it we arrived at over the past 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.
So far this has been about how we protect our own code. The same view turns outward too: how your company looks to an attacker, and what holes are open there. PentaTrail is a service that inspects exactly that, continuously, with AI.
Visualize your attack surface with PentaTrail CTEM/ASM
From discovery to vulnerability validation and remediation — all powered by the CTEM framework.
Get Started


