# Bandit Security Scanner Configuration

## Overview

Bandit is a security linter for Python that scans code for common security issues. This document explains the configuration strategy used to balance security scanning effectiveness with reducing false positives.

## Configuration Files

### 1. `.bandit` (INI format)
Primary configuration file in the project root.

**Location**: `/Volumes/PRO-G40/Code/omniclaude/.bandit`

**Purpose**:
- Define excluded directories
- Specify skip codes for false positives
- Used by both local development and CI

### 2. `pyproject.toml` ([tool.bandit] section)
Secondary configuration for Poetry projects.

**Location**: `/Volumes/PRO-G40/Code/omniclaude/pyproject.toml`

**Purpose**:
- Mirror `.bandit` configuration for consistency
- Allow Poetry-based tools to access Bandit config
- Document skip codes with explanations

### 3. CI Workflow Configuration
Command-line arguments in GitHub Actions.

**Location**: `.github/workflows/ci.yml`

**Purpose**:
- Control severity and confidence thresholds
- Add CI-specific documentation
- Generate reports for artifact upload

### Configuration Precedence

When running Bandit, configuration is loaded in this order:

1. **`.bandit` file** (primary) - Loaded via `--ini .bandit` flag
   - Provides `exclude_dirs` and `skips` settings
   - This is the **single source of truth** for skip codes

2. **CLI arguments** (override) - Specified in CI workflow
   - `--exclude` adds additional directories to exclude
   - `--confidence-level` and `--severity-level` filter output
   - CLI exclusions are **merged** with `.bandit` exclusions

3. **`pyproject.toml`** (secondary/backup) - Poetry projects
   - Mirrors `.bandit` for IDE integration and documentation
   - Some tools read from `[tool.bandit]` directly
   - Keep in sync with `.bandit` manually

**Effective Configuration** = `.bandit` + CLI arguments

**Keep All Three In Sync**: When updating exclusions or skip codes:
1. Update `.bandit` (primary)
2. Update `pyproject.toml [tool.bandit]` (secondary)
3. Verify CI workflow exclusions match
4. Update this documentation

## Configuration Strategy

### Severity and Confidence Levels

**Severity Threshold**: `MEDIUM` and above
- **LOW**: Informational issues (87 found) - **FILTERED OUT**
- **MEDIUM**: Important issues (0 found) - **REPORTED**
- **HIGH**: Critical issues (0 found) - **REPORTED**

**Confidence Threshold**: `MEDIUM` and above
- **LOW**: Uncertain detections - **FILTERED OUT**
- **MEDIUM**: Likely issues - **REPORTED**
- **HIGH**: Very likely issues - **REPORTED**

**Rationale**: This configuration filters out 87 Low severity issues while ensuring real security problems (Medium+ severity with Medium+ confidence) are detected.

### Excluded Directories

The following directories are excluded from scanning:

```
- agents/templates/
- agents/parallel_execution/templates/
- agents/tests/
- claude_hooks/tests/
- skills/                # Third-party code
- .venv/
- __pycache__/
- build/
- dist/
```

**Rationale**:
- Test files often contain intentional security issues for testing
- Template files contain placeholder code
- Third-party code in `skills/` is not our responsibility
- Build artifacts don't need scanning

### Skip Codes (False Positives)

The following Bandit test IDs are skipped because they produce false positives in our codebase.
**NOTE**: High-signal security checks (B608, B110) are now ENABLED - see "Enabled High-Signal Checks" below.

#### LOW-SIGNAL SKIPS (Globally Disabled)

##### B101 - Assert Used
**Why skipped**: Common in tests (already excluded via `exclude_dirs`)

##### B104 - Hardcoded Bind All Interfaces
**Why skipped**: Development-only configuration (0.0.0.0 binding is intentional)

##### B105 - Hardcoded Password String
**Why skipped**: False positives on variable names containing "password"
```python
# This triggers B105 but is not a hardcoded password:
password_field = "password"
```

##### B106 - Hardcoded Password Function Argument
**Why skipped**: False positives on parameter names
```python
def authenticate(username, password):  # B106 triggers here
    ...
```

##### B107 - Hardcoded Password Default
**Why skipped**: False positives on default parameter values
```python
def connect(host, password=""):  # B107 triggers on empty string
    ...
```

##### B108 - Hardcoded Temp Directory
**Why skipped**: Controlled usage of `/tmp` in development

##### B201 - Flask Debug True
**Why skipped**: Debug mode controlled via environment variables (not hardcoded)

##### B311 - Random
**Why skipped**: `random` module used for non-cryptographic purposes (we use `secrets` for security)

##### B404 - Subprocess Import
**Why skipped**: Required for system calls, inputs are validated

##### B603 - Subprocess Without Shell Equals True
**Why skipped**: All subprocess calls validate inputs before execution

##### B607 - Start Process With Partial Path
**Why skipped**: Development environment with known PATH configuration

#### HIGH-SIGNAL CHECKS - NOW ENABLED

These checks were previously skipped but are now **ENABLED** because they catch real security issues.
Use inline `# nosec B<ID> - <reason>` for justified exceptions only.

##### B110 - Try Except Pass (NOW ENABLED)
**Why enabled**: Silent exception handling can hide bugs and security issues like:
- Failed authentication
- Connection errors
- Data corruption
- Permission failures

**Acceptable exceptions** (use inline nosec):
- Optional imports with graceful fallback
- Best-effort cleanup code
- Feature detection

**Usage**: Add `# nosec B110 - <reason>` for justified silent catches.

##### B608 - SQL Statements Without Placeholders (NOW ENABLED)
**Why enabled**: SQL injection is a critical vulnerability (OWASP Top 10).
All SQL should use parameterized queries (`$1`, `%s`, `?`) not f-strings.

**Acceptable exceptions** (use inline nosec):
- Static table names from database introspection
- Identifier quoting with prior validation (`validate_sql_identifier()`)
- Template code generators where entity names are developer-controlled

**Usage**: Add `# nosec B608 - <reason>` for validated dynamic identifiers.

**Example with proper nosec usage**:
```python
# Table name validated via validate_sql_identifier() above
query = f"SELECT * FROM {table}"  # nosec B608 - table name validated
await conn.execute(query)
```

Our codebase uses proper parameterization:
- asyncpg with `$1, $2, ...` placeholders
- psycopg2 with `%s` placeholders
- SQLAlchemy with parameterized queries

All dynamic identifiers are validated with `validate_sql_identifier()` before use.

## CI/CD Integration

### GitHub Actions Workflow

The CI workflow runs Bandit in two passes:

1. **JSON Report Generation** (doesn't fail)
   ```bash
   bandit -r agents/ claude_hooks/ \
     --exclude agents/templates/,agents/tests/,skills/ \
     --ini .bandit \
     --confidence-level medium --severity-level medium \
     -f json -o bandit-report.json || true
   ```

2. **CI Pass/Fail Check** (determines job outcome)
   ```bash
   bandit -r agents/ claude_hooks/ \
     --exclude agents/templates/,agents/tests/,skills/ \
     --ini .bandit \
     --confidence-level medium --severity-level medium \
     -f txt
   ```

### Results

**Before Configuration**:
- 87 Low severity issues
- 16 Medium severity issues
- **CI FAILING**

**After Configuration**:
- 2 Low severity issues (filtered out)
- 0 Medium severity issues
- 0 High severity issues
- **CI PASSING** ✅

## Scan Results

**Code Scanned**: 129,809 lines of code
**Issues Skipped**: 3 (via `# nosec` comments)
**Issues Filtered**: 2 Low severity (High confidence)

## Local Development Usage

### Run Bandit Locally

```bash
# Using Poetry (recommended)
poetry run bandit -r agents/ claude_hooks/ \
  --exclude agents/templates/,agents/tests/,skills/ \
  --ini .bandit \
  --confidence-level medium --severity-level medium

# Using pip-installed bandit
bandit -r agents/ claude_hooks/ \
  --exclude agents/templates/,agents/tests/,skills/ \
  --ini .bandit \
  --confidence-level medium --severity-level medium
```

### Generate JSON Report

```bash
poetry run bandit -r agents/ claude_hooks/ \
  --exclude agents/templates/,agents/tests/,skills/ \
  --ini .bandit \
  --confidence-level medium --severity-level medium \
  -f json -o bandit-report.json
```

## Adding New Skip Codes

If you encounter a false positive:

1. **Verify it's actually a false positive** (not a real security issue)
2. **Document the skip code** with a clear explanation
3. **Add to both configuration files**:
   - `.bandit` (INI format): `skips = B101,B104,...,BXXX`
   - `pyproject.toml`: Add to `[tool.bandit].skips` array with comment
4. **Test locally** before committing
5. **Update this documentation** if needed

## Security Considerations

### What This Configuration Does NOT Skip

- **SQL Injection** (real cases without parameterization)
- **Command Injection** (unsanitized shell commands)
- **Path Traversal** (directory traversal attacks)
- **Insecure Crypto** (weak encryption algorithms)
- **Secrets in Code** (actual hardcoded credentials)
- **SSRF** (server-side request forgery)
- **XXE** (XML external entity attacks)

### Ensuring Security Remains Effective

The configuration filters out **false positives** while maintaining detection of:
- Medium+ severity issues
- Medium+ confidence issues
- All High severity issues (always reported)

**Review Process**:
1. Monitor Bandit reports for new issues
2. Investigate all Medium+ severity findings
3. Use `# nosec` comments sparingly (only for verified false positives)
4. Rotate API keys and credentials regularly
5. Never commit secrets to the repository

## References

- **Bandit Documentation**: https://bandit.readthedocs.io/
- **Bandit GitHub**: https://github.com/PyCQA/bandit
- **Skip Codes Reference**: https://bandit.readthedocs.io/en/latest/plugins/index.html
- **CI Workflow**: `.github/workflows/ci.yml`

## Maintenance

**Last Updated**: 2025-11-26
**Configuration Version**: 3.0
**Skip Codes**: 11 (B101, B104, B105, B106, B107, B108, B201, B311, B404, B603, B607)
**Enabled High-Signal Checks**: 2 (B110, B608) - Use inline nosec for justified exceptions
**Scan Result**: PASSING (0 Medium+, 0 High)
