AI Security

GitHub Copilot and AI Code Security: Vulnerable Code and Secret Leakage

How GitHub Copilot and similar AI coding tools generate vulnerable code patterns, leak secrets, and propagate insecure practices — and how teams can use AI coding tools safely.

October 6, 20259 min readShipSafer Team

GitHub Copilot, Amazon CodeWhisperer, Cursor, and similar AI coding assistants are now deeply embedded in development workflows. They increase developer velocity measurably — and they introduce a category of security risk that most teams have not adequately addressed.

The core problem is not that AI coding tools are malicious. It is that they are trained primarily to produce code that works, not code that is secure. They reflect the security weaknesses in the codebases they learned from, which means they reproduce the most common patterns in open-source code — including the most common security mistakes.

How AI Coding Tools Generate Vulnerable Code

Training Data Reflects Real-World Vulnerabilities

GitHub Copilot was trained on public GitHub repositories. Researchers at New York University (Pearce et al., 2022) found that approximately 40% of Copilot-generated code samples contained security vulnerabilities, with SQL injection, path traversal, hardcoded credentials, and improper input validation being the most common issues.

This isn't surprising: a substantial fraction of open-source code contains these vulnerabilities. Copilot learned from that code. It reproduces patterns it has seen frequently, and vulnerable patterns are extremely common.

Context-Driven Vulnerability Reproduction

Copilot generates completions based on surrounding context. When you write variable names like user_input, password, query, or filename, you activate training patterns where those variables were used in potentially unsafe ways.

Example: SQL injection via string formatting

# Developer writes this:
def get_user_by_name(name):
    # Copilot may suggest completing with:
    query = f"SELECT * FROM users WHERE name = '{name}'"
    return db.execute(query)

This pattern — f-string SQL construction — appears thousands of times in training data. Copilot suggests it because it's statistically common, not because it's secure.

Secure completion Copilot should suggest:

def get_user_by_name(name: str):
    query = "SELECT * FROM users WHERE name = %s"
    return db.execute(query, (name,))

The parameterized form is also common in training data, but the specific context (variable naming, surrounding code style) influences which pattern Copilot proposes.

Common Vulnerability Categories in AI-Generated Code

1. Insecure deserialization

# Copilot suggestion when given context about loading user data from cache
import pickle

def load_user_data(data: bytes):
    return pickle.loads(data)  # VULNERABLE: Remote code execution if data is attacker-controlled
# Secure alternative
import json

def load_user_data(data: bytes):
    return json.loads(data.decode('utf-8'))

2. Path traversal

# Copilot completion when building a file server endpoint
@app.route('/files/<filename>')
def serve_file(filename):
    return open(f'/uploads/{filename}').read()  # VULNERABLE: ../../etc/passwd

# Secure version
from pathlib import Path

UPLOAD_DIR = Path('/uploads').resolve()

@app.route('/files/<filename>')
def serve_file(filename: str):
    target = (UPLOAD_DIR / filename).resolve()
    if not str(target).startswith(str(UPLOAD_DIR)):
        abort(403)
    if not target.exists():
        abort(404)
    return target.read_bytes()

3. Weak cryptography

# Copilot may suggest MD5 or SHA1 for password hashing in older-pattern context
import hashlib

def hash_password(password: str) -> str:
    return hashlib.md5(password.encode()).hexdigest()  # VULNERABLE: MD5 is broken for passwords

# Secure version
import bcrypt

def hash_password(password: str) -> bytes:
    return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=12))

4. Insecure random number generation

# Copilot may use random module where secrets module is needed
import random
import string

def generate_token() -> str:
    return ''.join(random.choices(string.ascii_letters + string.digits, k=32))  # NOT cryptographically secure

# Secure version
import secrets
import string

def generate_token() -> str:
    return secrets.token_urlsafe(32)

5. Missing authentication on sensitive endpoints

Copilot may complete endpoint handlers without adding authentication decorators, particularly if the surrounding code doesn't consistently use them:

# Copilot may complete without auth decorator
@app.route('/admin/users')
def list_all_users():
    return jsonify(User.query.all())

# Requires explicit addition of auth
@app.route('/admin/users')
@require_admin
def list_all_users():
    return jsonify(User.query.all())

Secret Leakage Patterns

Copilot Suggesting Hardcoded Secrets

When a developer writes code that references configuration variables inline, Copilot may suggest completing with actual-looking credential strings:

# Developer types: aws_client = boto3.client('s3', aws_access_key_id=
# Copilot may suggest a plausible-looking fake key that the developer might
# replace with their actual key and accidentally commit

aws_client = boto3.client(
    's3',
    aws_access_key_id='AKIAIOSFODNN7EXAMPLE',
    aws_secret_access_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
)

Even if Copilot suggests placeholder strings, developers under time pressure sometimes commit these with actual keys substituted — and at that point, real credentials are in version control.

Training Data Secret Leakage

There is well-documented evidence of Copilot memorizing and reproducing actual secrets from training data. Researchers (Carlini et al.) have shown that large language models memorize and can reproduce training data, including sensitive content.

In practice, this means Copilot may suggest code containing:

  • API key patterns that appear in its training data
  • Database connection strings
  • Private endpoint URLs from internal projects that were accidentally made public

Prevention: Pre-commit Secret Scanning

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']

Configure detect-secrets with patterns covering your tech stack:

# .secrets.baseline patterns to add
CUSTOM_PATTERNS = [
    r'sk-[a-zA-Z0-9]{32,}',           # OpenAI keys
    r'sk-ant-[a-zA-Z0-9\-]{50,}',     # Anthropic keys
    r'xoxb-[0-9]+-[a-zA-Z0-9]+',      # Slack bot tokens
    r'ghp_[a-zA-Z0-9]{36}',           # GitHub personal access tokens
    r'AKIA[0-9A-Z]{16}',              # AWS access key IDs
]

Safe Practices for AI Coding Tools

Code Review as a Security Gate

Treat AI-generated code the same way you treat code from a junior developer: review every change before merging. Specifically:

  • Check for parameterized queries wherever database access is generated
  • Verify authentication and authorization on every endpoint generated
  • Look for hardcoded values that should be environment variables
  • Check cryptographic choices (library selection, key sizes, algorithm choices)

Create a code review checklist specifically for AI-generated code:

## AI-Generated Code Review Checklist

### Input Handling
- [ ] All user inputs are validated before use
- [ ] No string-formatted SQL queries (use parameterized queries)
- [ ] File paths are validated against allowed directories
- [ ] No eval() or exec() with user-controlled input

### Authentication / Authorization
- [ ] Sensitive endpoints have authentication decorators
- [ ] Authorization checks verify the requesting user owns the resource
- [ ] No authorization logic based solely on client-supplied values

### Secrets and Configuration
- [ ] No hardcoded secrets, API keys, or passwords
- [ ] Credentials loaded from environment variables or secrets manager
- [ ] No internal URLs hardcoded

### Cryptography
- [ ] Passwords hashed with bcrypt/argon2 (not MD5/SHA1)
- [ ] Tokens generated with secrets.token_urlsafe() (not random)
- [ ] TLS verification not disabled

Use SAST Tools to Catch What Review Misses

Static analysis tools should run on every commit as a CI gate, including on AI-generated code:

# .github/workflows/security.yml
name: Security Scan

on: [push, pull_request]

jobs:
  sast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        uses: returntocorp/semgrep-action@v1
        with:
          config: >-
            p/python
            p/javascript
            p/security-audit
            p/owasp-top-ten

      - name: Run Bandit (Python)
        run: |
          pip install bandit
          bandit -r . -ll -ii --exit-zero -f json -o bandit-report.json

      - name: Secret scanning
        uses: gitleaks/gitleaks-action@v2

Configure Copilot Responsibly

GitHub Copilot (Business/Enterprise tiers) offers configuration options:

Disable suggestions matching public code:

// .github/copilot/settings.json
{
  "suggestions": {
    "matchPublicCode": "disabled"
  }
}

This prevents Copilot from suggesting code that closely matches copyrighted public code — and as a side effect, prevents direct reproduction of memorized vulnerable patterns.

Use Copilot for Business or Enterprise — these tiers explicitly don't use your code for training other users' suggestions, reducing but not eliminating the training data leakage concern.

Prompt Engineering for Secure Suggestions

Copilot generates completions based on context. You can influence the quality of suggestions by writing comments that prime secure patterns:

# Function to get user by ID
# Use parameterized query to prevent SQL injection
# Validate that user_id is a positive integer
def get_user(user_id: int) -> dict | None:
    # Copilot is now more likely to suggest a secure parameterized query
# Generate a cryptographically secure random token for password reset
# Use secrets module, not random
def generate_reset_token() -> str:
    # Copilot is more likely to suggest secrets.token_urlsafe()

The comment context shifts which training patterns are activated.

Security-Focused Code Review with AI

Copilot can also be used to review code for security issues — a form of AI-assisted security review. GitHub Copilot Chat can analyze code blocks:

# Ask Copilot Chat:
"Review this function for security vulnerabilities, focusing on injection
attacks, authentication bypasses, and insecure data handling."

Use this as a supplement to automated SAST, not a replacement. AI-assisted review catches different issues than pattern-matching SAST, but both have blind spots.

Measuring Security Impact of AI Coding Tools

Track your vulnerability introduction rate before and after adopting AI coding tools:

# Metrics to track
METRICS = {
    "sast_findings_per_pr": "New SAST findings per pull request",
    "secret_exposure_incidents": "Secrets accidentally committed to version control",
    "security_review_time": "Time spent on security review per PR",
    "vulnerability_category_distribution": "Types of vulnerabilities introduced",
}

If SAST findings per PR increase after adopting AI coding tools, the productivity gain may be offset by increased security debt. Adjust your review gates and training accordingly.

The Bottom Line

AI coding tools are net positive for developer productivity when used with appropriate safeguards. The key shifts required:

  1. Code review discipline cannot decrease because AI generates code faster. If anything, AI-generated code requires more security scrutiny, not less — the developer has less context about what the AI wrote than code they wrote themselves.

  2. Automated security gates are non-negotiable — secret scanning, SAST, and dependency scanning must run on every commit, regardless of whether a human or AI wrote the code.

  3. Developer security training remains essential — developers need to recognize when AI suggestions are insecure, which requires knowing what secure code looks like.

  4. The review checklist should evolve — maintain a checklist of vulnerability patterns specifically observed in AI-generated code in your codebase, updating it as new patterns emerge.

AI coding tools don't eliminate the need for security expertise. They increase the volume of code that expertise needs to cover.

GitHub Copilot
AI code generation
code security
secret leakage
SAST
secure coding

Check Your Security Score — Free

See exactly how your domain scores on DMARC, TLS, HTTP headers, and 25+ other automated security checks in under 60 seconds.