← Overnight blog Security & Architecture

Deny-by-Default: Designing Guardrails AI-Generated Code Can't Escape

You can't predict what a model will write, so you can't enumerate everything it might do wrong. The only guardrail that holds denies everything you didn't explicitly allow, and enforces that where the code runs.

Guide 5 min read Updated July 21, 2026

There are two ways to build a guardrail. You can start from "everything is allowed" and try to enumerate the bad things to block, which gives you a blocklist. Or you can start from "nothing is allowed" and enumerate the specific things to permit, which gives you an allowlist. Deny by default security is the second approach, and for AI-generated code it is the only one that survives contact with reality. NIST defines the posture plainly: deny by default means blocking all traffic that "has not been expressly permitted." Everything else fails closed.

Why deny by default security is the only model that scales to AI code

A blocklist only works if you can name the threats in advance. That assumption breaks the moment a language model is writing the code. You don't know which library it will import, which endpoint it will call, or which query it will construct, because the whole point of generation is that the output isn't specified ahead of time. Any blocklist you write is a guess about code that doesn't exist yet, and the model has more ways to surprise you than you have rules to catch them.

Allowlisting inverts the burden. Rather than predicting every dangerous thing the model might emit, you declare the small, finite set of things a given app is supposed to do, such as read this table, call that API, write to this bucket, and deny the rest by construction. You never have to anticipate the exploit. Anything you didn't sanction is simply out of reach. This is the same logic OWASP recommends for authorization: "an application should be configured to deny access by default," and developers should adopt "a 'deny-by-default' mentality" whenever new functionality is exposed.

The core asymmetry

A blocklist has to be right about every threat. An allowlist only has to be right about your app's legitimate behavior, a set you already know because you defined it. With AI writing the code, one of those is knowable and the other is a guess.

Blocklists enumerate evil; allowlists enumerate good

Nobody invented this for AI. It is the default posture of every serious network boundary. NIST's control catalog specifies it directly in SC-7(5): "Deny network communications traffic by default and allow network communications traffic by exception." Zero trust generalizes the same principle from the network to every resource. NIST SP 800-207 holds that "trust is never granted implicitly but must be continually evaluated," that access is "granted on a per-session basis," and that authorization to one resource "will not automatically grant access to a different resource." Nothing is ambient. Every reach has to be justified.

AI-generated software is exactly the workload zero trust was designed for. No human reviewed the code line by line, so implicit trust is off the table. What you extend it instead is a manifest: least privilege, granted per app, with everything outside that manifest denied.

Egress and data access: deny the reach, and intent stops mattering

Two boundaries matter most. The first is egress. If a generated app is compromised, or simply wrong, the damage is bounded by where its traffic can go. Carnegie Mellon's Software Engineering Institute is blunt about the tradeoff: "Default deny is the more secure posture," and filtering outbound traffic to known destinations "can prevent data exfiltration," including data smuggled out disguised as ordinary DNS or mail traffic. An allow-by-default network lets a shadow app phone home to anywhere. A default-deny egress boundary lets it reach only the handful of destinations you approved and drops the rest.

The second is data access. A model that writes a query has no concept of tenant boundaries, row-level scope, or which records are none of this app's business. If the runtime hands it a broad database credential, the credential defines the blast radius. If the runtime mediates every call and denies any table, tenant, or scope the app wasn't explicitly granted, the model's mistakes stay small. The app can only touch what its manifest names, and a query for anything else comes back empty rather than raising an error you hope someone notices.

Guardrails belong in the runtime

Here is where most attempts go wrong. Teams write the rules into the system prompt ("only query the orders table, never call external URLs") and treat that as the control. A prompt is a suggestion. The model can ignore it, misread it, or be talked out of it by injected input, and even a well-behaved model produces an SDK call that another process can bypass entirely. Instructions the code is asked to follow amount to a preference, and a preference is not a boundary.

A real guardrail is enforced below the code, at the runtime, where it holds regardless of what the model wrote or whether the SDK was used at all. The policy stops being "please don't" and becomes a mediation layer that inspects every call, checks it against the allowlist, and denies anything unlisted before it reaches the network or the database. The code can be buggy, adversarial, or entirely bypassed, and the boundary still decides what actually happens. That is the distance between a rule the code should obey and a rule it cannot escape.

Put those pieces together and the failure mode changes shape. The shadow app that would have been a breach, the one that quietly copies a customer table to an unknown host, never gets the chance. No scanner or alert catches it after the fact. Under deny by default, it cannot reach the data at all.

Sources

  1. NIST Computer Security Resource Center, "Deny by Default" (glossary, from SP 800-41 Rev. 1) · csrc.nist.gov/glossary/term/deny_by_default
  2. NIST SP 800-53 Rev. 5, Security and Privacy Controls for Information Systems and Organizations, control SC-7(5) "Deny by Default — Allow by Exception" · nvlpubs.nist.gov
  3. Rose et al., "Zero Trust Architecture," NIST Special Publication 800-207 · nvlpubs.nist.gov/nistpubs/specialpublications/NIST.SP.800-207.pdf
  4. Carnegie Mellon Software Engineering Institute, "Best Practices and Considerations in Egress Filtering" · sei.cmu.edu/blog/best-practices-and-considerations-in-egress-filtering
  5. OWASP Authorization Cheat Sheet (deny by default, least privilege) · cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html
Early access

Request access

Tell us where you want to run AI-written code and we will get back to you.

We use this to connect with you, and for nothing else. No recurring marketing emails.