Pre-ship checklist
Vibe coding security checklist: what to check before you ship AI-generated code
AI coding tools — Cursor, Copilot, Lovable, Bolt, v0 — can scaffold a working web app in minutes. They can also, without flagging it, hardcode an API key in a utility file, leave .env out of .gitignore, and generate a server config with no security headers. This isn’t a flaw in those tools; it’s their training data: most tutorials and Stack Overflow examples were written for clarity and explanation, not for production deployment. Here is the six-check sequence to run before any AI-assisted project goes live.
- Why AI tools introduce these gaps
- The 5 most common gaps
- The pre-ship checklist
- If you find a leak
- FAQ
Why AI tools introduce these specific security gaps
AI code assistants generate statistically likely code, not maximally secure code. The patterns they reproduce most faithfully are the ones that appear most frequently in their training data. Tutorial code, blog posts, and framework quickstarts — all optimised to get something running fast — dominate that corpus. Those sources routinely inline credentials for brevity, omit .gitignore entries that the reader is expected to add themselves, and skip security headers that would make the example harder to follow.
This means AI-generated code is often excellent at the application layer (routing, data models, UI) and consistently weak at the deployment and configuration layer. The categories below are not edge cases; they appear in the majority of greenfield AI-assisted projects.
The 5 most common security gaps in AI-generated code
-
Hardcoded API keys in source files
AI tools suggest
const OPENAI_KEY = "sk-..."in config objects, utility helpers, and sometimes inline in component files. Bots scrape the GitHub Events API in real time; a public push with an exposed key is collected within 60 seconds. -
Incomplete .gitignore
AI scaffolding usually creates
.env.example(correct) but does not always add.env,.env.local, and.env.productionto.gitignore. A singlegit add .then commits your real secrets file alongside your application code. -
Wildcard CORS on API routes
AI-generated Express and Fastify routes default to
Access-Control-Allow-Origin: *because it eliminates friction during development. Left in place in production, it allows any external site to make credentialed requests to your API from a visitor’s browser. -
Missing security headers
No framework generates a Content-Security-Policy, HSTS, X-Frame-Options, or Referrer-Policy header by default. AI-written server configs —
server.js,next.config.js,netlify.toml— almost never add them unless you ask explicitly. -
Source maps served in production
Vite and webpack default to generating source maps in production builds, or AI-generated configs enable them explicitly. A publicly served
.js.mapfile exposes your full un-minified source code — function names, comments, file structure, and anything accidentally left in the code.
The six-check pre-ship sequence
Work through these in order. The first two are free text checks that take under a minute; the rest are automated scans of your live URL.
-
Scan your source code for exposed secrets
Before your first commit and after any major AI-generated addition, paste your source files into LeakCheck. LeakCheck runs pattern matching for API keys, tokens, connection strings, and other credentials entirely in your browser — nothing you paste is sent to a server. Pay particular attention to config files, utility modules, and any file with “key”, “secret”, or “token” in its name.
-
Verify your .gitignore covers all env files
From your project root, run:
git check-ignore -v .env git check-ignore -v .env.local git check-ignore -v .env.productionEach command should print a line like
.gitignore:5:.env* .env. If it prints nothing, that file is not ignored. The simplest fix is a single line in.gitignore:.env*This covers
.env,.env.local,.env.production,.env.staging, and any other variant. Commit the updated.gitignorebefore committing anything else. -
Scan your live URL for exposed files
After your first deploy, enter your domain in ExposureCheck. It probes for exposed
.envfiles, accessible.gitdirectories, JavaScript source maps, bundled secrets, and other configuration files that can end up publicly served if a build or deploy config is misconfigured. AI-generated Vite and Next.js configs are a frequent source of publicly accessible source maps. -
Check your security headers
Paste your URL into HardenCheck. It checks your live site’s Content-Security-Policy, HSTS, X-Frame-Options, Referrer-Policy, and other security headers in seconds, with plain-English guidance on what each missing header enables an attacker to do and how to add it to your specific stack (Express, Next.js, Netlify, Cloudflare, nginx). AI-generated projects consistently have zero security headers on first deploy.
-
Audit your dependencies
Paste your
package.jsoninto DepCheck. AI tools suggest packages from their training data, which reflects the npm ecosystem as it existed at training time. Some suggested packages have since accumulated open CVEs, been abandoned by their maintainers, or have names one character away from a well-known typosquatting target. DepCheck flags all three categories. -
Check accessibility and privacy compliance
AI-generated UIs produce a recognisable pattern of accessibility errors: heading order violations, missing
altattributes on images, form controls without labels, and colour contrast ratios set for aesthetics rather than WCAG thresholds. Paste your URL into ShipSafe for a plain-English report on the accessibility and privacy gaps that most commonly attract ADA demand letters.
If you find a secret that was already committed
The order of operations matters: rotate the key first, then clean up the history. Removing a file from the working tree or rewriting git history does nothing for a key that is already in someone else’s hands. Rotate the credential at the issuing service (OpenAI, Stripe, AWS, Supabase, etc.) to invalidate the old value immediately, then follow the steps to remove it from source and rewrite git history so the old commit no longer contains the value.
Detailed walkthroughs:
- Accidentally committed an API key to GitHub — what to do right now
- How to remove a secret from git history with git-filter-repo or BFG
Frequently asked questions
Do AI coding tools know about security best practices?
They do, in the sense that they can give accurate security advice when asked. The issue is default behaviour: AI tools reproduce the most statistically common pattern for a given context, and the most common pattern in tutorials and example code is optimised for getting something running, not for production deployment. Asking your AI tool “does this code have any security issues” or “add production-ready security headers to this server config” often produces good results. The problem is that it does not volunteer those additions unprompted.
Are source maps actually a security risk in production?
Yes. A .js.map file contains your full un-minified source code — function names, variable names, comments, and file structure — reconstructed from the bundled output. If served publicly, anyone can view it in browser devtools (Sources tab) or fetch the URL directly. This exposes proprietary business logic, API endpoint patterns, and anything accidentally left in comments. Disable source maps in production builds: set build.sourcemap: false in Vite, devtool: false in webpack for production, or productionBrowserSourceMaps: false in Next.js.
My AI tool created a .env.example file — doesn't that mean .env is handled?
.env.example is a committed template with placeholder values — correct. But the existence of .env.example does not mean .env is in .gitignore. These are independent files. Verify with git check-ignore -v .env; if it prints nothing, your real secrets file is unprotected. Add .env* to .gitignore before your first git add ..
Can I ask my AI tool to add security headers for me?
Yes, and this works well. A prompt like “add Content-Security-Policy, HSTS, X-Frame-Options, and Referrer-Policy headers to my Express server config” or “update my next.config.js to add security headers” produces accurate output for most stacks. The catch is that you need to then verify the headers are actually present on the live site — misconfigured headers are common, and some middleware applies them only to certain routes or response types. HardenCheck scans the live response headers and tells you which ones are present, misconfigured, or missing.
What is the most common way AI-assisted projects get compromised?
Hardcoded credentials reaching a public git repository. AI tools suggest inline key values in config objects and test helpers; the developer commits the file without scanning it first; automated bots collect the key within minutes. The second most common path is source maps in production: a Vite or webpack config left at defaults exposes the full reconstructed source, revealing API endpoint patterns, authentication logic, and any secrets in comments. Both are caught in under two minutes with LeakCheck and ExposureCheck.
Do I need to rerun these checks on every deploy?
Run the full checklist at launch. After that: rerun LeakCheck and the .gitignore check whenever you add new environment variables or AI-generate a config change. Rerun ExposureCheck and HardenCheck when you change your build or deployment configuration, switch hosting providers, or add a new subdomain. Rerun DepCheck monthly or when you add new dependencies — CVE disclosures and package abandonment happen after packages are added to a project, not just at install time.
The full Copper Bay Labs ship-safety suite
Run all six tools before you flip a new project live. Each covers a distinct failure mode; none overlaps with the others.
Paste code or a .env file and see every exposed API key and secret flagged by severity. Runs entirely in your browser.
Scan your live URL for exposed .env files, .git directories, source maps, and bundled secrets.
Check your live site’s security headers: CSP, HSTS, X-Frame-Options, Referrer-Policy, and more.
Dependencies DepCheckPaste your package.json to find packages with CVEs, abandoned maintainers, or typosquatting risk.
Plain-English ADA/WCAG accessibility and privacy risk report for any live URL. Flags the issues that attract demand letters.