How Do You Run Security Audits on AI-Generated Code?
Problem
When I built my first app using AI coding tools, everything worked perfectly. Claude Code generated the backend, the frontend looked great, and all tests passed. I was ready to ship to production. Then I asked myself a scary question: “Is this code actually secure?”
I had no idea. The AI never warned me about security issues. It just built features fast. I realized I was about to ship code I couldn’t verify was safe.
The problem is that AI coding assistants like Claude Code, GitHub Copilot, and Cursor generate functional code at remarkable speed, but they prioritize functionality over security. The code works, but may contain vulnerabilities, hardcoded credentials, insecure defaults, or missing input validation.
What I Tried First (The Naive Approach)
I assumed the AI would catch security issues automatically. I was wrong. Here’s what happened when I reviewed my AI-generated code:
// AI-generated code that WORKS but has security issuesapp.post('/api/users', (req, res) => { const { username, email } = req.body;
// No input validation! // No SQL injection prevention! db.query(`INSERT INTO users (username, email) VALUES ('${username}', '${email}')`);
res.json({ success: true });});This code runs fine. But it has multiple critical vulnerabilities:
- SQL injection via unsanitized input
- No input validation
- No error handling
- No rate limiting
The AI gave me working code, not secure code. I needed a better approach.
Understanding Why AI Misses Security
I realized the core issue: AI models are trained to generate code that works, not code that’s secure. A Reddit comment summed it up perfectly: “The models will help you build fast. They won’t remind you to secure what you built. That part’s still on you.”
This happens because:
- Training data focuses on functionality: Most code examples show how to make things work, not how to secure them
- Security context is implicit: Developers often skip security in tutorials for clarity
- AI doesn’t know your threat model: It doesn’t know what data is sensitive or what attack vectors matter
- Speed vs. security trade-off: AI optimizes for getting things working quickly
Layer 1: Direct AI Review
The simplest approach I found was to ask the AI directly. One Reddit user mentioned: “Ask the AI directly: ‘review this code for security risks, potential hacks, and bugs.’ Just that one prompt catches a lot.”
I tried this approach:
Prompt I sent to Claude:
Review the following code for:1. SQL injection vulnerabilities2. XSS risks3. Hardcoded credentials4. Insecure defaults5. Missing input validation6. External services that need production hardening
Code:[paste your code here]
Provide a severity rating (CRITICAL/HIGH/MEDIUM/LOW) for each issue.The AI responded with specific issues:
Security Review Results:
CRITICAL: SQL Injection Vulnerability- Location: Line 5, db.query()- Issue: User input directly interpolated into SQL query- Fix: Use parameterized queries
HIGH: Missing Input Validation- Location: req.body destructuring- Issue: No validation of username or email format- Fix: Add validation middleware
MEDIUM: No Rate Limiting- Issue: Endpoint vulnerable to brute force attacks- Fix: Add rate limiting middlewareThis catches obvious issues quickly. But it’s not enough for production code.
Layer 2: Automated Tooling
I needed deeper automated analysis. Here are the tools I integrated:
Dependabot for Dependency Vulnerabilities
First, I enabled Dependabot to track CVEs in my dependencies:
version: 2updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "daily" open-pull-requests-limit: 10This automatically creates pull requests when vulnerabilities are found in my dependencies.
npm audit for Dependency Scanning
I added a pre-commit hook to run npm audit:
{ "scripts": { "audit": "npm audit --audit-level=moderate", "audit:fix": "npm audit fix" }}Running this shows:
$ npm auditfound 3 vulnerabilities (1 low, 1 moderate, 1 high)- high: Prototype Pollution in lodash <4.17.21- moderate: ReDoS in nodemailer- low: Regular Expression Denial of ServiceCodeRabbit for AI-Powered Code Review
CodeRabbit provides automated code review with security focus. I configured it:
reviews: profile: "assertive" request_changes: true high_level_summary: true security: enabled: trueWhen I opened a pull request, CodeRabbit automatically reviewed the code and flagged security issues.
Layer 3: Manual Security Checklist
No automated tool replaces human judgment. I created a checklist for production-critical code:
## Pre-Deployment Security Checklist
### Authentication & Authorization- [ ] All endpoints require authentication (except public ones)- [ ] User roles/permissions are checked before operations- [ ] Session tokens have reasonable expiration
### Input Validation- [ ] All user inputs are validated and sanitized- [ ] File uploads have type and size limits- [ ] API inputs have schema validation
### Data Protection- [ ] No hardcoded secrets or API keys- [ ] Sensitive data is encrypted at rest- [ ] Sensitive data is encrypted in transit (HTTPS)
### Injection Prevention- [ ] SQL queries use parameterized statements- [ ] No eval() or similar dangerous functions- [ ] HTML output is properly escaped
### External Services- [ ] All external API calls have error handling- [ ] External service credentials are in environment variables- [ ] External service responses are validated
### Logging & Monitoring- [ ] Sensitive data is not logged- [ ] Failed authentication attempts are logged- [ ] Critical operations have audit logsPutting It All Together
Here’s my complete security audit workflow:
# Step 1: Run dependency auditnpm run audit
# Step 2: Run linting with security rulesnpm run lint -- --rulesdir ./security-rules
# Step 3: Check for hardcoded secretsgit diff --staged | grep -E "(api[_-]?key|secret|token|password)"
# Step 4: Ask AI to review code# Send prompt with code and security checklist
# Step 5: Review CodeRabbit feedback on PR# Address all CRITICAL and HIGH issues
# Step 6: Run automated security testsnpm run test:securityReal Example: Fixing AI-Generated Code
Here’s how I fixed the vulnerable API endpoint from earlier:
// FIXED: Secure version with proper validationimport { body, validationResult } from 'express-validator';import rateLimit from 'express-rate-limit';
// Rate limiting middlewareconst limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100 // limit each IP to 100 requests per windowMs});
// Input validation middlewareconst validateUser = [ body('username').trim().escape().isLength({ min: 3, max: 30 }), body('email').isEmail().normalizeEmail()];
app.post('/api/users', limiter, validateUser, async (req, res) => { // Check validation results const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); }
const { username, email } = req.body;
try { // Parameterized query prevents SQL injection const result = await db.query( 'INSERT INTO users (username, email) VALUES ($1, $2) RETURNING id', [username, email] );
res.status(201).json({ success: true, id: result.rows[0].id }); } catch (error) { console.error('Database error:', error); res.status(500).json({ error: 'Internal server error' }); }});The fixes include:
- Rate limiting to prevent brute force
- Input validation and sanitization
- Parameterized queries to prevent SQL injection
- Proper error handling
- Status codes for different scenarios
Why This Multi-Layer Approach Works
Each layer catches different issues:
Layer 1: Direct AI Prompt- Catches: Obvious vulnerabilities, missing validation- Misses: Complex dependencies, CVEs, context-specific risks- Time: Minutes
Layer 2: Automated Tools (Dependabot, CodeRabbit)- Catches: Known CVEs, common patterns, dependency issues- Misses: Business logic flaws, novel attack vectors- Time: Hours (integrated into CI/CD)
Layer 3: Human Review- Catches: Business logic issues, context-specific risks- Misses: Nothing (with enough time)- Time: Hours to daysNo single layer is sufficient. The combination provides comprehensive coverage.
Common Mistakes to Avoid
I learned these lessons the hard way:
Mistake 1: Trusting AI Code Without Review
# NEVER do thisgit push # Ship AI-generated code directly
# ALWAYS do thisnpm run audit && npm run lint && npm run test# Then review CodeRabbit feedback# Then ask AI for security review# THEN pushMistake 2: Not Enabling Dependabot
I assumed my dependencies were safe. Then a critical CVE was announced for a package I used. Dependabot would have notified me immediately.
# Add this to EVERY repository.github/dependabot.ymlMistake 3: Skipping Security for “Small” Changes
A small change can introduce a big vulnerability. I once added a simple debug endpoint that exposed all user data:
// NEVER add endpoints like thisapp.get('/debug/users', (req, res) => { res.json(allUsers); // Exposes all user data!});Tracking External Service Requirements
One Reddit user suggested a clever approach: “I try to get it to call out when an external service is required to get this production ready, and to call it out and backlog it.”
I added this to my AI prompt:
After reviewing the code, list:1. External services required (databases, APIs, etc.)2. Configuration needed for production3. Security considerations for each service
Format as a checklist that I can add to my backlog.This creates a production readiness checklist:
## Production Requirements
- [ ] Configure PostgreSQL connection pool settings- [ ] Set up Redis for session storage- [ ] Add AWS S3 credentials for file uploads- [ ] Configure Stripe webhook secret- [ ] Set up error monitoring (Sentry)- [ ] Add logging infrastructureSummary
In this post, I showed how to run security audits on AI-generated code using a multi-layered approach. The key points are:
- Ask AI directly for security review: A simple prompt catches obvious issues
- Enable Dependabot: Automates CVE detection for dependencies
- Use CodeRabbit or similar tools: Provides deeper automated analysis
- Create a security checklist: Ensures human oversight for production code
- Track external services: Maintain a backlog of production hardening tasks
The most important lesson: AI accelerates development, but security responsibility remains yours. The models will help you build fast, but they won’t remind you to secure what you built. That part’s still on you.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Reddit: AI code security discussion
- 👨💻 CodeRabbit Documentation
- 👨💻 GitHub Dependabot Documentation
- 👨💻 OWASP Code Review Guide
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments