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
| Bandit | Skylos | |
|---|---|---|
| Best for | Quick security audits, teams that want a mature and simple security linter | Python teams that also need dead code detection, AI code scanning, and CI quality gates |
| Languages | Python only | Python, JavaScript/TypeScript, Go, Java, Kotlin, PHP, Rust, Dart, and C#; targeted Shell and configuration checks |
| Security scanning | Yes (AST checks with severity, confidence, and CWE metadata) | Yes; pattern and flow checks, deepest in Python and JavaScript/TypeScript |
| Dead code detection | No | Yes (with transitive propagation) |
| AI defense scanning | No dedicated LLM/agent suite | 13 static plugins mapped to selected OWASP LLM and Agentic categories |
| Removed-control diff check | No dedicated rule | Yes, for selected security-control patterns |
| Code quality metrics | Security-focused rather than a metrics suite | Yes; complexity, coupling, and cohesion are primarily Python-focused |
| Framework awareness | Individual plugins may recognize APIs; no dead-code entry-point model | Explicit Python entry-point models for common frameworks |
| Taint analysis | No general interstatement taint engine | Local propagation for supported source/sink patterns; ordinary parameters are treated conservatively |
| Custom rules / plugins | Yes (Python plugin system) | Focused Python AST custom rules; narrower extension model |
| CI/CD | Documented pre-commit, GitHub Action, and manual CI options | Auto-generated GitHub Actions workflow |
| Maturity | Long-running PyCQA project | Newer, actively developed |
| Pricing | Free (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
Related
- Bandit vs CodeQL vs Semgrep for Python
- Semgrep vs Skylos
- Snyk vs Skylos
- SonarQube vs Skylos
- Deadcode vs Vulture vs Skylos
- Best Python SAST Tools in 2026
- How to Detect Dead Code in Python
- How to Catch Hallucinated Imports in AI Code
- How to Secure GitHub Actions for Python Repos
- Python Security Scanner for GitHub Actions
Both tools are open source. Bandit | Skylos | Skylos Docs | Install Skylos