Skip to main content
โšก Calmops

Dependency Security and Vulnerability Scanning in Python: Protecting Your Supply Chain

Dependency Security and Vulnerability Scanning in Python: Protecting Your Supply Chain

Your Python application depends on hundreds of packages. You didn’t write most of them. You probably don’t review their code. Yet you trust them with your data, your users’ data, and your infrastructure.

This is the supply chain risk. A vulnerability in a single dependency can compromise your entire application. The 2023 PyPI supply chain attacks demonstrated this reality: attackers uploaded malicious packages with names similar to popular libraries, hoping developers would install them by mistake.

Dependency security isn’t optional. It’s essential. This guide shows you how to implement it.

Understanding Dependency Vulnerabilities

What is a Dependency Vulnerability?

A dependency vulnerability is a security flaw in a package your application uses. It might be:

  • Code injection: Attackers can execute arbitrary code
  • Data exposure: Sensitive data is leaked
  • Denial of service: Attackers can crash your application
  • Authentication bypass: Attackers can bypass security controls

Direct vs Transitive Dependencies

Direct dependencies are packages you explicitly install:

# requirements.txt
flask==2.3.2
requests==2.31.0

Transitive dependencies are packages that your dependencies depend on:

flask==2.3.2
โ”œโ”€โ”€ werkzeug==2.3.6
โ”œโ”€โ”€ jinja2==3.1.2
โ”‚   โ””โ”€โ”€ markupsafe==2.1.1
โ””โ”€โ”€ click==8.1.3

A vulnerability in markupsafe (a transitive dependency) affects your application even though you didn’t directly install it.

Why Python Projects Are Vulnerable

  • Large dependency trees: Average Python project has 50+ transitive dependencies
  • Rapid development: Security patches may lag behind vulnerability discovery
  • Ecosystem scale: PyPI has 500,000+ packages; not all are well-maintained
  • Supply chain attacks: Attackers target popular packages

Vulnerability Databases

Where Vulnerabilities Are Tracked

CVE (Common Vulnerabilities and Exposures)

  • Official vulnerability database
  • Assigned by MITRE
  • Example: CVE-2023-12345

GitHub Advisory Database

  • Aggregates vulnerabilities from multiple sources
  • Includes Python-specific advisories
  • Integrated with GitHub’s dependency scanning

PyPI Advisory Database

  • Python-specific vulnerability database
  • Maintained by Python Packaging Authority
  • Integrated into pip-audit

NVD (National Vulnerability Database)

  • US government vulnerability database
  • Comprehensive but not Python-specific

Python Dependency Scanning Tools

Tool Comparison

Tool Type Speed Accuracy Integration Cost
pip-audit CLI Fast High Pre-commit, CI/CD Free
Safety CLI Fast Medium Pre-commit, CI/CD Free/Paid
Snyk CLI/Web Medium High GitHub, GitLab, CI/CD Free/Paid
Bandit CLI Fast Medium Pre-commit, CI/CD Free
OWASP Dependency-Check CLI Slow High CI/CD Free

pip-audit: The Modern Standard

pip-audit is the official Python packaging tool for vulnerability scanning:

# Install pip-audit
pip install pip-audit

# Scan current environment
pip-audit

# Scan requirements file
pip-audit --requirements requirements.txt

# Scan with detailed output
pip-audit --desc

# Generate JSON report
pip-audit --format json > report.json

# Ignore specific vulnerabilities
pip-audit --ignore-ids PYSEC-2023-12345

Safety: Comprehensive Scanning

Safety checks against a vulnerability database:

# Install Safety
pip install safety

# Scan current environment
safety check

# Scan requirements file
safety check --file requirements.txt

# Generate JSON report
safety check --json > report.json

# Check for insecure packages
safety check --insecure

Snyk: Enterprise-Grade Scanning

Snyk provides comprehensive vulnerability scanning with remediation guidance:

# Install Snyk
npm install -g snyk

# Authenticate
snyk auth

# Scan project
snyk test

# Generate report
snyk test --json > report.json

# Monitor for new vulnerabilities
snyk monitor

Bandit: Code Security Scanning

Bandit scans Python code for security issues (not just dependencies):

# Install Bandit
pip install bandit

# Scan Python files
bandit -r .

# Generate JSON report
bandit -r . -f json > report.json

# Exclude test files
bandit -r . --exclude tests/

Implementing Vulnerability Scanning

Step 1: Local Scanning

Start by scanning your project locally:

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

# Scan for vulnerabilities
pip-audit

# Example output:
# Found 2 vulnerabilities in 1 package
# 
# django 3.2.0
#   PYSEC-2023-12345: SQL injection vulnerability
#   PYSEC-2023-12346: Authentication bypass

Step 2: Pre-commit Hooks

Prevent vulnerable code from being committed:

# Install pre-commit
pip install pre-commit

# Create .pre-commit-config.yaml
cat > .pre-commit-config.yaml << 'EOF'
repos:
  - repo: https://github.com/Lucas-C/pre-commit-hooks-safety
    rev: v1.3.1
    hooks:
      - id: python-safety-dependencies-check

  - repo: https://github.com/PyCQA/bandit
    rev: 1.7.5
    hooks:
      - id: bandit
        args: ['-c', '.bandit']

  - repo: https://github.com/hadialqattan/pycln
    rev: v2.2.2
    hooks:
      - id: pycln
        args: [--all]
EOF

# Install pre-commit hooks
pre-commit install

# Run manually
pre-commit run --all-files

Step 3: CI/CD Integration

Automate scanning in your CI/CD pipeline:

# GitHub Actions example
name: Security Scan

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pip-audit safety bandit
      
      - name: Run pip-audit
        run: pip-audit
      
      - name: Run Safety
        run: safety check
      
      - name: Run Bandit
        run: bandit -r . -f json -o bandit-report.json
      
      - name: Upload reports
        uses: actions/upload-artifact@v3
        with:
          name: security-reports
          path: |
            bandit-report.json
# GitLab CI example
security_scan:
  image: python:3.11
  script:
    - pip install -r requirements.txt
    - pip install pip-audit safety bandit
    - pip-audit
    - safety check
    - bandit -r . -f json -o bandit-report.json
  artifacts:
    reports:
      dependency_scanning: bandit-report.json

Step 4: Automated Dependency Updates

Use Dependabot or Renovate to automatically update dependencies:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
    reviewers:
      - "security-team"
    labels:
      - "dependencies"
    commit-message:
      prefix: "chore(deps):"

Interpreting Vulnerability Reports

Understanding Severity Levels

  • Critical: Immediate exploitation possible, high impact
  • High: Likely exploitation, significant impact
  • Medium: Possible exploitation, moderate impact
  • Low: Difficult exploitation, minor impact

Example Report

Found 3 vulnerabilities in 2 packages

django 3.2.0
  PYSEC-2023-12345: SQL injection in ORM
    Severity: HIGH
    Affected versions: <3.2.1
    Fixed version: 3.2.1
    Description: SQL injection vulnerability in Django ORM...
    References: https://nvd.nist.gov/vuln/detail/CVE-2023-12345

requests 2.28.0
  PYSEC-2023-12346: Improper certificate validation
    Severity: MEDIUM
    Affected versions: <2.28.1
    Fixed version: 2.28.1
    Description: Requests library fails to validate certificates...

Prioritization Strategy

  1. Critical/High severity: Fix immediately
  2. Medium severity: Fix within sprint
  3. Low severity: Track and fix in maintenance window
  4. Unpatched vulnerabilities: Evaluate alternatives or mitigations

Best Practices

1. Pin Dependency Versions

# Bad: No version pinning
# requirements.txt
flask
requests
django

# Good: Pinned versions
# requirements.txt
flask==2.3.2
requests==2.31.0
django==4.2.0

2. Use Lock Files

Lock files ensure reproducible builds:

# pip-tools
pip install pip-tools
pip-compile requirements.in > requirements.txt

# Poetry
poetry lock

# Pipenv
pipenv lock

3. Regular Scanning Schedule

# Weekly scan
0 0 * * 0 pip-audit

# Daily scan in CI/CD
schedule:
  - cron: '0 0 * * *'

4. Maintain a Software Bill of Materials (SBOM)

# Generate SBOM with pip-audit
pip-audit --format cyclonedx > sbom.xml

# Generate SBOM with Snyk
snyk sbom --format cyclonedx > sbom.json

5. Document Vulnerability Decisions

# SECURITY.md
## Known Vulnerabilities

### PYSEC-2023-12345: SQL Injection in Django
- **Status**: Accepted Risk
- **Reason**: Vulnerability only affects admin interface, not user-facing code
- **Mitigation**: Restrict admin access to trusted users
- **Review Date**: 2024-01-01

Common Pitfalls

Pitfall 1: Alert Fatigue

Too many false positives lead to ignoring real issues:

# Bad: Scanning everything
pip-audit  # Reports 50 vulnerabilities

# Good: Prioritize and filter
pip-audit --desc | grep -i "critical\|high"

Pitfall 2: Ignoring Transitive Dependencies

# Bad: Only checking direct dependencies
pip list

# Good: Understanding full dependency tree
pip-audit --desc  # Shows all vulnerabilities
pipdeptree  # Visualize dependency tree

Pitfall 3: Breaking Changes During Updates

# Bad: Blindly updating all packages
pip install --upgrade -r requirements.txt

# Good: Testing before updating
pip install --upgrade --dry-run -r requirements.txt
# Review changes, then update selectively
pip install --upgrade package-name==new-version
# Run tests
pytest

Pitfall 4: Not Scanning Development Dependencies

# Bad: Only scanning production dependencies
pip-audit

# Good: Scanning all dependencies
pip-audit --all

Advanced Practices

Custom Vulnerability Policies

# security_policy.py
VULNERABILITY_POLICY = {
    'critical': {
        'max_age_days': 0,  # Fix immediately
        'auto_update': True,
    },
    'high': {
        'max_age_days': 7,
        'auto_update': True,
    },
    'medium': {
        'max_age_days': 30,
        'auto_update': False,
    },
    'low': {
        'max_age_days': 90,
        'auto_update': False,
    },
}

Monitoring for New Vulnerabilities

# Snyk monitoring
snyk monitor

# GitHub security alerts
# Enable in repository settings

# Email notifications
# Configure in tool settings

Incident Response

# Vulnerability Response Procedure

1. **Detection**: Automated scan detects vulnerability
2. **Assessment**: Evaluate severity and impact
3. **Notification**: Alert security team
4. **Remediation**: Update package or apply workaround
5. **Testing**: Run full test suite
6. **Deployment**: Deploy fix to production
7. **Verification**: Confirm vulnerability is resolved
8. **Documentation**: Document incident and lessons learned

Conclusion

Dependency security is not a one-time taskโ€”it’s an ongoing process. By implementing vulnerability scanning, you protect your application, your users, and your organization.

Key takeaways:

  • Understand your dependencies: Know what you’re using and why
  • Scan regularly: Automate scanning in development and CI/CD
  • Prioritize remediation: Focus on critical and high-severity issues
  • Keep dependencies updated: Use automated tools like Dependabot
  • Document decisions: Track why you accept or ignore vulnerabilities
  • Test thoroughly: Ensure updates don’t break functionality
  • Monitor continuously: Watch for new vulnerabilities in production
  • Educate your team: Make security a shared responsibility

Dependency security is not a burdenโ€”it’s an investment in your application’s reliability and your users’ trust. Start implementing these practices today.

Resources

Comments