Python static analysis
Python L1 coordinates four skills across safety, ergonomics, types, and deep SAST.
| Skill | Axis | One-liner |
|---|---|---|
| bandit | Security | Injection / hard-coded secrets / unsafe APIs |
| ruff | Style + composites | Ultrafast lint (Rust core, 900+ rules) replacing Flake8/Pylint/isort/Black |
| mypy | Typing | PEP 484 semantics |
| semgrep | SAST | Custom YAML semantics, multi-language engine |
bandit
Role: Python-specific security scanning.
| Class | Examples |
|---|---|
| SQL injection | String-built SQL |
| Command injection | subprocess(shell=True) |
| Hard-coded secrets | api_key = "sk-..." |
| Dangerous builtins | Unsafe pickle, eval, naked yaml.load |
| Weak crypto | MD5/SHA1 for passwords |
Triggers: “Python security sweep”, “bandit run”, “SQLi risk?”
ruff
Role: Fastest lint in the ecosystem (Rust-backed), consolidating Flake8 + Pylint + isort + Black.
- 900+ rules
- Seconds on large repos
- Rule families:
E/W/F/I/N, … - Can format like
black
Triggers: “Python lint”, “style gate”, “run ruff”
mypy
Role: Static typing per PEP 484.
| Issue | Example |
|---|---|
| Type mismatch | def f(x: int) -> str: return x |
| Missing annotations | Params / returns untyped |
| Bad assignments | a: list[int] = ["x"] |
| Optional mishandling | Skipping None checks |
Triggers: “type check CI”, “mypy failures”, “Optional misuse”
semgrep
Role: Semantic SAST spanning many languages via YAML rules.
| Class | Detail |
|---|---|
| Injection classes | Common OWASP patterns |
| Secret leakage | Hard-coded creds |
| Taint tracking | Untrusted input → dangerous sinks |
| Custom rules | Org-specific policies |
vs bandit: bandit = Python AST heuristics; semgrep = programmable semantic engine for Python/JS/Java/Go/Ruby/…
Triggers: “SAST pass”, “author custom rule”, “polyglot security sweep”
Recommended bundle
Emit CodeEvidence → hand to code-review.