Introduction to Bandit Security Rules with Ruff: Finding Common Security Issues in Python Code

Security vulnerabilities in Python code can often go unnoticed during development. While code reviews help catch many issues, automated tools like Ruff with its Bandit integration can systematically identify common security pitfalls before they make it to production. This article introduces Ruff’s implementation of Bandit’s security rules, providing a faster and more integrated approach to Python security scanning.

What’s Static Analysis, and Why Should You Care?

Static Application Security Testing (SAST) analyzes code without executing it, identifying potential security vulnerabilities through pattern matching and code structure analysis. Finding security issues during development is significantly more cost-effective and less stressful than addressing them in production environments.

Meet Ruff’s Bandit Integration

Ruff includes a native implementation of Bandit’s security rules, providing the same security checks but with significantly improved performance. These rules help identify common security anti-patterns in Python code, with Ruff’s implementation being 10-100x faster than traditional tools.

Here’s what Ruff’s Bandit rules check for:

  • Weak cryptographic practices, like using MD5 for password hashing
  • Exposed secrets such as hardcoded API keys
  • SQL injection vulnerabilities through string formatting
  • Unsafe deserialization using modules like pickle

Getting started is straightforward:

pip install ruff

Using Ruff’s Bandit Rules

Running a security scan with Ruff is simple. Create a pyproject.toml file to enable the Bandit rules:

[tool.ruff.lint]
select = ["S"]  # Enable all Bandit rules

Then run Ruff:

# Check a single file
ruff check your_file.py

# Or scan an entire directory
ruff check .

Ruff’s output provides detailed information about each finding:

  • Severity level (Low/Medium/High)
  • Confidence rating (Low/Medium/High)
  • Precise location in the code
  • Description of the potential issue and remediation steps

Understanding Ruff’s Bandit Output: A Real Example

Consider this common security anti-pattern:

# Potentially dangerous code
def process_data(user_input):
    return pickle.loads(user_input)  # Ruff: S301 Pickle and modules that wrap it can be unsafe when used to deserialize untrusted data

Ruff flags this with high severity because pickle.loads() can execute arbitrary code, potentially leading to remote code execution. A safer alternative would be:

def process_data(user_input):
    return json.loads(user_input)  # Much safer!

Configuring Ruff’s Bandit Rules

Ruff’s Bandit implementation can be configured to match specific project requirements. Some findings might not be relevant in certain contexts, and these can be managed through configuration.

In your pyproject.toml, you can customize the behavior:

[tool.ruff.lint]
select = ["S"]  # Enable Bandit rules

[tool.ruff.lint.flake8-bandit]
# Configure specific Bandit options
check-typed-exception = true  # Enable additional checks for try-except-pass
hardcoded-tmp-directory = ["/tmp", "/var/tmp", "/custom/temp"]  # Configure temp directory checks

For specific lines that are known to be safe:

# Explicitly marking as safe for testing environment
test_password = "dummy_password"  # noqa: S105

Integrating Ruff into Your Workflow

Ruff works best when integrated into the development workflow. Here’s an example of incorporating it into a CI pipeline:

name: Security Checks

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Ruff
        run: |
          pip install ruff
          ruff check --select S .  # Run only Bandit security rules

This configuration ensures that code with potential security issues cannot be merged without review.

The Real Value of Security Scanning

Security tools like Ruff’s Bandit integration provide consistent, automated checking for common vulnerabilities. While they don’t guarantee perfect security, they establish a baseline of protection by catching well-known issues early in the development cycle. This proactive approach to security can prevent incidents before they occur and protect users from common vulnerabilities.

Getting Started Today

To begin improving your code’s security with Ruff:

  1. Install Ruff: pip install ruff
  2. Create a pyproject.toml with Bandit rules enabled
  3. Run initial scan: ruff check --select S .
  4. Address high-severity issues first
  5. Add it to your CI pipeline
  6. Make security scanning a regular part of your development process

Remember that security is an ongoing process of improvement. Tools like Ruff’s Bandit integration provide an automated first line of defense against common security issues, making it easier to maintain a secure codebase as it grows and evolves.

Even in seemingly simple applications, security vulnerabilities can lurk in unexpected places. Using automated security tools like Ruff helps ensure consistent security practices across your entire codebase.

Subscribe to the Newsletter

Get the latest posts and insights delivered straight to your inbox.