# How to Enable Ruff Security Rules


[Ruff](https://pydevtools.com/handbook/reference/ruff.md)'s `S` rule group implements [flake8-bandit](https://docs.astral.sh/ruff/rules/#flake8-bandit-s) 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`:

```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:

```bash
uv run ruff check .
```

## Pick Specific Rules

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

```toml
[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](https://pydevtools.com/handbook/how-to/how-to-configure-recommended-ruff-defaults.md) 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

```python
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:

```python
import hashlib
import os

password = os.environ["APP_PASSWORD"]

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

### SQL injection

```python
# 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:

```python
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:

```python
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:

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

## Learn More

- [Ruff flake8-bandit rule reference](https://docs.astral.sh/ruff/rules/#flake8-bandit-s)
- [How to configure recommended Ruff defaults](https://pydevtools.com/handbook/how-to/how-to-configure-recommended-ruff-defaults.md)
- [How to sort Python imports with Ruff](https://pydevtools.com/handbook/how-to/how-to-sort-python-imports-with-ruff.md)
- [Ruff: a complete guide](https://pydevtools.com/handbook/explanation/ruff-complete-guide.md)
