What it does
Bandit parses each Python file into an abstract syntax tree and walks the
tree running a set of plugins against specific node types. A plugin might
fire on a call to subprocess.Popen with shell=True, on yaml.load
without a safe loader, on pickle.loads, on hashlib.md5, or on a hardcoded
password assigned to a variable whose name looks like a credential. Each hit
becomes an issue with a severity rating, a confidence rating, and a link to
the relevant plugin documentation.
The important detail is that this is syntactic, not data flow. Bandit does
not trace a value from an HTTP request parameter into a SQL string. It
recognizes shapes: this function, called this way, is usually a problem. That
design keeps it fast enough to run on every commit, at the cost of missing
anything that depends on where a value came from. Findings can be suppressed
inline with a # nosec comment, and the plugin set can be filtered by test
ID or severity threshold.
Where it fits
Bandit belongs on the developer laptop and in the pull request. It is commonly wired in as a pre-commit hook or a CI job that fails the build above a severity and confidence floor. Developers usually own it rather than the security team, which is the right arrangement given the volume of low severity noise it produces. Nothing needs to be true beforehand except that the code parses under the Python version Bandit is running on.
Strengths
- Runs in seconds on most repositories with no build step, no compilation, and no dependency resolution.
- The plugin model is small enough that writing a custom check for an in-house dangerous helper is a realistic afternoon of work.
- Severity and confidence are reported separately, which lets you gate on high severity plus high confidence and triage the rest asynchronously.
Limitations
- No taint tracking. Injection flaws that depend on untrusted input reaching a sink are largely invisible unless the sink call itself looks wrong.
- High false positive rate on patterns like
assertusage,randomfor non-cryptographic purposes, and binding to all interfaces, all of which are fine in plenty of contexts. - Python only, and framework-unaware: it does not understand Django, Flask or FastAPI routing, so it cannot tell an internet-facing handler from a test fixture.
Who it suits
A good default for any Python team that wants a security baseline in CI without a procurement conversation. It is the wrong choice if you need interprocedural analysis or defensible audit evidence. Treat it as a linter with a security flavor and pair it with a taint-aware scanner when the application handles untrusted input.
Used Bandit? Recommend it under your own name and title.
Recommend this tool