comparison

Semgrep vs Bandit: Python Security Scanning Compared (2026)

Semgrep vs Bandit for Python security scanning - custom rules vs zero-config, taint tracking vs AST patterns, and which tool Python developers should use.

Published:

Last Updated:

Quick Verdict

Semgrep security scanning tool homepage screenshot
Semgrep homepage

Semgrep and Bandit both scan Python code for security vulnerabilities, but they are fundamentally different tools with different strengths, scopes, and intended audiences. Bandit is a focused, zero-configuration Python security scanner with around 80 built-in test plugins, no learning curve, and instant results. Semgrep is a programmable, multi-language security platform with cross-file taint tracking, custom YAML-based rule authoring, AI-powered triage, and a registry of 20,000+ Pro rules spanning 30+ languages.

The comparison matters because Python developers often encounter both tools when building a security scanning pipeline - Bandit appears in virtually every Python security checklist, and Semgrep is increasingly the recommendation for teams that need deeper analysis. Understanding when each is appropriate, and whether you need one or both, is essential for building a security program that actually catches vulnerabilities without overwhelming developers with noise.

Choose Bandit alone if: you have a small Python project, want zero-configuration security scanning, do not need custom rules, and are running quick pre-commit or CI checks to catch obvious issues like hardcoded passwords and weak crypto.

Choose Semgrep alone if: you work across multiple languages, need cross-file taint tracking, want framework-specific rules for Django or Flask, need custom organization-specific rules, or want AI-powered triage to reduce false positive noise.

The best setup for most Python teams: Run Bandit in pre-commit hooks for instant, zero-config checks, and run Semgrep in CI/CD for deeper cross-file analysis, taint tracking, and custom rules. The two tools are complementary, not redundant - their detection approaches overlap minimally and each catches issues the other misses.

At-a-Glance Comparison

CategorySemgrepBandit
TypeMulti-language programmable SAST scannerPython-only AST security scanner
Languages30+ (Python, JS, TS, Java, Go, Ruby, C, C++, etc.)Python only
Built-in rules2,800+ community / 20,000+ Pro~80 test plugins
Custom rulesYes - YAML, mirrors target language syntaxNo - requires writing Python plugins
Cross-file analysisYes (Pro engine, paid tier)No - single file only
Taint trackingYes (Pro engine)No
Framework-aware rulesYes - Django, Flask, FastAPI, and moreLimited - some Flask/Django patterns
AI triageYes - Semgrep Assistant (20-40% noise reduction)No
CI/CD integrationNative - runs in any CI, GitHub Actions built-inYes - pip install, exit codes
Pre-commit integrationYesYes - official pre-commit hook
IDE integrationVS Code extensionThird-party VS Code extensions
Pricing (free tier)OSS CLI free; full platform free for 10 contributorsCompletely free and open source
Paid pricing$35/contributor/month (Team tier)No paid tier - always free
Maintained bySemgrep, Inc. (commercial)PyCQA (community)
LicenseLGPL-2.1 (OSS) / commercial (Pro)Apache 2.0
False positive handlingAI triage (paid) + reachability analysisManual suppression via # nosec comments
SCA / dependency scanningYes - Semgrep Supply Chain (Pro)No
Secrets detectionYes - Semgrep Secrets (Pro)Partial - basic hardcoded password checks
IaC scanningYes - Terraform, K8s, DockerfilesNo

What Is Bandit?

Bandit security scanning tool features overview screenshot
Bandit features overview

Bandit is a Python security linting tool created by the OpenStack Security Project and now maintained by PyCQA (Python Code Quality Authority). It was designed for one purpose: scan Python source code for common security issues quickly and with zero configuration. The name Bandit reflects its adversarial framing - it looks for the “bad patterns” that security engineers know from experience lead to vulnerabilities.

Bandit works by building an abstract syntax tree (AST) from Python source code and running each file through a series of test plugins. Each plugin checks for a specific category of security issue. The test plugins are organized into a straightforward taxonomy:

  • B1xx - Misc: Assert statements in security contexts (B101), exec() usage (B102), hardcoded Python 2/3 compatibility issues
  • B2xx - Application: Configuration issues, debug mode flags, exec-related patterns
  • B3xx - Cryptography: Weak ciphers (B303), MD5 (B303), SHA1 (B303), use of random vs secrets (B311)
  • B4xx - Imports: Detection of dangerous module imports (subprocess, telnet, FTP)
  • B5xx - TBD: Various injection and code execution categories
  • B6xx - Injection: SQL injection patterns (B608), XSS markers, shell injection
  • B7xx - Execution: Subprocess calls with shell=True (B602, B603), os.system (B605)

The appeal is simplicity. You install Bandit with pip install bandit, run bandit -r src/, and immediately see a list of security findings. No configuration file required, no account, no API keys, no cloud connection. The output categorizes findings by severity (LOW, MEDIUM, HIGH) and confidence (LOW, MEDIUM, HIGH), allowing teams to prioritize quickly. For a 10,000-line Python project, Bandit typically completes in under 3 seconds.

Bandit’s limitation is equally clear: it analyzes each file in isolation. It has no awareness of how data flows from one file to another. When user input arrives in a view function, travels through a service layer, and reaches a database query in a data access object - Bandit sees three separate files, not a connected vulnerability. This single-file limitation means Bandit misses the cross-file vulnerabilities that characterize real-world injection attacks in well-structured Python applications.

False positive management in Bandit is handled through inline # nosec comments. If Bandit flags a line that you have determined is not a vulnerability - for example, a subprocess call with a hardcoded, non-user-controlled command - you add # nosec B603 to that line to suppress the finding. This works but does not scale well on large codebases where hundreds of nosec comments become their own maintenance burden.

What Is Semgrep?

Semgrep is a multi-language, programmable static analysis engine built for application security. Originally developed at Facebook and now maintained by Semgrep, Inc. (formerly r2c), it takes a fundamentally different approach from Bandit: rather than shipping a fixed set of tests, Semgrep provides a rule engine that lets you write security rules using syntax that mirrors the source code being scanned.

Semgrep operates across three tiers:

Community Edition (free, open source): The core Semgrep engine under LGPL-2.1. Single-file and single-function analysis. Includes the community registry with 2,800+ rules. Runs as a CLI with no login required. This is the tier most often compared directly to Bandit.

Team tier ($35/contributor/month, free for up to 10 contributors): The full Semgrep AppSec Platform. Adds cross-file dataflow analysis, the Pro engine with taint tracking, Semgrep Assistant (AI-powered triage), 20,000+ Pro rules, centralized dashboards, Semgrep Supply Chain (SCA with reachability), and Semgrep Secrets (credential detection with validation).

Enterprise (custom pricing): SSO/SAML, custom deployment options, advanced compliance reporting, dedicated support, and SLAs.

For Python specifically, Semgrep’s rule registry includes a substantial collection covering Python-specific vulnerability patterns, OWASP Top 10 categories for Python web applications, and framework-specific rules for Django, Flask, and FastAPI. The p/python ruleset is the general-purpose Python security ruleset, while p/django and p/flask provide framework-specific coverage. These rulesets are maintained by Semgrep’s security research team and updated regularly as new vulnerability patterns emerge.

Semgrep’s defining advantage over Bandit is the taint-tracking mode in the Pro engine. A taint-tracking rule specifies sources (where untrusted data enters the system) and sinks (dangerous operations that must not receive untrusted data), and Semgrep traces data flow between them across files and function calls. This is the mechanism that catches real-world injection vulnerabilities in well-architected Python applications - the ones where a Django request parameter flows through three service functions before reaching a database query.

For a detailed review of Semgrep’s capabilities, pricing, and positioning, see our Semgrep review.

Feature-by-Feature Breakdown

Python Security Rule Coverage

This is the dimension where the tools appear most similar but are actually quite different in depth.

Bandit covers the standard checklist of Python security anti-patterns. Its approximately 80 built-in test plugins address the most common mistakes: using weak cryptographic algorithms (MD5, SHA1, DES), hardcoding passwords in source code, calling subprocess with shell=True, using eval() or exec() with dynamic content, binding servers to all network interfaces (0.0.0.0), using assert statements for security checks (which get stripped in optimized Python), using the random module for security-sensitive operations instead of secrets, and flagging insecure protocol imports like telnet and FTP. These patterns appear consistently in Python security audits and Bandit catches them reliably.

Semgrep’s Python coverage extends into framework-specific and cross-file patterns. Beyond the standard checklist that Bandit covers, Semgrep adds rules for Django-specific vulnerabilities (missing CSRF protection, insecure mark_safe() usage, raw SQL via extra() and RawSQL()), Flask-specific issues (debug mode in production, insecure session secrets, missing login_required), FastAPI patterns (missing authentication dependencies, unvalidated path parameters), and SQLAlchemy misuse (raw string queries bypassing ORM protections). The framework-specific coverage matters because most real Python applications use a framework, and vulnerabilities often appear at the interaction between application code and framework APIs.

The critical gap for Bandit appears in real-world injection detection. Consider a typical Django application:

# views.py
def search_products(request):
    query = request.GET.get('q', '')
    results = product_service.search(query)  # user input passed here
    return JsonResponse({'results': results})

# services.py
def search(query_string):
    return db_helper.raw_query(query_string)  # taint reaches here

# db.py
def raw_query(sql_fragment):
    with connection.cursor() as cursor:
        cursor.execute(f"SELECT * FROM products WHERE name LIKE '%{sql_fragment}%'")  # sink
        return cursor.fetchall()

Bandit sees three separate files. In views.py, it sees user input assigned to a variable. In services.py, it sees a function call. In db.py, it sees a cursor.execute() call with a formatted string - and it will flag this as B608. But it cannot connect the dots to confirm that sql_fragment comes from user input in views.py. The flagged issue in db.py may produce a false positive if the analyst does not manually trace the flow, or worse, Bandit may miss the vulnerability entirely if the formatting pattern is less obvious. Semgrep’s taint-tracking mode traces the flow from request.GET.get() (source) across all three files to cursor.execute() (sink), confirming the injection vulnerability and reducing false positive ambiguity.

Custom Rule Authoring

This is one of the most meaningful practical differences between the two tools, especially for organizations with specific security requirements.

Bandit does not support user-facing custom rule authoring in the conventional sense. Extending Bandit requires writing Python test plugins that hook into Bandit’s visitor pattern. This is not a YAML configuration file - it is actual Python code that must be packaged and installed correctly. The development process involves understanding Bandit’s plugin architecture, AST node types, and how to register new visitors. For most development teams, this is inaccessible without dedicated security engineering resources.

Semgrep’s custom rule system is the most developer-accessible in the SAST industry. Rules are written in YAML using patterns that mirror the syntax of the target language. A Semgrep rule to detect a custom internal security anti-pattern in a Django project looks like this:

rules:
  - id: django-unsafe-render
    patterns:
      - pattern: |
          from django.utils.safestring import mark_safe
          ...
          mark_safe($USER_INPUT)
      - pattern-not: |
          mark_safe("...")
    message: >
      mark_safe() called with a non-literal string.
      This can create XSS vulnerabilities if $USER_INPUT contains user-controlled data.
    severity: ERROR
    languages: [python]

Any Python developer who can read Python can read this rule. Writing it takes 10-20 minutes for a developer encountering Semgrep for the first time. Deploying it across all repositories takes seconds by adding it to the organization’s shared Semgrep policy. This is transformative for organizations that need to encode internal security standards - for example, ensuring that all internal API clients use the organization’s authentication wrapper, or that all file uploads go through the organization’s virus-scanning middleware before being stored.

The Semgrep playground at semgrep.dev lets developers write and test rules interactively against sample code before deploying them, further reducing the barrier to rule authoring.

Cross-File Analysis and Taint Tracking

This is the most technically significant difference between the two tools, and it determines whether your security scanning catches real-world vulnerabilities in production codebases.

Bandit’s analysis is strictly single-file. When Bandit analyzes a Python file, it builds an AST of that file and checks it against its test plugins. It has no knowledge of what other files do, what values functions return when called from other modules, or how data flows between components. This is a fundamental architectural limitation, not a configuration option. No version of Bandit and no configuration change will enable cross-file analysis.

Semgrep’s Pro engine performs cross-file, cross-function taint analysis. The taint-tracking mode traces data from sources to sinks across the entire codebase. Sources include common Python entry points like Django request objects, Flask request objects, FastAPI path and query parameters, stdin, environment variables, and external API responses. Sinks include dangerous operations: SQL query execution, subprocess calls, eval(), exec(), pickle.loads(), yaml.load(), os.system(), and similar. Semgrep traces data from any configured source to any configured sink, following it through variable assignments, function calls, method chains, and module boundaries.

The practical impact was measured in independent testing: Semgrep’s Pro engine detected 72-75% of vulnerabilities in test suites compared to 44-48% for single-file analysis approaches like Bandit. This 24-27 percentage point gap represents real vulnerabilities that single-file scanning structurally cannot find.

For Python applications that follow standard architectural patterns - views/controllers calling services calling data access objects - cross-file analysis is not optional if you want comprehensive injection detection. It is the minimum requirement for catching vulnerabilities in real code.

False Positive Rates and Noise Management

False positives are the single biggest obstacle to actually using security scanning tools in practice. A tool that generates dozens of false positives on every PR causes developers to ignore all findings, defeating the purpose of scanning.

Bandit’s false positive management is manual and comment-based. The # nosec annotation suppresses findings on a per-line or per-issue basis. This works for small codebases but becomes unwieldy as codebases grow. The bigger issue is that Bandit’s lack of context awareness produces structurally unavoidable false positives in certain categories. The B608 rule (SQL injection via string formatting) will flag any f-string or % formatting in a string that looks like SQL - even if the formatted value comes from an internal constant and not user input. These false positives require manual # nosec suppression or understanding the rule’s limitations and accepting the noise.

Semgrep addresses false positives at multiple levels. The taint-tracking mode reduces false positives by understanding data origins - it distinguishes between user-controlled data and internally-controlled data, only flagging when user input actually reaches a dangerous sink. At the paid tier, Semgrep Assistant uses AI to classify findings, assess exploitability, and provide confidence ratings. Semgrep reports that Assistant reduces false positive noise by 20-40% out of the box, improving to higher reductions as the system learns organization-specific patterns through the Assistant Memories feature. Reachability analysis in Semgrep Supply Chain applies the same principle to dependency vulnerabilities: instead of flagging every CVE in every dependency, it determines whether the vulnerable code path is actually called by your application.

The net result is that Semgrep’s paid tier generates fewer findings that require human review, and the findings it does surface are more likely to represent real vulnerabilities. Bandit generates more findings per unit of codebase but a higher proportion of them require manual triage to determine legitimacy.

CI/CD Integration

Both tools integrate into CI/CD pipelines, but the setup experience differs.

Bandit integrates into any Python CI pipeline trivially. Because it is a pip-installable Python package with standard CLI behavior (non-zero exit code on findings), it works in any CI system that can run Python commands. A GitHub Actions integration looks like:

- name: Run Bandit security scan
  run: |
    pip install bandit
    bandit -r src/ -ll -x tests/

The -ll flag sets the minimum severity level to MEDIUM (skipping LOW findings that tend to be noisy), and -x tests/ excludes the test directory. Pre-commit integration is also available through the official bandit pre-commit hook.

Semgrep provides a native GitHub Actions integration that enables PR-level analysis with inline comments. The setup is similarly simple:

- uses: semgrep/semgrep-action@v1
  with:
    config: >-
      p/python
      p/django
      p/owasp-top-ten

For GitLab, Bitbucket, Jenkins, CircleCI, and other CI systems, Semgrep provides a Docker image and CLI that run in any environment. The diff-aware scanning mode means Semgrep only analyzes changed files on PRs, keeping scan times short even on large codebases.

Semgrep’s cloud platform adds PR comment posting, finding management dashboards, and policy enforcement beyond what Bandit’s CI integration provides. Bandit exits with a non-zero code on findings but does not post inline PR comments or maintain a historical findings database without third-party tooling.

Developer Experience and Onboarding

For teams evaluating these tools, the setup time and learning curve matter enormously.

Bandit has essentially zero onboarding. Install it, run it, read the output. The findings are self-explanatory - a B602 finding tells you which line has a subprocess call with shell=True and why that is dangerous. There is no configuration to learn, no ruleset to curate, no account to create. A developer who has never heard of Bandit before can be running it on their codebase within 60 seconds.

Semgrep requires slightly more setup but remains developer-friendly. Installing the CLI takes seconds (pip install semgrep or brew install semgrep). Running the community Python ruleset takes one command (semgrep scan --config p/python). Where Semgrep’s learning curve appears is in custom rule authoring - writing effective taint-tracking rules requires understanding sources, sinks, and Semgrep’s rule syntax. However, for teams using only the pre-built rulesets, the learning curve is minimal.

The Semgrep platform adds a web dashboard, policy management, and AI triage on top of the CLI. For teams adopting the full platform, there is an onboarding process involving connecting Git repositories and configuring rulesets. This takes hours rather than minutes but delivers significantly more capability.

Pricing Comparison

Semgrep security scanning tool pricing page screenshot
Semgrep pricing page

Bandit Pricing

Bandit is completely free. There is no paid tier, no enterprise edition, no usage limits, and no account required. It is maintained by PyCQA as an open-source community project under the Apache 2.0 license. The total cost of Bandit is zero dollars, forever, for any team size, any codebase size, and any commercial usage.

The only “cost” of Bandit is the developer time required to manage false positives (nosec comments), configure baseline files to track new issues over time, and build any reporting or dashboarding on top of its JSON or text output.

Semgrep Pricing

TierPriceWhat You Get
Community Edition (OSS)FreeOpen-source engine, 2,800+ community rules, single-file analysis, CLI and CI/CD, no login required
Team$35/contributor/month (free for first 10 contributors)Cross-file taint analysis, 20,000+ Pro rules, Semgrep Assistant (AI triage), Semgrep Supply Chain (SCA with reachability), Semgrep Secrets, centralized dashboard
EnterpriseCustomEverything in Team plus SSO/SAML, custom deployment, advanced compliance reporting, dedicated support, SLA guarantees

For full pricing details, see our Semgrep pricing guide.

Cost Comparison at Different Team Sizes

Team SizeBanditSemgrep CommunitySemgrep Team
1-5 developers$0$0$0 (free for 10 contributors)
10 developers$0$0$0 (free for 10 contributors)
20 developers$0$0$7,000/year
50 developers$0$0$21,000/year
100 developers$0$0$42,000/year

The cost picture makes the decision clear for small teams: both tools are free. For teams up to 10 contributors, the Semgrep full platform (including cross-file analysis, AI triage, and 20,000+ Pro rules) costs exactly the same as Bandit - zero. This is one of the most valuable free tiers in application security tooling, and Python teams under 10 contributors should almost certainly use both tools together at no cost.

For teams beyond 10 contributors where Semgrep’s paid tier is needed, the incremental cost represents significant capability: cross-file taint analysis, AI-powered triage, SCA with reachability, and secrets validation. Whether this capability justifies $35/contributor/month depends on the security requirements of the application and the cost of missing the vulnerabilities that single-file scanning cannot catch.

Alternatives to Consider

If neither Bandit nor Semgrep alone fits your needs, several alternatives and complementary tools are worth evaluating.

SonarQube covers both code quality and security for Python applications. Its Community Build is free and self-hosted, and SonarQube Cloud offers a free tier for up to 50K lines of code. SonarQube’s Python analysis includes security rules covering OWASP Top 10, bugs, code smells, and duplication detection in a single platform. For teams that need both security scanning and code quality metrics in one tool, SonarQube is the strongest free option. See our Semgrep vs SonarQube comparison for a detailed breakdown.

Snyk Code is a developer-first security scanner with strong Python coverage and a developer-friendly IDE integration. Snyk’s DeepCode AI engine performs interfile data flow analysis and is particularly strong at detecting injection vulnerabilities. Snyk pairs naturally with Snyk Open Source for SCA - flagging CVEs in your Python dependencies. The combination of Snyk Code (SAST) and Snyk Open Source (SCA) provides comprehensive Python security coverage. See our Snyk vs Semgrep comparison for a detailed breakdown.

Ruff is not a security scanner but is the recommended Python linter for code quality (replacing Flake8, isort, and many Pylint checks at 10-100x the speed). For teams assembling a Python toolchain, Ruff handles code quality while Bandit or Semgrep handles security. They are complementary, not competing.

CodeAnt AI is a broader platform that combines AI code review, SAST, secrets detection, and DORA metrics in a single tool at $24-40/user/month. For Python teams that want to consolidate their code review and security scanning into one vendor rather than running separate tools, CodeAnt AI provides a bundled alternative. It is not as deep as Semgrep for custom security rules, but for teams that primarily need standard security checks alongside AI-powered PR review, it reduces the number of tools to manage.

Pylint with security plugins provides an alternative for teams already using Pylint. While Pylint is primarily a code quality tool, plugins like pylint-django add Django-specific security checks. This is not a replacement for dedicated security scanning but can catch some security-relevant issues within an existing Pylint workflow.

For a comprehensive view of the Python security tooling landscape, see our best code review tools for Python guide and our best SAST tools comparison.

Use Cases - When to Choose Each

When Bandit Is the Right Choice

Python-only projects that need zero-configuration security scanning. If your entire codebase is Python and you want to catch the standard security anti-patterns (weak crypto, hardcoded passwords, shell injection via subprocess) without any setup overhead, Bandit is the simplest possible choice. Install it, run it, and you have baseline security coverage in under two minutes.

Pre-commit hooks where speed is critical. Bandit’s single-file analysis is extremely fast - typically under 5 seconds on a medium Python project. This makes it ideal for pre-commit hooks where developers expect near-instantaneous feedback. Semgrep’s Community Edition is also fast but adds some overhead from multi-language support. Bandit’s Python-specific focus keeps it lean.

Teams on zero security tooling budget. Bandit is free, always will be, and requires no account, no cloud connection, and no vendor relationship. For open-source Python projects, student projects, or teams with genuinely zero budget for security tooling, Bandit provides meaningful baseline coverage at no cost.

CI/CD pipelines that already have Python available. Bandit installs via pip, which means it works in any environment that already has Python configured. No additional runtimes, no Docker pulls, no binary downloads. If your CI environment runs pip install -r requirements.txt, adding pip install bandit requires zero infrastructure changes.

Catching quick wins in existing codebases. When inheriting a Python codebase or starting a security audit, running Bandit is one of the first steps. It quickly surfaces the low-hanging fruit - the hardcoded API keys in the code, the subprocess calls with shell=True, the use of MD5 for password hashing. These findings are high-confidence and easy to prioritize, making Bandit a practical starting point before deeper analysis.

When Semgrep Is the Right Choice

Applications with real injection risk from user input. If your Python application takes user input and processes it through multiple layers before reaching a database, file system, or subprocess - which describes most web applications - you need cross-file taint analysis to catch injection vulnerabilities. Semgrep’s Pro engine is the tool for this. Bandit will miss the majority of these vulnerabilities by design.

Django, Flask, or FastAPI web applications. Semgrep’s framework-specific rulesets provide Python web application coverage that Bandit cannot match. Django-specific rules for CSRF protection, ORM misuse, and template injection, Flask rules for debug mode and session security, and FastAPI rules for authentication patterns are all available in Semgrep’s registry and regularly updated.

Teams that need custom security rules. If your organization has internal security policies, proprietary API patterns, or compliance requirements that require checking for specific code patterns, Semgrep’s YAML-based rule authoring lets you encode these checks in hours. This is impossible with Bandit without writing Python plugin code.

Multi-language stacks. If your team writes Python microservices alongside Go APIs, JavaScript frontends, and Terraform infrastructure, Semgrep scans all of them with the same tool, rule authoring approach, and centralized dashboard. Running separate tools (Bandit for Python, ESLint for JS, a Go linter for Go, a Terraform scanner for IaC) adds complexity and provides no unified view.

Teams that have outgrown Bandit’s noise. On large Python codebases, Bandit’s false positive rate in certain categories (especially B608 SQL injection patterns) can generate significant noise. Teams that have added hundreds of # nosec comments throughout their codebase may find Semgrep’s taint-tracking approach delivers higher-quality findings with less manual suppression.

Teams of 10 or fewer contributors who want enterprise-grade scanning. The Semgrep full platform is completely free for up to 10 contributors. This means a 10-person Python team can have cross-file taint analysis, AI-powered triage, 20,000+ Pro rules, SCA with reachability, and secrets validation - at no cost. There is no reason to use only Bandit if you qualify for the free Semgrep Team tier.

When to Use Both Together

The strongest Python security scanning posture for most teams is running both tools, because they complement each other effectively:

Bandit in pre-commit hooks: Bandit runs in under 5 seconds, catches obvious issues before code is committed, and requires no configuration. Developers get instant feedback on the most common mistakes without waiting for CI.

Semgrep in CI/CD pipelines: Semgrep’s deeper analysis, framework-specific rules, and (in the paid tier) cross-file taint tracking runs on every PR. It catches the vulnerabilities that require more analysis than a pre-commit hook supports, posts inline comments on the PR, and blocks merges when critical findings are detected.

This layered approach matches the feedback loop to the tool’s capabilities: fast, simple checks at commit time, thorough analysis at PR time. The total cost for teams under 10 contributors is still zero - Bandit is always free, and Semgrep Community Edition plus the full platform is free for up to 10 contributors.

The Verdict

Semgrep and Bandit are not competing for the same position in your Python security toolchain - they occupy different tiers of the analysis stack.

Bandit occupies the fast, zero-configuration baseline tier. Every Python project should run Bandit. It takes two minutes to set up, catches the most common and highest-confidence security mistakes, and costs nothing. Not running Bandit on a Python project is leaving easy security wins on the table. There is no scenario where Bandit makes your security posture worse.

Semgrep occupies the deeper, programmable analysis tier. For Python web applications that handle user input, have architectural complexity across multiple modules, or need to enforce organization-specific security policies, Bandit alone is insufficient. The structural inability to trace data across files means Bandit will miss the injection vulnerabilities that matter most in production applications. Semgrep’s taint tracking, framework-specific rules, and custom rule authoring fill this gap.

For solo developers and very small Python teams (1-5): Use Bandit in pre-commit hooks and Semgrep Community Edition in CI/CD. Add the free Semgrep Team platform to get cross-file analysis and AI triage at zero cost (free for up to 10 contributors). This stack costs nothing and provides comprehensive Python security scanning.

For small to medium Python teams (5-20): The same as above. If you have up to 10 contributors, the Semgrep Team tier is still free. If you exceed 10 contributors and need the Pro engine’s cross-file analysis (and you should, for any web application), Semgrep Team at $35/contributor/month is justified by the vulnerability categories it catches that Bandit structurally cannot. Compare this to the cost of a security incident from a SQL injection or command injection that single-file scanning missed.

For larger teams or organizations with broader platform needs: Consider supplementing Semgrep with SonarQube for code quality metrics and quality gate enforcement, or evaluate platforms like CodeAnt AI ($24-40/user/month) that bundle AI code review, SAST, and secrets detection together. See our Semgrep vs SonarQube comparison and best SAST tools guide for broader context.

The bottom line is direct: if you write Python, run Bandit. If you write Python web applications, also run Semgrep. If you can only have one tool and your application takes user input from the internet, run Semgrep with a Python ruleset - it covers Bandit’s ground and goes significantly further. If cost is the constraint, both tools are free for small teams, making the “choose one” framing a false dilemma.

Frequently Asked Questions

Is Bandit still maintained in 2026?

Yes, Bandit is actively maintained by the PyCQA (Python Code Quality Authority) organization on GitHub. It receives regular updates for new Python versions and bug fixes, though the pace of new feature development is slower than tools like Semgrep. Bandit 1.8.x is the current stable release and supports Python 3.8 through 3.13. The tool is considered stable and production-ready for its intended use case of fast, zero-config Python security scanning. That said, Bandit has not added cross-file taint tracking or AI-powered triage, so teams with advanced security needs may find it insufficient on its own.

Can Bandit detect SQL injection in Python?

Bandit can detect some SQL injection patterns - specifically, it flags calls to cursor.execute() that use string formatting or concatenation (B608 rule). However, Bandit's detection is limited to simple, single-file patterns. It cannot trace user input across multiple files or function calls to determine if it eventually reaches a SQL query. Semgrep's taint-tracking mode performs cross-file dataflow analysis and can follow user input from a Django request, through a service layer, to a database call, even if the path spans multiple files and functions. For comprehensive SQL injection detection in real-world Python applications, Semgrep's Pro engine provides significantly better coverage.

Is Bandit a linter or a SAST tool?

Bandit is a SAST (Static Application Security Testing) tool, not a linter. It analyzes Python source code specifically for security issues using an AST-based approach, checking code against a set of predefined security test plugins. It does not check for code style, formatting, unused variables, or code quality concerns. Traditional Python linters like Ruff, Flake8, or Pylint handle style and code quality. Bandit focuses exclusively on security - things like hardcoded passwords, use of weak cryptographic algorithms, shell injection risks, use of assert statements in security checks, and binding to all network interfaces.

Does Semgrep support Django and Flask security rules?

Yes. Semgrep has extensive framework-specific rules for both Django and Flask in its registry. For Django, Semgrep includes rules for detecting missing CSRF protection, unsafe use of raw SQL with Django ORM, missing authentication decorators, insecure template rendering with mark_safe(), and overly permissive ALLOWED_HOSTS settings. For Flask, Semgrep includes rules for detecting debug mode enabled in production, insecure session configuration, missing login_required decorators, and user input reaching subprocess calls. These framework-aware rules are available in the community registry (2,800+ rules) and are significantly expanded in the Pro rule library (20,000+ rules).

What is the difference between Bandit and Semgrep for Python projects?

The core difference is depth, scope, and customizability. Bandit is a focused, zero-configuration Python security scanner with approximately 80 built-in test plugins that check for common security issues using AST analysis within single files. It is easy to run, fast, and requires no setup. Semgrep is a multi-language, programmable security scanner that supports Python alongside 29+ other languages, performs cross-file taint analysis in the paid tier, and lets you write custom rules in YAML using syntax that mirrors the target code. Bandit is better for quick security checks with no configuration overhead. Semgrep is better for comprehensive security scanning, custom organization-specific rules, and cross-file vulnerability detection.

Is Bandit free and open source?

Yes. Bandit is completely free and open source under the Apache 2.0 license. There is no paid edition, no premium tier, no usage limits, and no account required. You install it with pip (pip install bandit), run it against your code (bandit -r src/), and get results immediately. Bandit will remain free because it is a community project maintained by PyCQA. This makes Bandit extremely attractive for Python-only projects, CI/CD pipelines on a budget, and open-source projects. Semgrep's Community Edition is also free and open source under LGPL-2.1, but the full platform with cross-file analysis and AI triage costs $35/contributor/month for teams beyond 10 contributors.

Can I run Bandit and Semgrep together in CI/CD?

Yes, and running both together is a common and recommended pattern. Bandit runs in seconds and catches quick wins with zero configuration - hardcoded passwords, weak crypto, assert-based authentication checks, dangerous subprocess patterns. Semgrep adds cross-file taint analysis, framework-specific rules, and custom organization-specific security checks that Bandit cannot perform. The two tools have complementary detection approaches (AST pattern matching vs dataflow analysis) and minimal overlap in what they catch. Adding both to a GitHub Actions or GitLab CI pipeline takes under 10 minutes and provides significantly better security coverage than either tool alone.

What does Bandit check for in Python code?

Bandit checks for approximately 80 categories of security issues organized into test plugins. Key categories include: hardcoded passwords and secret strings (B105, B106, B107), use of weak cryptographic algorithms like MD5 and SHA1 (B303, B304), use of random instead of secrets for cryptographic operations (B311), shell injection via subprocess with shell=True (B602, B603), SQL injection patterns via string formatting (B608), use of exec() or eval() with dynamic content (B102, B307), binding to all network interfaces (B104), use of assert statements in security-sensitive code (B101), insecure XML parsing (B405, B408), use of the deprecated and insecure telnet or FTP (B401, B321), and use of insecure HTTP rather than HTTPS (B310). Bandit does not check code style, complexity, or non-security quality concerns.

Does Semgrep replace Bandit?

Semgrep can replace most of what Bandit does and adds capabilities Bandit cannot provide. Semgrep's registry includes rules that cover the same vulnerability categories as Bandit's built-in tests, plus cross-file taint tracking, framework-specific patterns, and custom rule authoring. However, Bandit has advantages in zero-configuration simplicity and Python-specific institutional knowledge accumulated over years of development. Many teams keep both: Bandit for fast, no-setup Python-specific checks in pre-commit hooks, and Semgrep for deeper analysis in CI/CD pipelines. For teams that want to minimize tool count, Semgrep alone (with a good Python ruleset like p/python or p/django) covers Bandit's ground and more.

How fast is Bandit compared to Semgrep?

Both tools are fast, but in different contexts. Bandit is typically faster on pure Python codebases because it is Python-specific and has no overhead from multi-language support or cross-file analysis. A small to medium Python project (10,000-50,000 lines) typically completes in under 5 seconds. Semgrep's Community Edition on the same codebase completes in roughly 10-20 seconds. Semgrep's Pro engine with cross-file analysis takes longer depending on codebase complexity, typically 30 seconds to 2 minutes. For pre-commit hooks where speed is critical, Bandit's single-file analysis is faster. For CI/CD pipelines where thoroughness matters more than raw speed, the difference is less significant.

Which tool has better false positive rates, Semgrep or Bandit?

This depends on the specific issue category. Bandit tends to have higher false positive rates because its AST-based pattern matching lacks context - for example, the B608 SQL injection rule flags any string that looks like a SQL query with variable interpolation, even if the variable comes from a safe internal source rather than user input. Semgrep's taint analysis can distinguish between user-controlled and internally-controlled data, which reduces false positives in that category. However, Bandit's simpler rules in categories like weak crypto, hardcoded passwords, and insecure function use produce fewer false positives because those patterns are genuinely problematic regardless of context. Semgrep's paid tier adds AI-powered triage (Semgrep Assistant) that reduces false positive noise by 20-40% across all finding types.

What is the best Python security scanner for a small team or startup?

For a small Python team (under 10 developers), the best setup is Bandit plus the free Semgrep Community Edition, or the full Semgrep platform which is free for up to 10 contributors. Bandit provides instant Python-specific security checks with zero configuration. The free Semgrep platform adds cross-file analysis, AI-powered triage, and 20,000+ Pro rules at no cost for teams under 10 contributors. This combination provides enterprise-grade Python security scanning at zero cost. For teams that want a single tool with broader security plus code quality coverage, Codacy's Pro plan at $15/user/month or CodeAnt AI's Premium plan at $40/user/month bundle SAST, secrets detection, and code review in one platform.

Explore More

Tool Reviews

Free Newsletter

Stay ahead with AI dev tools

Weekly insights on AI code review, static analysis, and developer productivity. No spam, unsubscribe anytime.

Join developers getting weekly AI tool insights.

Related Articles