Security Detection in AI-Generated Code
AI-generated code presents security vulnerabilities at a rate 2.5 times higher than manually written code. This is not a marginal problem: SQL injection, hardcoded secrets, weak authentication and insecure configurations are recurring patterns in AI assistant output. The main cause is that language models optimize for functionality, not security.
In this article we will analyze the most common vulnerabilities in AI-generated code according to the OWASP Top 10 and CWE classifications, the error patterns specific to AI assistants and how to implement automated scanning to intercept these issues before production.
What You Will Learn
- The most frequent OWASP vulnerabilities in AI-generated code
- Insecure defaults patterns typical of AI output
- How to detect hardcoded secrets and credentials in generated code
- SAST (Static Application Security Testing) techniques for AI code
- CWE classification of AI-specific vulnerabilities
- Implementing automated security scanning in the pipeline
OWASP Top 10 in the AI Code Context
The OWASP Top 10 classification represents the most critical and widespread web vulnerabilities. In the context of AI-generated code, some of these vulnerabilities emerge with significantly higher frequency than average, particularly injection, broken authentication and security misconfiguration.
A03:2021 - Injection
Injection is the most common vulnerability in AI-generated code. Language models frequently generate SQL queries by concatenating strings, shell commands without escaping and HTML templates without sanitization. This happens because training data contains millions of examples with these insecure patterns.
# VULNERABLE: Typical AI-generated SQL injection
def get_user(username):
query = f"SELECT * FROM users WHERE username = '{username}'"
return db.execute(query)
# VULNERABLE: Command injection
def convert_file(filename):
import os
os.system(f"convert {filename} output.pdf")
# SECURE: Parameterized queries
def get_user_safe(username):
query = "SELECT * FROM users WHERE username = %s"
return db.execute(query, (username,))
# SECURE: Subprocess with argument list
def convert_file_safe(filename):
import subprocess
import shlex
# Filename validation
if not filename.isalnum() and '.' not in filename:
raise ValueError("Invalid filename")
subprocess.run(["convert", filename, "output.pdf"],
check=True, capture_output=True)
A07:2021 - Identification and Authentication Failures
AI frequently generates authentication mechanisms with structural flaws: plaintext passwords, sessions without expiration, predictable tokens and lack of rate limiting. These patterns stem from the simplification AI applies to satisfy the prompt in the most direct way possible.
A05:2021 - Security Misconfiguration
Insecure configurations are a pervasive problem in AI code: permissive CORS, debug mode active in production, missing security headers and disabled TLS. AI tends to generate configurations functional for local development that are not suitable for production.
OWASP Vulnerability Frequency in AI vs Human Code
| OWASP Vulnerability | Human Code Frequency | AI Code Frequency | Related CWEs |
|---|---|---|---|
| A03 - Injection | 12% | 34% | CWE-89, CWE-78, CWE-79 |
| A07 - Auth Failures | 8% | 22% | CWE-287, CWE-798 |
| A05 - Misconfiguration | 15% | 41% | CWE-16, CWE-611 |
| A02 - Crypto Failures | 6% | 18% | CWE-327, CWE-328 |
| A01 - Broken Access Control | 10% | 15% | CWE-284, CWE-862 |
Insecure Defaults Patterns in AI
One of the most insidious problems with AI code is the generation of insecure defaults: configurations that work correctly but are not secure. AI chooses the simplest and most functional options, which often coincide with the least secure ones.
# Typical AI insecure defaults patterns
# 1. Permissive CORS
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # AI default: allows EVERYTHING. No origin restriction
# SECURE:
CORS(app, origins=["https://myapp.com"], methods=["GET", "POST"])
# 2. JWT without expiration
import jwt
token = jwt.encode({"user_id": 123}, SECRET_KEY) # No expiration!
# SECURE:
from datetime import datetime, timedelta
token = jwt.encode(
{"user_id": 123, "exp": datetime.utcnow() + timedelta(hours=1)},
SECRET_KEY,
algorithm="HS256"
)
# 3. Weak hashing
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest() # MD5!
# SECURE:
import bcrypt
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# 4. Debug mode in production
app.run(debug=True, host="0.0.0.0") # Exposes debugger and stacktrace
# SECURE:
import os
app.run(debug=os.getenv("FLASK_DEBUG", "false").lower() == "true",
host="127.0.0.1")
Hardcoded Secrets and Credentials
AI frequently generates code with hardcoded credentials: API keys, database passwords, authentication tokens and encryption secrets directly in source code. This is one of the most serious security problems because credentials end up in version control and can be extracted from public or compromised repositories.
# Scanner for hardcoded secrets in AI code
import re
from pathlib import Path
class SecretScanner:
"""Detects hardcoded credentials in source code"""
PATTERNS = [
(r'(?i)(api[_-]?key|apikey)\s*[=:]\s*["\']([A-Za-z0-9_\-]{20,})["\']',
"API Key", "HIGH"),
(r'(?i)(password|passwd|pwd)\s*[=:]\s*["\']([^"\']+)["\']',
"Password", "CRITICAL"),
(r'(?i)(secret|token)\s*[=:]\s*["\']([A-Za-z0-9_\-]{16,})["\']',
"Secret/Token", "CRITICAL"),
(r'(?i)(aws_access_key_id)\s*[=:]\s*["\']?(AKIA[A-Z0-9]{16})',
"AWS Access Key", "CRITICAL"),
(r'(?i)(mongodb(\+srv)?://[^"\'\s]+)',
"Database Connection String", "HIGH"),
(r'-----BEGIN (RSA |EC )?PRIVATE KEY-----',
"Private Key", "CRITICAL"),
(r'(?i)bearer\s+[A-Za-z0-9\-._~+/]+=*',
"Bearer Token", "HIGH"),
]
def scan_file(self, filepath):
"""Scans a file for hardcoded credentials"""
findings = []
content = Path(filepath).read_text()
for pattern, name, severity in self.PATTERNS:
matches = re.finditer(pattern, content)
for match in matches:
line_num = content[:match.start()].count('\n') + 1
findings.append({
"file": filepath,
"line": line_num,
"type": name,
"severity": severity,
"match": match.group(0)[:50] + "..."
})
return findings
def scan_directory(self, directory, exclude_patterns=None):
"""Recursively scans a directory"""
all_findings = []
exclude = exclude_patterns or [".git", "node_modules", "__pycache__"]
for path in Path(directory).rglob("*"):
if any(excl in str(path) for excl in exclude):
continue
if path.is_file() and path.suffix in [".py", ".js", ".ts", ".java", ".go"]:
all_findings.extend(self.scan_file(str(path)))
return sorted(all_findings, key=lambda x: x["severity"] == "CRITICAL",
reverse=True)
CWE Classification for AI-Specific Vulnerabilities
The Common Weakness Enumeration (CWE) system provides a standardized catalog of software weaknesses. For AI-generated code, certain CWEs are particularly relevant and recurring. Understanding them helps configure security scanners in a targeted manner.
Critical CWEs in AI-Generated Code
| CWE ID | Name | AI Frequency | Impact |
|---|---|---|---|
| CWE-89 | SQL Injection | Very high | Data breach, data manipulation |
| CWE-798 | Hardcoded Credentials | High | Unauthorized access |
| CWE-79 | Cross-Site Scripting | High | Session hijacking, phishing |
| CWE-327 | Broken Crypto Algorithm | Medium | Data exposure |
| CWE-862 | Missing Authorization | Medium | Privilege escalation |
| CWE-400 | Uncontrolled Resource | Medium | Denial of Service |
SAST for AI Code: Tools and Configuration
Static Application Security Testing (SAST) is the first line of defense against vulnerabilities in AI-generated code. SAST tools analyze source code without executing it, identifying insecure code patterns, vulnerable configurations and security best practice violations.
# Semgrep configuration for AI-generated code
# .semgrep.yml
rules:
- id: ai-sql-injection
patterns:
- pattern: |
$QUERY = f"... {$VAR} ..."
$DB.execute($QUERY)
message: "Possible SQL injection: use parameterized queries"
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-89"
confidence: HIGH
ai-specific: true
- id: ai-hardcoded-secret
patterns:
- pattern: |
$KEY = "..."
- metavariable-regex:
metavariable: $KEY
regex: ".*(secret|key|token|password|api_key).*"
message: "Hardcoded credential: use environment variables"
languages: [python]
severity: ERROR
metadata:
cwe: "CWE-798"
- id: ai-insecure-hash
patterns:
- pattern: hashlib.md5(...)
- pattern: hashlib.sha1(...)
message: "Weak hashing algorithm: use SHA-256 or bcrypt"
languages: [python]
severity: WARNING
metadata:
cwe: "CWE-327"
Security Scanning Pipeline
An effective security scanning pipeline for AI code must include multiple analysis layers: secret detection (pre-commit), SAST (CI), dependency scanning (CI) and DAST (staging). Each layer intercepts different categories of vulnerabilities, creating defense in depth.
Recommended Security Scanning Layers
- Pre-commit: Secret detection with git-secrets or detect-secrets to block credentials before commit
- Pull Request: SAST with Semgrep, CodeQL or Snyk Code for source code analysis
- CI Pipeline: Dependency scanning with Dependabot, Snyk or OWASP Dependency-Check
- Staging: DAST with OWASP ZAP or Burp Suite for running application testing
- Production: Runtime protection with RASP and continuous vulnerability monitoring
AI-Specific Security Anti-Patterns
Beyond classic vulnerabilities, AI code presents specific security anti-patterns that derive from the nature of the generation process. These patterns are difficult to identify with traditional scanners and require custom rules.
- Over-permissive error messages: AI generates error messages that expose internal system details
- Sensitive data logging: personal information, tokens and credentials end up in logs
- Trust boundary violations: AI does not distinguish between trusted and untrusted input
- Race conditions: AI generates concurrent code without proper synchronization
- Insecure deserialization: use of pickle, eval or JSON.parse without validation
Automated Remediation
Vulnerabilities found in AI code can often be corrected automatically. Tools like Semgrep, CodeQL and Snyk support autofix that transforms insecure patterns into secure versions. This approach reduces the burden on developers and accelerates the fix cycle.
Automated remediation is particularly effective for the most common and well-defined vulnerabilities such as SQL injection, hardcoded secrets and weak cryptographic algorithms, where the secure transformation is deterministic and well-documented.
Conclusions
The security of AI-generated code cannot be left to developer goodwill. Statistics show that vulnerabilities are significantly more frequent and error patterns are predictable. This makes it possible to build effective defenses through automated scanning, custom rules and integrated security pipelines.
In the next article we will explore test intelligence: how to generate smart tests for AI code, using mutation testing, property-based testing and fuzzing to discover defects that traditional tests do not catch.
Security is not an option, it is a requirement. And for AI-generated code, this requirement must be automated and verified at every commit.







