Bandit

PyCQA

Python security linter — the baseline SAST pass, and a lesson in what a baseline misses.

Application & Supply Chain Free & Open Source CLI Practiced in lab Cross-platform

Cyber Kill Chain & Defender Lifecycle

Attacker — Kill Chain
1 Reconnaissance
2 Weaponization
3 Delivery
4 Exploitation
5 Installation
6 Command & Control
7 Actions on Objectives
Defender — IR Lifecycle
8 Preparation
9 Detection & Analysis
10 Containment, Eradication & Recovery
11 Post-Incident Activity

Description

Bandit walks the Python AST looking for known-dangerous constructs: subprocess with shell=True, yaml.load without a safe loader, assert used for validation, hardcoded passwords.

It is the first scan to run and the cheapest to keep. The series uses it as the honest baseline in every lab, including where it comes back clean on genuinely vulnerable code — Bandit knows Python, not Django, so an ORM .raw() call built with an f-string is invisible to it. That gap is exactly where the custom Semgrep rules start.

Use cases

  • First-pass SAST on any Python codebase
  • Pre-commit hook against dangerous stdlib calls
  • Establishing what a generic scanner does and does not catch

Example

bandit -r . -x ./venv,./tests -ll

Related blog posts