Bandit

LanguagePython
Kindtool
Readiness🟢
Detector conformance🟡
Checked2026-09-08, version 1.9.4

Bandit is a security-oriented static analyser for Python from the PyCQA family. It walks the AST and runs a set of test plugins, each with an identifier such as B101, reporting a severity and a confidence with each issue. In a pipeline it runs alongside the general linters as the check for the hazard classes that are specifically about security: shell injection, weak hashes, hard-coded credentials and the like.

How it is conformant

Bandit’s checks are themselves plugins, and a project can add its own through the same route. Writing tests documents the bandit.plugins entry point, the @checks decorator that selects the node types a test inspects, and the @test_id decorator by which the author chooses the identifier once, and that identifier is printed with every issue in the text and JSON formats (4.1, 4.3). The same guide’s process of writing an example file in examples/ and running Bandit against it is a fixture-based proof, and -t B101 restricts a run to a single test (configuration), which meets 4.2 and 5.4. Bandit accepts individual files as targets (manual page) and prints each issue with its identifier and a terse description in the command’s own output (5.1, 5.2, 5.3). Every bundled test ships its documentation as the plugin’s own docstring, so the two are versioned together (6.3). Clause 7.1 holds: # nosec is the inline route and the --ignore-nosec flag makes Bandit disregard every such comment, whilst the -b baseline is a route only used when the flag is passed, so a project can forbid both.

How it is not conformant

Section 6 fails on resolution. There is no command that resolves a printed B identifier to its documentation from the installed copy (6.1), and the per-test pages are on the website (6.2), so the docstring that does ship is reachable only by reading the plugin source. Clause 7.2 is not met: the documentation says a reason on # nosec is “good practice” rather than required (configuration), and a baseline entry carries none. There is no rule over the rules that fails a plugin without a @test_id (4.4), and no release gate over the bundled documentation is documented (6.4, not verified).

Clause by clause

Document Clause Result Evidence
Detector 4.1 Yes bandit.plugins entry point
Detector 4.2 Yes Example file plus -t <id> on a single target
Detector 4.3 Yes @test_id chosen by the author and printed
Detector 4.4 No None
Detector 5.1 Yes bandit <targets> locally (manual)
Detector 5.2 Yes Individual files accepted
Detector 5.3 Yes Issues with ids printed to stdout
Detector 5.4 Yes -t selects a single test
Detector 6.1 No No resolver keyed on the id
Detector 6.2 No Per-test pages on the website
Detector 6.3 Yes Docstring ships with the plugin
Detector 6.4 Not verified No release gate documented
Detector 7.1 Yes --ignore-nosec disables the inline route; -b baseline is opt-in (manual)
Detector 7.2 No Reason optional (configuration)

Notes for a practitioner

Where the class is a security hazard, write it as a Bandit plugin with a @test_id in a range the project reserves, keep the example file as the retained fixture, and prove it with bandit -t <id> example.py. Run the enforced invocation with --ignore-nosec so inline suppressions do nothing, and put every accepted finding in the configuration skips list with a comment naming the hazard and scope.