Bandit is a long-running, Python-only security analyzer maintained under PyCQA. Its documented workflow is intentionally direct: install it, scan Python files, and review findings with severity and confidence ratings. The project also documents profiles, baselines, plugins, pre-commit use, and CI integrations. See Bandit's official getting-started guide.

Skylos is a newer multi-language static analysis tool that combines security scanning with dead-code, secrets, dependency, quality, and AI-defect checks. Its deepest framework and data-flow analysis is in Python; coverage in other languages varies by category.

This comparison is for Python teams evaluating both tools. We'll be straightforward about where each is stronger, where each one creates more triage work, and what to test on a real repository before deciding.


The short version

BanditSkylos
Best forQuick security audits, teams that want a mature and simple security linterPython teams that also need dead code detection, AI code scanning, and CI quality gates
LanguagesPython onlyPython, JavaScript/TypeScript, Go, Java, Kotlin, PHP, Rust, Dart, and C#; targeted Shell and configuration checks
Security scanningYes (AST checks with severity, confidence, and CWE metadata)Yes; pattern and flow checks, deepest in Python and JavaScript/TypeScript
Dead code detectionNoYes (with transitive propagation)
AI defense scanningNo dedicated LLM/agent suite13 static plugins mapped to selected OWASP LLM and Agentic categories
Removed-control diff checkNo dedicated ruleYes, for selected security-control patterns
Code quality metricsSecurity-focused rather than a metrics suiteYes; complexity, coupling, and cohesion are primarily Python-focused
Framework awarenessIndividual plugins may recognize APIs; no dead-code entry-point modelExplicit Python entry-point models for common frameworks
Taint analysisNo general interstatement taint engineLocal propagation for supported source/sink patterns; ordinary parameters are treated conservatively
Custom rules / pluginsYes (Python plugin system)Focused Python AST custom rules; narrower extension model
CI/CDDocumented pre-commit, GitHub Action, and manual CI optionsAuto-generated GitHub Actions workflow
MaturityLong-running PyCQA projectNewer, actively developed
PricingFree (OSS)Free (OSS) / Paid (Cloud)

Where Bandit is stronger

Maturity and adoption

Bandit has a mature Python security rule vocabulary, stable finding IDs, severity and confidence fields, CWE links, JSON baselines, and a documented plugin API. Existing policies and suppressions built around those interfaces may matter more than switching tools.

Skylos is newer and has a smaller ecosystem.

Simplicity

Bandit does one thing: scan Python code for security issues. The mental model is easy to explain to any developer:

pip install bandit
bandit -r src/

You get findings with severity and confidence ratings. Bandit can run without a project configuration, while profiles, exclusions, baselines, and plugins are available when needed.

Skylos exposes more analysis categories and policy choices. If you only need Bandit's Python security checks, its narrower interface may be easier to adopt. Runtime should be measured on your repository rather than inferred from scope.

CWE mappings

Bandit's documented test plugins expose CWE references alongside severity and confidence for many findings. If your audit workflow consumes those fields or stable B rule IDs, verify the exact plugins in your selected profile.

Plugin system

Bandit supports custom test plugins. If you need a project-specific Python security check, you can write and distribute one using its documented plugin interface. Skylos also has custom rules, but its extension model is narrower: local/community and Cloud-synced rules match focused Python AST patterns. Managed Cloud gates reject the local engine's basic custom taint_flow pattern because it is not supported as an attested blocking rule.

Lightweight and fast

Bandit has a focused Python security scope. Skylos can run several analyzers in one invocation, and their cost depends on which flags are enabled. Benchmark both with the same files and CI runner before making a performance claim.


Where Skylos is stronger

Taint analysis vs. pattern matching

This is the most significant technical difference between the two tools.

Bandit plugins inspect Python AST nodes and surrounding context. Its B602 documentation says Bandit reports every shell=True call but adjusts severity based on whether the command is a simple static string, a static string containing shell characters, or a computed/formatted value:

# Bandit reports both as B602, with different severity
subprocess.call("ls -la", shell=True)           # LOW for a simple static string
subprocess.call(user_input, shell=True)          # HIGH for a computed value

Skylos's supported Python rules propagate taint through local assignments and expressions from sources such as request.args.get(), input(), and sys.argv to dangerous sinks. They also conservatively treat ordinary function parameters as untrusted, so a finding is evidence to review rather than proof of a complete request path:

# Skylos flags this -- user input flows to shell command
cmd = request.args.get("command")
subprocess.call(cmd, shell=True)     # SKY-D212: Command injection

# Skylos flags SKY-D209 (shell=True) but NOT SKY-D212 -- no tainted input
subprocess.call("ls -la", shell=True)  # Warning only, not a command injection finding

The practical impact depends on the rule. Bandit preserves a broad shell=True warning and communicates context through severity; Skylos also reports shell=True as SKY-D209, while reserving its SKY-D212 command-injection finding for a recognized tainted flow.

Neither approach dominates every case. Bandit's pattern finding can be useful even without a proven input flow. Skylos's additional taint finding gives evidence about a recognized source-to-sink path. Both analyzers are bounded by the APIs and data-flow patterns they model.

Dead code detection

Bandit has no dead code detection at all. If you need to find unused functions, classes, imports, and variables, you need a second tool alongside Bandit (typically Vulture or deadcode).

Skylos detects dead code with transitive propagation:

def process_refund(order_id):               # dead -- nothing calls this
    validated = _validate_refund(order_id)   # also dead (transitive)
    _send_refund_email(validated)            # also dead (transitive)

Dead code is a security concern too. Unused code still gets scanned, reviewed, and maintained. Removing it reduces attack surface and review burden.

AI defense scanning

Skylos has a dedicated AI defense engine with 13 static plugins mapped to selected OWASP LLM 2024/2025 and Agentic 2026 categories:

skylos defend .

This scans codebases that integrate with LLMs (via OpenAI, Anthropic, LangChain, etc.) and runs the checks that apply to each detected integration, including:

  • Raw input reaching prompt construction without intermediate processing
  • Missing delimiters around user or retrieved RAG context
  • Output PII filtering gaps
  • Missing rate limiting on LLM endpoints
  • Missing cost controls
  • Insufficient logging on LLM interactions

Bandit does not document a dedicated LLM/agent rule suite. Skylos can report the listed patterns when its static plugins recognize the relevant libraries and flows. The OWASP mapping has gaps, so this is not complete Top 10 coverage and should be tested against the frameworks you use.

Diff-aware security regression detection

This is an explicit Git-diff heuristic that Bandit does not document.

SKY-L021 checks removed and added diff lines for selected controls, including:

  • Removed authentication decorators (@login_required, @requires_auth)
  • Removed CSRF protection
  • Removed rate limiting
  • Removed selected validation calls
  • Disabled security headers
  • Removed logging calls

Bandit analyzes the current Python tree rather than exposing a dedicated removed-control rule. Skylos can flag a recognized deleted decorator or call in a Git-aware scan. The result is author-agnostic and heuristic: it does not prove the control was required or that AI removed it.

AI provenance tracking

Skylos can associate findings on changed ranges with commit metadata that resembles known AI-tool authors, emails, subjects, or co-author trailers. This is useful provenance context, but it is heuristic and does not prove which model generated a line.

Bandit does not track code authorship or provenance.

Framework awareness

Bandit is not a dead-code analyzer, so it does not need to decide whether framework-registered functions are reachable. Individual security plugins recognize APIs and call forms, but Bandit does not document a general framework entry-point model.

Skylos has built-in framework visitors for Django, Flask, FastAPI, Pydantic, pytest, Celery, and Click. This matters primarily for dead code detection (avoiding false positives on framework-registered functions), but it also improves security analysis by understanding framework-specific input sources and sinks:

@app.route("/api/users")
def get_users():
    user_id = request.args.get("id")
    # Skylos knows request.args.get() is a taint source in Flask
    cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
    # SKY-D211: SQL injection (tainted data flows to query)

Code quality metrics

Bandit is not a code quality tool. It does not report on complexity, coupling, cohesion, or architectural metrics.

Skylos includes:

skylos src/ --quality
  • Cyclomatic complexity (SKY-Q301)
  • Deep nesting detection (SKY-Q302)
  • Function length and argument count (SKY-C303, SKY-C304)
  • God class detection (SKY-Q501)
  • Coupling and cohesion metrics (SKY-Q701, SKY-Q702)
  • Architecture metrics (instability, distance from main sequence)
  • Async blocking call detection (SKY-Q401)

CI/CD quality gate

Skylos generates a complete GitHub Actions workflow:

skylos cicd init

This creates a .github/workflows/skylos.yml with PR scanning, annotations, review comments, and a quality-gate step. Bandit documents a GitHub Action and pre-commit use, and can also be wired into other CI systems; Skylos's distinction is the generator.


Detection comparison: real examples

SQL injection

Bandit:

# Bandit flags this as B608 (hardcoded SQL expressions)
query = "SELECT * FROM users WHERE id = %s" % user_id
cursor.execute(query)

Bandit detects the string formatting pattern in a SQL context. It does not trace where user_id comes from.

Skylos:

# Skylos flags this (SKY-D211: SQL injection via taint analysis)
user_id = request.args.get("id")
query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)

# Skylos does NOT flag this -- hardcoded value, no tainted input
cursor.execute("SELECT * FROM users WHERE active = 1")

Command injection

Bandit:

# Bandit reports B602 for shell=True and adjusts severity by command form
subprocess.call("ls -la", shell=True)         # LOW for this simple static string
subprocess.call(user_cmd, shell=True)          # HIGH for a computed value

Skylos:

# Skylos flags the taint-aware command injection rule only when user input reaches the sink
user_cmd = request.form.get("cmd")
subprocess.call(user_cmd, shell=True)         # SKY-D212: command injection (tainted input)

subprocess.call("ls -la", shell=True)         # SKY-D209: shell=True warning, but NO D212 (no tainted input)

Hardcoded secrets

Bandit:

# Bandit flags B105 (hardcoded password string)
password = "admin123"

Skylos:

# Skylos flags SKY-L014 (hardcoded credentials)
API_KEY = "sk-1234567890abcdef"
PASSWORD = "admin123"

Both cover hardcoded-credential patterns. Bandit's documented B105 family looks for password-like names; Skylos's secrets scanner also has provider- and entropy-oriented checks. Test the credential formats you use rather than assuming equivalent provider coverage.

Disabled security controls

Bandit:

# Bandit flags B501 (requests with verify=False)
requests.get(url, verify=False)

Skylos:

# Skylos also catches this pattern
requests.get(url, verify=False)

# Skylos can also associate a changed-line finding with heuristic Git provenance

Both catch verify=False. Skylos can also associate a changed-line finding with Git metadata that resembles a known AI tool, but that attribution is not proof of authorship.


Migration guide: Bandit to Skylos

If you're currently running Bandit and want to evaluate Skylos, here's how to compare:

1. Run both on the same codebase

# Bandit
pip install bandit
bandit -r src/ -f json -o bandit-results.json

# Skylos
pip install skylos
skylos src/ --danger --quality --ai-defects --json > skylos-results.json

2. Compare the overlap

Do not assume one-to-one rule parity. Compare these practical differences:

  • Bandit and Skylos can both report dangerous call patterns, but their rule boundaries, severities, and flow evidence differ
  • Skylos may add findings from dead-code, quality, and AI-defect categories that Bandit does not scan
  • Skylos may add SQL-injection and command-injection evidence when a supported flow spans local assignments or expressions

3. Evaluate the noise difference

Classify true positives, false positives, and misses in each result set. For shell=True, compare Bandit's severity adjustment with Skylos's separate dangerous-shell and tainted-command rules. Record the suppressions your own repository needs instead of relying on a general noise claim.

4. Decide if you need both

Bandit and Skylos can run side by side. If you have existing Bandit suppressions and custom plugins that represent institutional knowledge, keep Bandit and add Skylos for dead code, quality, and AI defense. If you're starting fresh, Skylos covers more ground in a single tool.


When to use Bandit

  • You need an established Python security scanner with stable finding IDs
  • You want focused security scanning without enabling additional analysis categories
  • You need CWE mappings for compliance or audit reports
  • You have custom Bandit plugins encoding project-specific security rules
  • You need to scan only for security -- dead code and quality are handled by other tools
  • Your team is familiar with Bandit and its findings format

When to use Skylos

  • Your codebase is primarily Python, or you have validated Skylos's category depth for its other supported languages
  • You want security + dead code + quality in a single tool
  • Your team uses AI coding tools (Cursor, Copilot, Claude Code) and you want to catch regressions
  • You want taint evidence alongside dangerous-pattern findings for supported injection flows
  • You need AI defense scanning for LLM-integrated applications
  • You want zero-config CI with PR inline comments and quality gates
  • You use Django, Flask, FastAPI, or Pydantic and want framework-aware analysis

Can you use both?

Yes. Some teams run Bandit for its stable finding IDs, CWE mappings, or existing plugins and Skylos for dead code, quality, AI defense, and taint-aware rules. Expect overlapping security findings and deduplicate them by location and vulnerability class in CI.


Quick start

Bandit

pip install bandit
bandit -r src/

Skylos

pip install skylos
skylos src/ --danger --quality

Final thoughts

Bandit is the more established, narrowly scoped choice. If you need a Python security linter with stable rule IDs, severity and confidence, CWE mappings, baselines, and a plugin API, Bandit fits that job.

Skylos is the broader tool. It adds local taint-aware checks to pattern findings, plus dead code detection, code quality metrics, AI defense scanning, and diff-aware regression detection. If your team ships AI-generated code and wants those categories in one workflow, compare its findings with the separate tools you use today.

The honest answer: if security scanning is your only need and you value maturity and simplicity, Bandit is a solid choice. If you also care about dead code, AI-specific risks, and quality metrics, Skylos is worth evaluating alongside it.

Try both on your codebase and compare the output. That tells you more than any comparison article.

If your next question is "how would this look in a pull request workflow?", read Python Security Scanner for GitHub Actions next.

If your real issue is AI-generated code that looks plausible but is wrong, go to How to Verify AI-Generated Python Code and Catch Hallucinated Imports.


Try Skylos

If you want taint-aware security, dead-code detection, and LLM-application guardrail checks from the same CLI:

pip install skylos
skylos src/ --danger --quality
skylos defend src/

No signup is required for a local CLI scan; runtime depends on repository size and enabled checks. View on PyPI | Read the docs



Both tools are open source. Bandit | Skylos | Skylos Docs | Install Skylos