TL;DR: GenAI is making it easy for non-developers to ship production code, but it also bypasses the review and safety checks we’ve built around human-written software. The risk isn’t “AI wrote bad code,” it’s that things feel finished sooner and insecure behavior slips into production without anyone intentionally accepting that risk. The fix is to engineer the path from prompt to production with secure-by-default scaffolding like templates, standardized auth, secrets management, CI gates, network controls, logging, and least privilege. Put the guardrails in the IDE, repo, and deployment pipeline so the safe choice is the easiest choice.
Over the last year, we’ve seen generative AI workflows in two main ways. First, augmenting operator-performed work, and second, the unique use cases of our clients using it directly inside their own environments. Some interesting use cases involve marketing campaigns, accelerating fintech workflows, performing log analysis, automating aspects of the security assessment process, customer service chat solutions, and increasingly enabling non-developers to generate production application code with prompt-driven tooling.
That last one is where things get interesting & we’ve seen real security risks occur. When someone can describe a workflow in plain language and a tool produces a service that “mostly works,” writing code is no longer the hard part. Controlling that code is: how it gets produced, wired, deployed, and maintained.
The security goal itself hasn’t changed. Nobody wants to ship something unsafe. What has changed is that AI-generated code doesn’t trigger the same fail-safes that have been iteratively built around reviewing and validating human-written code. Things feel “done” sooner, and unsafe behavior slips through without anyone making a conscious decision to take on that risk.
The starting point remains the same: understand your architecture, map likely abuse paths, and put controls in place that don’t rely on every prompt getting it right. From there, the harder question becomes: what scaffolding should developers provide, so the path from prompt-to-production stays inside the guardrails you’ve placed?
Based on my experience, we keep coming back to these 15 guardrails to shepherd users in a safe direction. We don’t want to slow down development, but we do want to make secure defaults the path of least resistance.
- Project templates
Always start with a template.
Templates are where you decide how services are structured. Prompts should fill in business logic, not invent infrastructure, architecture, or security patterns on the fly.
Use opinionated templates for the common service patterns your organization builds. Pick the approved programming language you support. Bake in your code standards. Lock in the frameworks you actually want people using. Pin versions for the components that should not be left to “vibe coding.”
Include example tests that fail when core flows are modified unsafely. Treat these tests as rules, not suggestions. GenAI shouldn’t be allowed to “fix” failures by rewriting the tests to match unsafe behavior.
- Authentication and authorization library
Establish a shared authentication and authorization library that teams must use. If you can’t centralize access enforcement behind a centralized API or access gateway, standardizing the library is the next best approach.
This can avoid the technical debt of each application having a unique authentication and authorization scheme. This approach also makes code review and testing more consistent across a fleet of internal or customer-facing applications.
- Code quality enforcement via domain specific language
Use code-quality templates that bake in the baseline security settings you expect to be present. Generated projects shouldn’t have to “remember” to include authorization checks or defensive middleware.
The template’s behavior in the IDE should make absence obvious and failures loud before the pull request. As an example, if the user chose to ignore the IDE’s warning, the pull request would be rejected due to the absence of the authentication library.
- Secrets management
Make inline keys a non-option. Only allow platform-provided secret stores and helper utilities that retrieve secrets at runtime. This turns “don’t hardcode secrets” from a guideline into an implementation constraint.
- Data access layer
Force database and third-party software-as-a-service (SaaS) access through approved SDKs or repositories that avoid raw queries from user-authored code. This is where you can enforce parameterization, logging, access patterns, and safe defaults without relying on every prompt to do the right thing.
- Outbound network policy
Configure a firewall rule to deny outbound traffic by default. Allowlist domains and ports that are explicitly needed and block the rest. This limits accidental data exfiltration paths and reduces the blast radius when something goes wrong. Consider if those DNS servers really need to recurse to the internet or if a split-horizon is more appropriate.
- Resource and rate limits
Configure timeouts, CPU constraints, memory caps, and request quotas into the runtime and deployment patterns. Add provider-aware rate limiting for internal systems and SaaS APIs to prevent “a prompt produced a loop” from becoming an outage.
- Continuous integration checks
Pre-wire linting, SAST, dependency scanning, and basic tests on every deploy. Generated code should inherit the same gates as organic, artisanal, token-free text crafted by a meat space blob. If a team wants to bypass checks, that decision should be explicit and on an exemption basis that documents the accepted risk.
- Secure HTTP defaults
Make secure HTTP settings the default, not a checklist item.
TLS where it makes sense. Enable security headers, sane timeouts, and centralized web server logging from day one. No one should have to rediscover these settings for every new generated service.
- Error and audit logging
Standardize how services log errors and audit events.
Provide an SDK that standardizes error logging and audit events to automatically correlate with trace IDs and user IDs. This makes incident response feasible in an environment where many small services can be deployed quickly.
- Environment isolation
Keep environments clearly separated.
Automate promotion paths that include gates on branches that invoke purpose-driven generative AI review, such as security or organizational code standards. You shouldn’t have any direct production edits. If GenAI makes it easier to generate code, deployments should become more boring and controlled.
- Input validation
Centralize input validation.
Strongly typed schemas at entry points make it clear what’s allowed and what isn’t. Prompts are great at producing handlers; they’re less reliable at consistently parsing and rejecting unsafe input unless explicitly instructed to do so, which requires knowledge of security.
- Least-privilege roles
Assume one generated app will eventually be compromised.
Give each application the minimum permissions it needs. Assume one generated app will eventually be compromised, and design so that compromise is contained and does not facilitate lateral movement, privilege escalation, or additional unauthorized access.
- Governed package registry
Use a governed package registry with allowlists and approved versions.
This reduces supply-chain risk and removes “random package choice” from the prompt surface area. This concern is ever more relevant given the fragility of package management ecosystems in popular programming languages.
- In-app guardrail docs
- Put guidance where people actually make decisions.
Small, contextual “do it this way” hints in the editor or repository will likely be used more than a separate wiki page. Inline guidance such as comments in templates, agent instructions, or a short architecture doc meets code builders where they are, right when they’re about to make a decision.
Hopefully, you can see the common theme across all of this is straightforward: help non-developers succeed by establishing critical security elements ahead of time. Let the IDE, templates, and application deployment pipeline ensure the presence of these controls. If a control is still missed, the environment itself is hardened in a way where applications are isolated, can only communicate to approved destinations, and have least privilege configured in a way that limits the impact of the security incident.
We can think of these controls as a form of context engineering. For example, by handling authentication in traditional application code, the AI no longer needs to reason about who the user is or what they’re allowed to do, i.e., batteries are included for users in the SDK. That reduces the amount of context the AI has to manage and makes the system easier to secure.
When developers act as shepherds by owning the safeguard configurations, non-developers will be empowered to ship useful software without being set up to fail. GenAI is powerful, but it still must be molded to the desired outcome, which still requires domain specific knowledge. For now, a combination of human-in-the-loop and deterministic controls can help address risk and usability at scale.
If you’re still not confident in your AI-developed code, check out how Bishop Fox can help through secure code reviews, threat modeling, and architecture assessments.
Subscribe to our blog
Be first to learn about latest tools, advisories, and findings.
Thank You! You have been subscribed.