semgrep
Semgrep is a static-analysis CLI that matches source code by syntax and meaning instead of treating it as plain text. Its YAML rules can find insecure calls, bug patterns, policy violations, and migration targets across more than 30 languages, and some rules can propose or apply fixes. The open-source Community Edition runs locally and works well for focused searches, but Semgrep's own README says serious security scanning benefits from paid platform features such as cross-file analysis, supply-chain scanning, secrets scanning, and managed rules.
Install Semgrep when you have concrete patterns your existing linters cannot express, especially across several languages. Do not mistake the open-source CLI for the full commercial security platform; Semgrep's own README draws that boundary plainly.
Use it if
- You want project-specific security or correctness checks expressed as code-shaped patterns that developers can review
- You need one local scanner across a polyglot repository and want it in pre-commit hooks or CI
- You are auditing a codebase for a dangerous API, deprecated call, or bug variant and regex would produce too much noise
- You want a large public rule registry but still need the option to keep source code on your own machine
- You expect the free engine to be a complete enterprise SAST product: the project README explicitly warns that Community Edition is limited at function and file boundaries and will miss true positives that require paid cross-file or cross-function analysis
- You only scan Python for common mistakes: Ruff plus Bandit is simpler to install, faster to explain to a team, and avoids Semgrep's YAML rule language and larger dependency tree
- You need deterministic offline scans but plan to use Registry configs: remote configurations can change and report pseudonymous rule metrics unless you pin local rules and set metrics off
- You want a small Python dependency: the current package requires Python 3.10 or newer and installs a long list of runtime packages, including telemetry, MCP, YAML, crypto, and database dependencies
Setup reality
Use pipx or uv tool install rather than dropping Semgrep's large dependency set into an application environment. A first scan is easy, but useful CI needs pinned rule configuration, paths and generated files excluded, exit-code policy, baseline handling, and a decision about Registry metrics. Login is optional for local Community Edition scans, while Semgrep CI and the platform features require an account token.
Patterns
Install Semgrep as an isolated CLIinstall-cli
pipx install semgrep
semgrep --versionSemgrep requires Python 3.10 or newer and has many dependencies; pipx keeps them out of your application's environment.
Scan with a Registry rulesetscan-registry-config
semgrep scan --config p/default src/Registry configurations are remote. Pin rules locally for reproducible CI, and review the metrics policy before using them.
Run a one-off structural searchsearch-inline-pattern
semgrep -e '$X == $X' --lang py path/to/srcShell quotes are required around metavariables such as $X so the shell does not expand them.
Write a local rulewrite-basic-rule
rules:
- id: no-python-eval
languages: [python]
message: Avoid eval on untrusted input
severity: ERROR
pattern: eval($X)A rule needs an id, languages, message, severity, and at least one pattern operator.
Require several conditionscombine-patterns
rules:
- id: flask-debug-enabled
languages: [python]
message: Do not enable Flask debug mode
severity: WARNING
patterns:
- pattern: $APP.run(...)
- pattern-inside: |
$APP = Flask(...)
...
- pattern: $APP.run(..., debug=True, ...)All entries under patterns must match; ellipses allow intervening arguments or statements.
Exclude an allowed formexclude-safe-case
rules:
- id: require-timeout
languages: [python]
message: HTTP requests need a timeout
severity: WARNING
patterns:
- pattern: requests.get(...)
- pattern-not: requests.get(..., timeout=$T, ...)Negative patterns are applied to the positive match range; test the rule against both allowed and rejected examples.
Attach a simple automatic fixadd-autofix
rules:
- id: replace-debug-print
languages: [python]
message: Use the logger
severity: INFO
pattern: print($X)
fix: logger.info($X)A textual fix can change behavior when formatting or argument semantics differ; review changes before running with --autofix.
Run checked-in rules without Registry metricsscan-local-rules
semgrep scan --config .semgrep/rules.yml --metrics off src/ tests/Local rules keep configuration reproducible; --metrics off also prevents Registry rule metrics from being sent.
Produce machine-readable findingsemit-json
semgrep scan --config .semgrep/rules.yml --json --output semgrep-results.json src/Keep human terminal output and JSON artifact handling separate in CI so logs do not corrupt the result file.
Run platform-managed CI scanningrun-ci-mode
export SEMGREP_APP_TOKEN='replace-with-ci-secret'
semgrep cisemgrep ci is platform-oriented and needs an authenticated token; plain semgrep scan is the account-free local path.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| bandit | PyPI | Python-only security checks where a focused, familiar rule set is enough |
| ruff | PyPI | Python lint, correctness, and modernization checks where speed and low setup matter more than custom semantic rules |
| detect-secrets | PyPI | You only need secret detection with a reviewable baseline, not general static analysis |