Skip to content
AI and Code

Secure AI-Generated Code: Controls for Regulated Teams

13 min read

AI-generated web code fails in predictable ways: string-concatenated SQL, unencoded output, and permissive defaults. Better prompts help, but the fix we rely on is a control set that makes the insecure pattern unrepresentable. Roslyn analysers and banned-API rules turn raw SQL into a build error, and hooks check every edit. Continuous integration (CI) security gates and risk-weighted human review complete the set. For regulated UK teams, the same harness doubles as audit evidence for ISO 27001 secure development controls and for whatever assurance questions your sector regulator asks.

  • Use prompts to steer the first draft, and deterministic controls to guarantee the merged result.
  • Ban the escape hatches, so that raw SQL concatenation fails to compile and the model has no way to ship it.
  • Gate CI on security findings from day one, before a backlog builds up.
  • Aim human review at the areas tools cannot see: authorisation logic, trust boundaries, and abuse cases.

Our risks of AI-augmented development guide covers the wider risk set, and this guide covers the security controls in depth.

Teams worry about AI writing internet-facing code with customer data behind it

Teams moving desktop or internal systems onto the web with AI assistance tend to share one concern. How do we stop the model writing SQL injection, cross-site scripting, or sloppy state handling into code that will face the internet? For any organisation handling personal or commercially sensitive data, it is a fair question. It also comes up in supplier assessment, where the organisation’s own clients, auditors, and regulators will ask it.

The concern is well-founded when generation is unguarded. Security vendors’ published analyses of unguarded AI output consistently find injection and encoding weaknesses at high rates (Veracode’s analysis and Vidoc’s vulnerability teardown are representative examples). Models reproduce the most common patterns in their training data, and in decades of public code the most common patterns are often not the secure ones.

We do not think the answer is to keep AI away from web code. Instead, we treat security as a property of the pipeline the code passes through rather than of whoever wrote it, and the rest of this guide describes that pipeline.

Why are prompts and rules files not enough?

Prose guidance improves the average result, but it does not limit the worst case. In our experience a security section in CLAUDE.md (‘always use parameterised queries, always encode output’) does improve first drafts, and you should have one. An instruction is still only context, though, and context degrades. Long sessions, competing rules, and unusual code shapes all produce moments where the model emits a pattern its instructions forbid.

Our harness engineering guide draws a distinction between inferential and computational controls. Inferential controls (prompts, rules, model judgement) steer the model, while computational controls (compilers, analysers, tests, gates) guarantee an outcome. Security is a domain where guarantees count, so every security rule that can be enforced deterministically should be.

Making SQL injection unrepresentable

The strongest control is structural. All data access goes through parameterised stored procedures behind a repository layer, and the escape hatches are banned at compile time. We recommend this access pattern for performance as well as security, because query plans stay close to the engine and the application identity holds EXECUTE permissions only, with no direct table access. Even a misgenerated or compromised component then cannot run arbitrary SQL that the database will accept.

Layer rule. No type outside the data access project may reference Microsoft.Data.SqlClient. Enforce this with an ArchUnitNET test, so an agent that tries to bypass a repository with a direct connection breaks the build. Our tightly coupled code post shows the pattern.

Banned APIs. The BannedApiAnalyzers package flags dangerous members at compile time through a BannedSymbols.txt file. The file is checked into the repository and scoped to every project except data access:

Text
T:Microsoft.Data.SqlClient.SqlConnection; Data access goes through the repository layer
T:Microsoft.Data.SqlClient.SqlCommand; Data access goes through the repository layer

Inside the data access project, one further convention completes the control. Every command invokes a named stored procedure (CommandType.StoredProcedure) with typed parameters, enforced by an architecture test or a small analyser, so string-built SQL has nowhere to live in the codebase.

With TreatWarningsAsErrors turned on (our .NET migration guide sets this up), a banned symbol fails the build. The compiler applies the rule in the same way to a model and to a developer working under time pressure.

Security analysers in the build. Enable the security rules in the .NET analyser set (AnalysisMode with security rules elevated to errors). Add SecurityCodeScan or an equivalent tool for taint-style findings, which trace untrusted input through to a dangerous call. The agent can read analyser output, so a PostToolUse hook that builds after every edit feeds each finding straight back. The agent then fixes it in the same session, rather than waiting for a security review three weeks later.

Making cross-site scripting hard to write

Razor encodes output by default, so the goal is to protect the places where developers (and models) opt out of that safety:

  • Ban Html.Raw outside a sanctioned helper. Add it to BannedSymbols.txt, and provide one reviewed helper for the rare legitimate case that wraps sanitisation with a well-maintained library. Any diff that touches the helper gets senior review by policy.
  • Treat dangerouslySetInnerHTML the same way in React front ends, with an ESLint rule set to error and one sanctioned wrapper component.
  • Set the headers centrally. A Content Security Policy, X-Content-Type-Options, and frame options belong in middleware configured once, rather than in generated page code. Generated code cannot then leave them out, because they are set outside it.
  • Validate at the boundary. Use model binding with explicit view models, server-side validation as the source of truth, and anti-forgery tokens on state-changing endpoints. The agent applies these patterns consistently once CLAUDE.md names them and a code review skill checks for them.

The same approach covers the rest of the Open Worldwide Application Security Project (OWASP) Top 10. Choose the secure-by-default mechanism, centralise it, and ban the ways of opting out of it.

State, secrets, and configuration

Three further control areas need attention in a desktop-to-web transition specifically, because the legacy codebase was not written with web threats in mind.

State management. Desktop code trusts in-memory state, whereas web code must not trust anything the client can touch. The rules that need enforcing are no sensitive data in cookies or hidden fields, session state held server-side, and authorisation checked on every request rather than only at page entry. These rules are semantic rather than syntactic, so they become review-checklist items backed by tests.

Secrets. Deny rules in the agent configuration keep production configuration and secret files out of the agent’s reach entirely (our .NET configuration guide includes the deny list). Secret scanning in CI then catches anything that slips into a diff by another route.

Dependencies. Generated code pulls in packages, and choosing a package is a supply-chain decision. Pin the approved libraries for cryptography, sanitisation, and authentication in CLAUDE.md, put new package additions behind an ask-level permission, and run dependency scanning continuously. The governance framework for the agent-side supply chain is in governing the AI agent supply chain.

The pipeline: where each control runs

Each control runs at a different point, and the order is important because feedback closest to generation is the cheapest to act on:

  1. In-session (seconds). CLAUDE.md security conventions steer generation. PostToolUse hooks run the build with analysers and banned-API rules after every edit, so violations are corrected in the turn where they occur.
  2. Pre-merge (minutes). CI runs the full analyser set, security-focused tests, secret scanning, and a dependency audit, and new findings fail the build. Static analysis platforms (GitHub Advanced Security or equivalent) add taint tracking across the codebase, and our supported-software security guide covers that tooling layer.
  3. Review (risk-weighted). People review the areas where tools are blind: authorisation and access-control logic, trust boundaries, payment handling, and abuse cases. The blast radius of a change decides how deep the review goes. AI-generated code gets the same scrutiny as human code, which is a policy we hold across all our delivery (see why we do not let AI ship code unsupervised).
  4. Continuous (post-merge). Dynamic scanning against deployed environments and dependency monitoring continue after merge, because some classes of flaw only show up in a running system.

What does this give a regulated business, concretely?

It gives you evidence. Auditors, clients, and sector regulators ask a regulated UK organisation how it ensures the security of the software it runs, whoever or whatever wrote it. A harness of documented, automated controls lets you answer it with records:

  • The analyser and banned-API configuration lives in source control, so you can prove the control exists and show its history.
  • CI gate results and review records form an audit trail that maps onto ISO 27001 secure development controls. Our ISO 27001 and AI post covers the policy side.
  • Provenance records for AI-generated changes support procurement and assurance conversations, as covered in AI code attribution for enterprise procurement.
  • Supplier-side assurance over the model provider itself (terms, training exclusions, retention, residency) is a separate workstream, covered in the Claude GDPR guide.

An assurance that developers are careful with AI output gives an auditor nothing to inspect, while a gated pipeline produces records they can check.

Next steps

If AI-assisted delivery of web-facing code is on your roadmap, put the security harness into the environment before the first feature is built. That means banned APIs, analysers set as errors, hooks, CI gates, and a risk-weighted review policy. The setup cost is small, and it turns a difficult assurance question into one you can answer with evidence.

For the wider control framework, see harness engineering for coding agents and the risks of AI-augmented development. For the Claude Code project setup that the hooks, permissions, and CLAUDE.md conventions above sit in, see our Claude Code for .NET developers guide. If the trigger is a desktop-to-web modernisation, the WinForms to web guide covers the delivery side.

Want the harness reviewed or built? Book a free consultation.

Frequently asked questions

Is AI-generated code less secure than human-written code?
Unguarded, it repeats the insecure patterns common in its training data, such as string-concatenated SQL, unencoded output, and permissive configuration. Published analyses by security vendors repeatedly find injection and cross-site scripting weaknesses in unguarded AI output. The more useful comparison is between guarded and unguarded generation. With analysers, banned APIs, and gates in place, generated code is held to a stricter standard than a lot of hand-written code has historically met.
How do you stop AI writing SQL injection vulnerabilities?
Make the vulnerable pattern impossible to merge, instead of relying on instructions to the model. Route all data access through parameterised stored procedures behind the repository layer, ban direct SQL construction outside it with Roslyn banned-API rules, run security analysers in the build, and fail CI on new findings. Stored procedures also let the application identity hold EXECUTE permissions only. Prompt instructions still help with the first draft, but only the deterministic controls guarantee what gets merged.
What is wrong with relying on security prompts and rules files?
Prose guidance improves the average output, but it cannot guarantee anything. Under context pressure a model can produce a pattern its instructions forbid. Controls that run outside the model (analysers, banned APIs, hooks, CI gates) do not degrade in the same way. Use prompts to steer generation towards the secure pattern, and use deterministic checks to make the insecure pattern unmergeable.
How does this map to ISO 27001 and regulator expectations?
Auditors and sector regulators ask how you ensure the security of code, whoever or whatever wrote it. A harness of documented, automated controls (analysers in the build, gated CI, enforced review, and an audit trail of findings) gives you evidence you can demonstrate. A statement that 'developers review AI output carefully' does not. It fits within ISO 27001 secure development controls without a separate AI-specific framework.
Do we still need human security review?
Yes, aimed at the areas where tools are weak. Analysers catch pattern-level defects, while human reviewers catch design-level ones such as broken authorisation logic, trust-boundary mistakes, and business-logic abuse cases. With the controls in place, reviewers no longer need to scan for string-concatenated SQL, so they can spend their attention on flaws that only a person will find. Review effort should be weighted by the blast radius of the change.
Does generating code faster make us less secure?
It can, if the controls stay manual while generation speeds up. Manual review cannot keep pace with the volume, so the controls need to be computational, because automated checks scale with the code. Teams that pair agentic generation with an enforced harness tend to end up with a stronger security baseline than before. Every change, human or AI, then passes gates that previously depended on individual diligence.

Ready to transform your software?

Let's talk about your project. Contact us for a free consultation and see how we can deliver a business-critical solution at startup speed.