Audit for security risks
AI-generated code often runs correctly, which makes security problems easy to miss. A security flaw usually doesn't cause a crash, so a passing test run doesn't prove the code is safe. Check for these risks on purpose during every review.
Injection attacks
Injection happens when user input changes the meaning of a query or command. AI tools often build queries and commands with string formatting, which makes injection possible.
SQL injection
Prompt: "Write a function that finds a user by name in SQLite."
AI-generated code:
def get_user(cursor, username):
query = f"SELECT * FROM users WHERE name = '{username}'"
cursor.execute(query)
return cursor.fetchone()
If a user enters ' OR '1'='1, the query condition is always true, so the code returns a row even when no name matches. The input has changed the query.
Safer version, which passes the value as a parameter:
def get_user(cursor, username):
cursor.execute("SELECT * FROM users WHERE name = ?", (username,))
return cursor.fetchone()
The database driver treats the value as data, not as part of the SQL. The placeholder syntax depends on the driver. The sqlite3 module uses ?, and psycopg uses %s.
Command injection
AI-generated code:
import os
def ping_host(host):
os.system(f"ping -c 1 {host}")
If host is example.com; cat secrets.txt, the shell runs the second command too.
Safer version, which avoids the shell:
import subprocess
def ping_host(host):
subprocess.run(["ping", "-c", "1", host], check=True)
Passing a list of arguments means the host is always a single argument. The -c flag works on Linux and macOS. Windows uses -n. Validate the format of any host that comes from users.
Hardcoded secrets
AI-generated examples often include API keys or passwords that look real. Developers sometimes keep them in the code.
API_KEY = "sk-example-1234567890abcdef"
Anyone who can read the code can use this key. Read secrets from the environment instead:
import os
API_KEY = os.environ["WEATHER_API_KEY"]
If the variable isn't set, this code raises an error at startup, which is easier to diagnose than a failure later. If a secret has appeared in code that other people can read, revoke it and create a new one.
Unsafe evaluation and deserialization
AI tools sometimes use eval to parse data, which runs any Python expression in the input:
def load_settings(text):
return eval(text)
Safer version, which only reads data:
import json
def load_settings(text):
return json.loads(text)
Similarly, pickle.loads can run code while it loads data. Use JSON for data from outside your program.
Path traversal
AI-generated file code often joins a user-supplied file name directly onto a folder path:
def read_upload(filename):
with open(f"uploads/{filename}") as file:
return file.read()
A file name such as ../../secrets.txt reads a file outside the uploads folder. Safer version, which checks the resolved path:
from pathlib import Path
UPLOAD_DIR = Path("uploads").resolve()
def read_upload(filename):
path = (UPLOAD_DIR / filename).resolve()
if not path.is_relative_to(UPLOAD_DIR):
raise ValueError("File is outside the uploads folder")
return path.read_text()
Path.is_relative_to requires Python 3.9 or later.
Weak randomness for security values
The random module is predictable, so it's unsafe for tokens and passwords:
import random
def create_reset_token():
return "".join(random.choices("0123456789abcdef", k=32))
Use the secrets module instead:
import secrets
def create_reset_token():
return secrets.token_hex(16)
Packages and deprecated APIs
AI tools sometimes suggest packages that don't exist. Attackers can register those names and publish malicious code under them, so installing a suggested package without checking it is a risk. Before you install a package, check that:
- The name is spelled exactly as you expect on PyPI. Attackers also publish packages with names that are one letter off from popular packages.
- The project has a known maintainer, a release history, and working project links.
- Download counts and recent activity support the choice. These are signals, not proof of safety.
After you choose a package, pin its version in requirements.txt, such as requests==2.32.3, so your environment doesn't change unexpectedly.
AI models are also trained on older code, so they often suggest deprecated functions. For example, datetime.utcnow() is deprecated since Python 3.12:
from datetime import datetime
timestamp = datetime.utcnow()
The current replacement is timezone-aware:
from datetime import datetime, timezone
timestamp = datetime.now(timezone.utc)
Test security behavior
The unit tests in the previous unit showed how a test can enforce a requirement. The same approach can check security behavior:
import pytest
def test_read_upload_rejects_path_traversal():
with pytest.raises(ValueError):
read_upload("../secrets.txt")
Protect data in your prompts
Don't paste passwords, API keys, customer records, or confidential code into an AI tool unless your organization has approved that tool for that data. Check your organization's AI policy and the tool's data settings before you share code.
The security review checklist
When you audit AI code for security, ask:
- Does user input reach a query, a shell command, or a file path without validation?
- Are there secrets, such as keys, passwords, or tokens, written directly in the code?
- Does the code use
eval,exec, orpickleon data from outside the program? - Do the imported packages exist, and are they maintained?
- Are tokens and other security values generated with the
secretsmodule?
Security issues often pass normal testing. Review for them deliberately, even when the code appears to work.