Language-Specific Risks in AI-Generated Code
Language-specific security risks in AI-generated code: Python (pickle, eval, subprocess), JavaScript (prototype pollution, eval), Rust (unsafe blocks), and Go (SQL injection in string formatting).
While the CWE-mapped vulnerability patterns from the previous page apply across languages, each programming language has unique features, idioms, and ecosystem characteristics that create language-specific risks in AI-generated code. This page covers the most significant risks in Python, JavaScript, Rust, and Go.
Python
Python's flexibility and extensive standard library create multiple avenues for AI-generated vulnerabilities. The language's "batteries included" philosophy means there are often both secure and insecure ways to accomplish the same task, and models frequently choose the insecure option.
Pickle Deserialization
pickle is Python's native serialization format, and it is fundamentally insecure: deserializing a pickle object executes arbitrary code. AI models frequently suggest pickle for serialization tasks because it is the most common serialization mechanism in Python training data.
# AI-generated: arbitrary code execution via pickle
import pickle
def load_user_preferences(data):
return pickle.loads(data) # Executes arbitrary code in the data
# AI-generated: insecure file loading
def load_model(path):
with open(path, 'rb') as f:
return pickle.load(f) # Dangerous if path is user-controlled
# Secure alternatives
import json
def load_user_preferences(data):
return json.loads(data) # Cannot execute code
# For ML models, use safetensors or verify signatures
from safetensors import safe_open
def load_model(path):
return safe_open(path, framework="pt")The pickle risk is amplified in machine learning contexts where models frequently suggest pickle or torch.load() (which uses pickle internally) for loading model weights. An attacker who can supply a malicious model file achieves code execution.
eval() and exec()
AI models frequently suggest eval() for tasks that involve dynamic computation, string-to-number conversion, or configuration parsing:
# AI-generated: code execution via eval
def calculate(expression):
return eval(expression) # Arbitrary code execution
def parse_config_value(value_str):
return eval(value_str) # Intended for "True"/"False", executes anything
# Secure alternatives
import ast
def calculate(expression):
return ast.literal_eval(expression) # Only evaluates literals
def parse_config_value(value_str):
return {'true': True, 'false': False}.get(value_str.lower())subprocess with shell=True
AI models frequently use shell=True in subprocess calls or use os.system() because these patterns are more common in training data:
# AI-generated: command injection via shell=True
def ping_host(hostname):
result = subprocess.run(
f"ping -c 4 {hostname}",
shell=True, # Enables command injection
capture_output=True,
text=True
)
return result.stdout
# Secure alternative
def ping_host(hostname):
result = subprocess.run(
["ping", "-c", "4", hostname], # No shell interpretation
capture_output=True,
text=True
)
return result.stdoutYAML Unsafe Loading
# AI-generated: arbitrary code execution via YAML
import yaml
def load_config(path):
with open(path) as f:
return yaml.load(f) # Unsafe loader, can execute code
# Secure alternative
def load_config(path):
with open(path) as f:
return yaml.safe_load(f) # Only loads basic typesJavaScript / TypeScript
JavaScript's prototype-based inheritance, dynamic typing, and browser execution context create unique vulnerability patterns in AI-generated code.
Prototype Pollution
AI models generate code that is vulnerable to prototype pollution — modifying Object.prototype through user-controlled input. This is especially common in object merging and configuration handling:
// AI-generated: prototype pollution via recursive merge
function deepMerge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// Attacker sends: {"__proto__": {"isAdmin": true}}
// All objects now have isAdmin === true
// Secure alternative
function deepMerge(target, source) {
for (const key of Object.keys(source)) { // Own properties only
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue; // Skip dangerous keys
}
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = Object.create(null);
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}eval() and Function() Constructor
// AI-generated: code execution via eval
function processTemplate(template, data) {
return eval('`' + template + '`'); // Template with code execution
}
// AI-generated: indirect eval via Function constructor
function createValidator(rule) {
return new Function('value', `return ${rule}`);
}
// Secure alternatives depend on the use case:
// - Template literals: use a templating library (Handlebars, EJS with escaping)
// - Validation: use a schema validation library (Joi, Zod)Regular Expression Denial of Service (ReDoS)
AI models frequently generate regular expressions that are vulnerable to catastrophic backtracking:
// AI-generated: ReDoS-vulnerable email validation
const emailRegex = /^([a-zA-Z0-9]+\.)*[a-zA-Z0-9]+@([a-zA-Z0-9]+\.)*[a-zA-Z]{2,}$/;
// Catastrophic backtracking on: "aaaaaaaaaaaaaaaaaa@" + "a".repeat(100)
// Secure alternative: use a dedicated validation library
const { z } = require('zod');
const emailSchema = z.string().email();Insecure Dependency Usage
AI models often suggest npm install of packages without version pinning or security consideration, and may suggest deprecated or unmaintained packages that have known vulnerabilities.
Rust
Rust's memory safety guarantees are a significant security advantage, but AI models can undermine these guarantees by suggesting unsafe blocks, and Rust's complexity means models sometimes generate code that compiles but has logical safety issues.
Unnecessary unsafe Blocks
AI models trained on systems programming code may suggest unsafe blocks for operations that can be accomplished safely:
// AI-generated: unnecessary unsafe for string conversion
fn to_string(bytes: &[u8]) -> String {
unsafe { String::from_utf8_unchecked(bytes.to_vec()) }
// Undefined behavior if bytes is not valid UTF-8
}
// Secure alternative
fn to_string(bytes: &[u8]) -> Result<String, std::string::FromUtf8Error> {
String::from_utf8(bytes.to_vec())
}// AI-generated: unsafe pointer arithmetic
fn get_element(data: &[u32], index: usize) -> u32 {
unsafe { *data.as_ptr().add(index) }
// No bounds checking — buffer overflow
}
// Secure alternative
fn get_element(data: &[u32], index: usize) -> Option<u32> {
data.get(index).copied()
}Raw Pointer Misuse
Models may suggest raw pointer operations for performance optimization that are unnecessary and dangerous:
// AI-generated: use-after-free potential
fn process_data(data: Vec<u8>) -> *const u8 {
let ptr = data.as_ptr();
// data is dropped here, ptr is dangling
ptr
}FFI Boundary Issues
When generating code that interacts with C libraries through FFI, models may not properly handle the safety boundaries:
// AI-generated: missing null check on FFI return
extern "C" {
fn get_user_name() -> *const c_char;
}
fn user_name() -> String {
unsafe {
CStr::from_ptr(get_user_name()) // Crashes if null is returned
.to_string_lossy()
.into_owned()
}
}Go
Go's simplicity and explicit error handling create specific patterns where AI-generated code introduces vulnerabilities.
SQL Injection via String Formatting
Go's fmt.Sprintf is frequently used by AI models to construct SQL queries:
// AI-generated: SQL injection
func GetUser(db *sql.DB, username string) (*User, error) {
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)
row := db.QueryRow(query)
// ...
}
// Secure alternative
func GetUser(db *sql.DB, username string) (*User, error) {
row := db.QueryRow("SELECT * FROM users WHERE username = $1", username)
// ...
}Ignored Error Returns
Go's explicit error handling requires checking every error return. AI models frequently generate code that silently discards errors:
// AI-generated: ignored errors
func writeConfig(path string, data []byte) {
f, _ := os.Create(path) // Error ignored
f.Write(data) // Error ignored, f may be nil
f.Close() // Error ignored
}
// Secure alternative
func writeConfig(path string, data []byte) error {
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("creating config: %w", err)
}
defer f.Close()
if _, err := f.Write(data); err != nil {
return fmt.Errorf("writing config: %w", err)
}
return nil
}HTTP Server Misconfigurations
AI models often generate Go HTTP servers without proper timeout configuration, leading to denial-of-service vulnerabilities:
// AI-generated: no timeouts, vulnerable to slowloris
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil) // No timeouts configured
}
// Secure alternative
func main() {
srv := &http.Server{
Addr: ":8080",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
srv.ListenAndServe()
}Cross-Language Review Checklist
When reviewing AI-generated code in any language, check for:
| Check | Python | JavaScript | Rust | Go |
|---|---|---|---|---|
| Deserialization | pickle, yaml.load | JSON.parse (usually safe) | serde (usually safe) | encoding/gob |
| Code execution | eval, exec | eval, Function | unsafe blocks | exec.Command |
| Input validation | Missing sanitization | Prototype pollution | Type system helps | Missing validation |
| Error handling | Bare except | Missing catch | Unwrap panics | Ignored errors |
| Crypto | hashlib.md5 | crypto (subtle API) | External crates | crypto/md5 |
Related Topics
- CWE Mapping — Cross-language vulnerability mappings
- AI-Generated Vulnerability Patterns — Overview of why models produce vulnerable code
- Code Suggestion Poisoning — How attackers amplify language-specific patterns