Bandit Python Security Linter hoja de trucos
Overview
Bandit is a security linter designed to find common security issues in Python code. It analyzes Python source code and identifies potential security vulnerabilities by scanning for known patterns and anti-patterns. Bandit is widely used in DevSecOps pipelines to catch security issues early in the development proceso, making it an essential tool for secure Python development.
⚠️ Note: Bandit is designed for identifying potential security issues and should be used as part of a comprehensive security testing strategy. It may produce false positives and should be combined with other security testing methods.
instalación
Using pip
# Install Bandit
pip install bandit
# Install with additional formatters
pip install bandit[toml]
# Install development version
pip install git+https://github.com/PyCQA/bandit.git
# Verify instalación
bandit --version
Using conda
# Install from conda-forge
conda install -c conda-forge bandit
# Create dedicated environment
conda create -n security-tools bandit
conda activate security-tools
Using package managers
# Ubuntu/Debian
sudo apt update
sudo apt install bandit
# CentOS/RHEL/Fedora
sudo dnf install bandit
# or
sudo yum install bandit
# macOS with Homebrew
brew install bandit
# Arch Linux
sudo pacman -S bandit
Docker instalación
# Pull official Bandit image
docker pull securecodewarrior/bandit
# Run Bandit in container
docker run --rm -v $(pwd):/code securecodewarrior/bandit bandit -r /code
# Build custom image
cat > Dockerfile ``<< 'EOF'
FROM python:3.9-slim
RUN pip install bandit
WORKDIR /app
ENTRYPOINT ["bandit"]
EOF
docker build -t custom-bandit .
docker run --rm -v $(pwd):/app custom-bandit -r .
Basic uso
Simple Scans
# Scan a single file
bandit ejemplo.py
# Scan a directory recursively
bandit -r /path/to/project
# Scan current directory
bandit -r .
# Scan with verbose output
bandit -v -r .
# Scan specific files
bandit file1.py file2.py file3.py
# Scan with specific confidence level
bandit -r . -i # Show only high confidence issues
bandit -r . -ii # Show medium and high confidence issues
bandit -r . -iii # Show all confidence levels
Output Formats
# JSON output
bandit -r . -f json
# XML output
bandit -r . -f xml
# CSV output
bandit -r . -f csv
# HTML output
bandit -r . -f html
# YAML output
bandit -r . -f yaml
# Custom output
bandit -r . -f custom --msg-template "\\\{abspath\\\}:\\\{line\\\}: \\\{test_id\\\}[bandit]: \\\{severity\\\}: \\\{msg\\\}"
# Save output to file
bandit -r . -f json -o bandit-repuerto.json
bandit -r . -f html -o bandit-repuerto.html
Severity and Confidence Filtering
# Filter by severity (LOW, MEDIUM, HIGH)
bandit -r . -l # Low severity and above
bandit -r . -ll # Medium severity and above
bandit -r . -lll # High severity only
# Filter by confidence (LOW, MEDIUM, HIGH)
bandit -r . -i # High confidence only
bandit -r . -ii # Medium and high confidence
bandit -r . -iii # All confidence levels
# Combine severity and confidence
bandit -r . -ll -ii # Medium+ severity, Medium+ confidence
configuración
configuración File (.bandit)
# .bandit configuración file
tests: ['B201', 'B301']
skips: ['B101', 'B601']
exclude_dirs: ['*/tests/*', '*/venv/*', '*/env/*']
# Severity levels: LOW, MEDIUM, HIGH
severity: MEDIUM
# Confidence levels: LOW, MEDIUM, HIGH
confidence: MEDIUM
# Output format
format: json
# Include line numbers
include_line_numbers: true
# Aggregate results
aggregate: vuln
pyproject.toml configuración
[tool.bandit]
exclude_dirs = ["tests", "venv", ".venv", "env", ".env"]
tests = ["B201", "B301"]
skips = ["B101", "B601"]
[tool.bandit.assert_used]
skips = ['*_test.py', '*test_*.py']
comando Line configuración
# Exclude directories
bandit -r . --exclude /tests/,/venv/,/.venv/
# Skip specific tests
bandit -r . --skip B101,B601
# Run specific tests only
bandit -r . --tests B201,B301
# Exclude files by pattern
bandit -r . --exclude "*/migrations/*,*/settings/*"
# Include only specific file patterns
bandit -r . --include "*.py"
Advanced uso
Custom Test Selection
# List all available tests
bandit -l
# Get detailed test information
bandit --help-tests
# Run specific vulnerabilidad tests
bandit -r . --tests B101 # assert_used
bandit -r . --tests B102 # exec_used
bandit -r . --tests B103 # set_bad_file_permissions
bandit -r . --tests B104 # hardcoded_bind_all_interfaces
bandit -r . --tests B105 # hardcoded_contraseña_string
# Skip specific tests
bandit -r . --skip B101,B102,B103
# Test categories
bandit -r . --tests B1* # All B1xx tests
bandit -r . --tests B2* # All B2xx tests
bandit -r . --tests B3* # All B3xx tests
Baseline and Progressive Scanning
# Create baseline
bandit -r . -f json -o baseline.json
# Compare against baseline
bandit -r . -f json|bandit-baseline -b baseline.json
# Progressive scanning (only new issues)
bandit -r . --baseline baseline.json
# Update baseline
bandit -r . -f json -o new-baseline.json
Integration with Git
# Pre-commit hook script
#!/bin/bash
# .git/hooks/pre-commit
bandit -r . -ll -ii
if [ $? -ne 0 ]; then
echo "Bandit found security issues. Commit aborted."
exit 1
fi
# Make executable
chmod +x .git/hooks/pre-commit
# Git hook with specific files
#!/bin/bash
# Check only modified Python files
git diff --cached --name-only --diff-filter=ACM|grep '\.py
## CI/CD Integration
### GitHub Actions
```yaml
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install Bandit
run: pip install bandit[toml]
- name: Run Bandit
run: bandit -r . -f json -o bandit-repuerto.json
- name: Upload results
uses: actions/upload-artifact@v3
with:
name: bandit-repuerto
path: bandit-repuerto.json
- name: Bandit Repuerto
uses: tj-actions/bandit@v5.1
with:
opcións: "-r . -f json"
exit_zero: true
GitLab CI
# .gitlab-ci.yml
stages:
- security
bandit:
stage: security
image: python:3.9
before_script:
- pip install bandit[toml]
script:
- bandit -r . -f json -o bandit-repuerto.json
artifacts:
repuertos:
sast: bandit-repuerto.json
paths:
- bandit-repuerto.json
expire_in: 1 week
allow_failure: true
Jenkins Pipeline
// Jenkinsfile
pipeline \\{
agent any
stages \\{
stage('Security Scan') \\{
steps \\{
script \\{
sh 'pip install bandit[toml]'
sh 'bandit -r . -f json -o bandit-repuerto.json'
\\}
\\}
post \\{
always \\{
archiveArtifacts artifacts: 'bandit-repuerto.json', huella digital: true
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
repuertoDir: '.',
repuertoFiles: 'bandit-repuerto.html',
repuertoName: 'Bandit Security Repuerto'
])
\\}
\\}
\\}
\\}
\\}
Azure DevOps
# azure-pipelines.yml
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
- script: |
pip install bandit[toml]
bandit -r . -f json -o $(Agent.TempDirectory)/bandit-repuerto.json
displayName: 'Run Bandit Security Scan'
- task: PublishTestResults@2
inputs:
testResultsFiles: '$(Agent.TempDirectory)/bandit-repuerto.json'
testRunTitle: 'Bandit Security Scan'
Common vulnerabilidad Patterns
Hardcoded contraseñas (B105, B106, B107)
# BAD: Hardcoded contraseña
contraseña = "secret123"
api_clave = "abc123def456"
# GOOD: Environment variables
impuerto os
contraseña = os.environ.get('contraseña')
api_clave = os.environ.get('API_clave')
# GOOD: configuración file
impuerto configparser
config = configparser.ConfigParser()
config.read('config.ini')
contraseña = config.get('database', 'contraseña')
inyección SQL (B608)
# BAD: String formatting
query = "SELECT * FROM users WHERE id = %s" % user_id
query = f"SELECT * FROM users WHERE id = \\{user_id\\}"
# GOOD: parámetroized queries
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
inyección de comandos (B602, B605, B606, B607)
# BAD: Shell injection
impuerto os
os.system(f"ls \\{user_input\\}")
os.popen(f"grep \\{pattern\\} \\{filename\\}")
# GOOD: Subproceso with list
impuerto subproceso
subproceso.run(['ls', user_input])
subproceso.run(['grep', pattern, filename])
Insecure Random (B311)
# BAD: Predictable random
impuerto random
token = random.randint(1000, 9999)
# GOOD: Cryptographically secure
impuerto secrets
token = secrets.randbelow(9999)
secure_token = secrets.token_hex(16)
Unsafe YAML Loading (B506)
# BAD: Unsafe YAML loading
impuerto yaml
data = yaml.load(user_input)
# GOOD: Safe YAML loading
data = yaml.safe_load(user_input)
data = yaml.load(user_input, Loader=yaml.SafeLoader)
Custom Rules and Plugins
Creating Custom Tests
# custom_bandit_test.py
impuerto bandit
from bandit.core impuerto test_properties
@test_properties.test_id('B999')
@test_properties.checks('Call')
def custom_security_check(context):
"""Check for custom security pattern"""
if context.call_function_name_qual == 'dangerous_function':
return bandit.Issue(
severity=bandit.HIGH,
confidence=bandit.HIGH,
text="Use of dangerous_function detected",
lineno=context.node.lineno,
Plugin Development
# bandit_plugin.py
from bandit.core impuerto extension_loader
def load_tests():
"""Load custom tests"""
return [custom_security_check]
# Register plugin
extension_loader.MANAGER.register_plugin('custom_tests', load_tests)
Using Custom Tests
# Load custom tests
bandit -r . --tests custom_bandit_test.py
# Use plugin
bandit -r . --plugin bandit_plugin.py
Automation and Scripting
Automated Scanning Script
#!/usr/bin/env python3
# bandit_scanner.py
impuerto subproceso
impuerto json
impuerto sys
impuerto argparse
from pathlib impuerto Path
class BanditScanner:
def __init__(self, project_path, config_file=None):
self.project_path = Path(project_path)
self.config_file = config_file
self.results = \\{\\}
def run_scan(self, output_format='json', severity='MEDIUM', confidence='MEDIUM'):
"""Run Bandit scan with specified parámetros"""
cmd = [
'bandit', '-r', str(self.project_path),
'-f', output_format,
f'-l\\{self._severity_to_bandera(severity)\\}',
f'-i\\{self._confidence_to_bandera(confidence)\\}'
]
if self.config_file:
cmd.extend(['--configfile', self.config_file])
try:
result = subproceso.run(cmd, capture_output=True, text=True, check=False)
if output_format == 'json':
self.results = json.loads(result.stdout) if result.stdout else \\{\\}
else:
self.results = result.stdout
return result.returncode == 0
except subproceso.CalledprocesoError as e:
print(f"Error running Bandit: \\{e\\}")
return False
except json.JSONDecodeError as e:
print(f"Error parsing JSON output: \\{e\\}")
return False
def _severity_to_bandera(self, severity):
"""Convert severity to Bandit bandera"""
mapping = \\{'LOW': '', 'MEDIUM': 'l', 'HIGH': 'll'\\}
return mapping.get(severity.upper(), 'l')
def _confidence_to_bandera(self, confidence):
"""Convert confidence to Bandit bandera"""
mapping = \\{'LOW': 'ii', 'MEDIUM': 'i', 'HIGH': ''\\}
return mapping.get(confidence.upper(), 'i')
def get_summary(self):
"""Get scan summary"""
if not isinstance(self.results, dict):
return "No results available"
metrics = self.results.get('metrics', \\{\\})
return \\{
'total_lines': metrics.get('_totals', \\{\\}).get('loc', 0),
'total_issues': len(self.results.get('results', [])),
'high_severity': len([r for r in self.results.get('results', [])
if r.get('issue_severity') == 'HIGH']),
'medium_severity': len([r for r in self.results.get('results', [])
if r.get('issue_severity') == 'MEDIUM']),
'low_severity': len([r for r in self.results.get('results', [])
if r.get('issue_severity') == 'LOW'])
\\}
def get_issues_by_severity(self, severity='HIGH'):
"""Get issues filtered by severity"""
if not isinstance(self.results, dict):
return []
return [issue for issue in self.results.get('results', [])
if issue.get('issue_severity') == severity.upper()]
def generate_repuerto(self, output_file='bandit_repuerto.html'):
"""Generate HTML repuerto"""
cmd = [
'bandit', '-r', str(self.project_path),
'-f', 'html', '-o', output_file
]
if self.config_file:
cmd.extend(['--configfile', self.config_file])
try:
subproceso.run(cmd, check=True)
return True
except subproceso.CalledprocesoError:
return False
def save_results(self, output_file='bandit_results.json'):
"""Save results to file"""
if isinstance(self.results, dict):
with open(output_file, 'w') as f:
json.dump(self.results, f, indent=2)
else:
with open(output_file, 'w') as f:
f.write(str(self.results))
def main():
parser = argparse.ArgumentParser(Descripción='Automated Bandit Scanner')
parser.add_argument('project_path', help='Path to project to scan')
parser.add_argument('--config', help='Bandit configuración file')
parser.add_argument('--severity', default='MEDIUM',
choices=['LOW', 'MEDIUM', 'HIGH'],
help='Minimum severity level')
parser.add_argument('--confidence', default='MEDIUM',
choices=['LOW', 'MEDIUM', 'HIGH'],
help='Minimum confidence level')
parser.add_argument('--output', help='Output file for results')
parser.add_argument('--repuerto', help='Generate HTML repuerto')
args = parser.parse_args()
scanner = BanditScanner(args.project_path, args.config)
print(f"Scanning \\{args.project_path\\}...")
success = scanner.run_scan(severity=args.severity, confidence=args.confidence)
if success:
summary = scanner.get_summary()
print(f"Scan completed successfully!")
print(f"Total lines of code: \\{summary['total_lines']\\}")
print(f"Total issues found: \\{summary['total_issues']\\}")
print(f"High severity: \\{summary['high_severity']\\}")
print(f"Medium severity: \\{summary['medium_severity']\\}")
print(f"Low severity: \\{summary['low_severity']\\}")
if args.output:
scanner.save_results(args.output)
print(f"Results saved to \\{args.output\\}")
if args.repuerto:
if scanner.generate_repuerto(args.repuerto):
print(f"HTML repuerto generated: \\{args.repuerto\\}")
else:
print("Failed to generate HTML repuerto")
# Exit with error code if high severity issues found
if summary['high_severity'] >`` 0:
print("High severity issues found!")
sys.exit(1)
else:
print("Scan failed!")
sys.exit(1)
if __name__ == '__main__':
main()
Batch procesoing Script
#!/bin/bash
# batch_bandit_scan.sh
# configuración
PROJECTS_DIR="/path/to/projects"
REpuertoS_DIR="/path/to/repuertos"
DATE=$(date +%Y%m%d_%H%M%S)
# Create repuertos directory
mkdir -p "$REpuertoS_DIR"
# Function to scan project
scan_project() \{
local project_path="$1"
local project_name=$(basename "$project_path")
local repuerto_file="$REpuertoS_DIR/$\{project_name\}_$\{DATE\}.json"
local html_repuerto="$REpuertoS_DIR/$\{project_name\}_$\{DATE\}.html"
echo "Scanning $project_name..."
# Run Bandit scan
bandit -r "$project_path" -f json -o "$repuerto_file" -ll -ii
bandit -r "$project_path" -f html -o "$html_repuerto" -ll -ii
# Check for high severity issues
| high_issues=$(jq '.results | map(select(.issue_severity == "HIGH")) | length' "$repuerto_file") |
if [ "$high_issues" -gt 0 ]; then
echo "WARNING: $project_name has $high_issues high severity issues!"
echo "$project_name" >> "$REpuertoS_DIR/high_severity_projects.txt"
fi
echo "Scan completed for $project_name"
\}
# Scan all Python projects
find "$PROJECTS_DIR" -name "*.py" -type f|while read -r file; do
project_dir=$(dirname "$file")
if [ ! -f "$project_dir/.bandit_scanned" ]; then
scan_project "$project_dir"
touch "$project_dir/.bandit_scanned"
fi
done
echo "Batch scanning completed. Repuertos saved to $REpuertoS_DIR"
Integration with IDEs
VS Code Integration
// .vscode/settings.json
\{
"python.linting.banditEnabled": true,
"python.linting.banditArgs": [
"--severity-level", "medium",
"--confidence-level", "medium"
],
"python.linting.enabled": true
\}
PyCharm Integration
# External tool configuración
# Program: bandit
# Arguments: -r $FileDir$ -f json
# Working directory: $ProjectFileDir$
Vim/Neovim Integration
" .vimrc or init.vim
" Bandit integration with ALE
let g:ale_linters = \{
\ 'python': ['bandit', 'flake8', 'pylint'],
\\}
let g:ale_python_bandit_opcións = '-ll -ii'
Best Practices
configuración Management
# .bandit - Comprehensive configuración
tests: ['B101', 'B102', 'B103', 'B104', 'B105', 'B106', 'B107', 'B108', 'B110', 'B112', 'B201', 'B301', 'B302', 'B303', 'B304', 'B305', 'B306', 'B307', 'B308', 'B309', 'B310', 'B311', 'B312', 'B313', 'B314', 'B315', 'B316', 'B317', 'B318', 'B319', 'B320', 'B321', 'B322', 'B323', 'B324', 'B325', 'B401', 'B402', 'B403', 'B404', 'B405', 'B406', 'B407', 'B408', 'B409', 'B410', 'B411', 'B412', 'B413', 'B501', 'B502', 'B503', 'B504', 'B505', 'B506', 'B507', 'B601', 'B602', 'B603', 'B604', 'B605', 'B606', 'B607', 'B608', 'B609', 'B610', 'B611', 'B701', 'B702', 'B703']
skips: ['B101'] # Skip assert_used in test files
exclude_dirs: [
'*/tests/*',
'*/test/*',
'*/.venv/*',
'*/venv/*',
'*/.env/*',
'*/env/*',
'*/migrations/*',
'*/node_modules/*',
'*/.git/*'
]
# Severity: LOW, MEDIUM, HIGH
severity: MEDIUM
# Confidence: LOW, MEDIUM, HIGH
confidence: MEDIUM
False Positive Management
# Inline comments to suppress warnings
contraseña = "default" # nosec B105
# Suppress specific test
impuerto subproceso
subproceso.call(shell_comando, shell=True) # nosec B602
# Suppress multiple tests
eval(user_input) # nosec B307,B102
Team Workflow Integration
# Pre-commit configuración (.pre-commit-config.yaml)
repos:
- repo: https://github.com/PyCQA/bandit
rev: '1.7.5'
hooks:
- id: bandit
args: ['-ll', '-ii']
exclude: ^tests/
# Make file integration
.PHONY: security-scan
security-scan:
bandit -r . -ll -ii -f json -o security-repuerto.json
@echo "Security scan completed. Check security-repuerto.json for results."
.PHONY: security-check
security-check:
bandit -r . -ll -ii
@if [ $? -ne 0 ]; then \
echo "Security issues found. Please review and fix."; \
exit 1; \
fi
solución de problemas
Common Issues
# Issue: ImpuertoError when running Bandit
# Solution: Ensure proper Python environment
python -m pip install --upgrade bandit
# Issue: configuración not being read
# Solution: Verify configuración file location and sintaxis
bandit --help-config
# Issue: Too many false positives
# Solution: Tune configuración and use suppressions
bandit -r . --skip B101,B601 -ll -ii
# Issue: Performance issues with large codebases
# Solution: Exclude unnecessary directories
bandit -r . --exclude "*/venv/*,*/node_modules/*,*/.git/*"
# Issue: Integration with CI/CD failing
# Solution: Use appropriate exit codes and error handling
| bandit -r . -ll -ii | | true # Continue on errors |
Performance Optimization
# Parallel procesoing (if available)
bandit -r . --procesoes 4
# Exclude large directories
bandit -r . --exclude "*/venv/*,*/env/*,*/node_modules/*,*/.git/*,*/migrations/*"
# Use specific tests only
bandit -r . --tests B201,B301,B401,B501
# Limit recursion depth
| find . -name "*.py" -not -path "*/venv/*" | head -100 | xargs bandit |
Debugging
# Verbose output
bandit -v -r .
# Debug mode
bandit -d -r .
# Show skipped files
bandit -r . --verbose
# Test specific file with all details
bandit -v -ll -iii specific_file.py
Resources
- Bandit Official documentación
- Bandit GitHub Repository
- Python Security Best Practices
- OWASP Python Security
- PyCQA Tools
This hoja de trucos provides comprehensive guidance for using Bandit to identify security vulnerabilities in Python code. Always combine static analysis with other security testing methods for comprehensive coverage.
|xargs bandit -ll -ii ```
CI/CD Integration
GitHub Actions
CODE_BLOCK_13
GitLab CI
CODE_BLOCK_14
Jenkins Pipeline
CODE_BLOCK_15
Azure DevOps
CODE_BLOCK_16
Common vulnerabilidad Patterns
Hardcoded contraseñas (B105, B106, B107)
CODE_BLOCK_17
inyección SQL (B608)
CODE_BLOCK_18
inyección de comandos (B602, B605, B606, B607)
CODE_BLOCK_19
Insecure Random (B311)
CODE_BLOCK_20
Unsafe YAML Loading (B506)
CODE_BLOCK_21
Custom Rules and Plugins
Creating Custom Tests
CODE_BLOCK_22
Plugin Development
CODE_BLOCK_23
Using Custom Tests
CODE_BLOCK_24
Automation and Scripting
Automated Scanning Script
CODE_BLOCK_25
Batch procesoing Script
CODE_BLOCK_26
Integration with IDEs
VS Code Integration
CODE_BLOCK_27
PyCharm Integration
CODE_BLOCK_28
Vim/Neovim Integration
CODE_BLOCK_29
Best Practices
configuración Management
CODE_BLOCK_30
False Positive Management
CODE_BLOCK_31
Team Workflow Integration
CODE_BLOCK_32
solución de problemas
Common Issues
CODE_BLOCK_33
Performance Optimization
CODE_BLOCK_34
Debugging
CODE_BLOCK_35
Resources
- Bandit Official documentación
- Bandit GitHub Repository
- Python Security Best Practices
- OWASP Python Security
- PyCQA Tools
This hoja de trucos provides comprehensive guidance for using Bandit to identify security vulnerabilities in Python code. Always combine static analysis with other security testing methods for comprehensive coverage.