Skip to content

How to Enable Ruff Security Rules

Ruff’s S rule group implements flake8-bandit security checks, catching hardcoded passwords, injection vulnerabilities, insecure hashing, and unsafe deserialization before they reach production.

Enable the Full Set

Add the S group to the project’s pyproject.toml:

[tool.ruff.lint]
extend-select = [
    "S",    # flake8-bandit security rules
]

This enables all flake8-bandit rules at once. Run the linter to see what it finds:

uv run ruff check .

Pick Specific Rules

For a more targeted approach, enable individual rules instead of the full group:

[tool.ruff.lint]
extend-select = [
    "S105",  # Hardcoded passwords in strings
    "S106",  # Hardcoded passwords in function arguments
    "S107",  # Hardcoded passwords in function defaults
    "S108",  # Insecure temp file usage
    "S113",  # HTTP requests without timeout
    "S301",  # Pickle deserialization
    "S324",  # Insecure hash functions
    "S608",  # SQL injection via string formatting
]

These rules can be combined with the recommended Ruff defaults by adding them to the same extend-select list.

Rule Category What it catches
S105, S106, S107 Secrets Hardcoded passwords in assignments, arguments, and defaults
S108 File safety Insecure use of temp files
S113 Network HTTP requests missing a timeout parameter
S301 Deserialization Use of pickle.loads and related functions
S324 Cryptography Insecure hash algorithms like MD5 and SHA1
S608 Injection SQL queries built with string concatenation or formatting

Examples

Hardcoded secrets and insecure hashing

import hashlib

# S105: Possible hardcoded password assigned to: "password"
password = "supersecret123"

# S324: Probable use of insecure hash functions in `hashlib`: `md5`
digest = hashlib.md5(b"data").hexdigest()

Fix by loading secrets from environment variables and switching to a secure hash algorithm:

import hashlib
import os

password = os.environ["APP_PASSWORD"]

digest = hashlib.sha256(b"data").hexdigest()

SQL injection

# S608: Possible SQL injection vector through string-based query construction
def get_user(user_id):
    query = "SELECT * FROM users WHERE id = " + user_id
    return query

Fix by using parameterized queries:

def get_user(cursor, user_id):
    cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
    return cursor.fetchone()

Suppress False Positives

Some S rules flag code that is safe in context. Use # noqa comments to suppress individual lines:

assert response.status_code == 200  # noqa: S101

For broader suppression, use per-file ignores in pyproject.toml. This is common for test files, where assert statements (S101) and hardcoded test credentials (S105, S106) are expected:

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101", "S105", "S106"]

Learn More

Get Python tooling updates

Subscribe to the newsletter
Last updated on

Please submit corrections and feedback...