← Back to DevBytes

Semgrep: Setup, Configuration, and Best Practices

Introduction to Semgrep

Semgrep is a fast, open-source static analysis tool designed to find bugs, detect vulnerabilities, and enforce code standards across your codebase. Unlike traditional static analysis tools that rely on complex abstract syntax trees and slow compilation, Semgrep uses a lightweight pattern-matching approach that feels familiar to developers. You write rules that look like the code you want to find, and Semgrep does the rest.

Originally developed by Return To Corporation (r2c) and now maintained by Semgrep Inc., the tool supports over 30 programming languages including Python, JavaScript, TypeScript, Java, Go, C, C++, Ruby, PHP, and more. Its syntax-aware matching means it understands code structure, so it won't produce false positives from comments or string literals that happen to contain matching text.

Why Semgrep Matters

Security and code quality tools often fall into two camps: fast but shallow (like grep), or thorough but slow and noisy (like heavy static analyzers). Semgrep occupies a useful middle ground. It runs quickly enough to integrate into CI pipelines and pre-commit hooks, yet it understands code semantics well enough to catch real issues.

Installing Semgrep

Semgrep can be installed through several methods. The most common approach is via pip, but Homebrew and Docker are also supported.

Installation via pip

pip install semgrep

Installation via Homebrew (macOS)

brew install semgrep

Running with Docker

docker run --rm -v "${PWD}:/src" returntocorp/semgrep semgrep scan /src

After installation, verify the tool is available on your PATH:

semgrep --version

Your First Scan

The fastest way to try Semgrep is to run it against a project using rules from the Semgrep Registry. The auto config pulls a curated set of rules based on the languages detected in your repository.

semgrep scan --config auto

This command will analyze your code, print findings to the terminal, and upload results to the Semgrep App if you have authenticated. For a local-only scan without any upload, use:

semgrep scan --config auto --json > results.json

Scanning a Specific Directory

By default, Semgrep scans the current working directory. You can target a specific path:

semgrep scan --config auto path/to/source

Understanding Semgrep Rules

Semgrep rules are written in YAML. Each rule defines a pattern to match, the languages it applies to, and optional metadata. The core idea is that the pattern looks like the code you want to find, with metavariables acting as wildcards.

A Simple Rule Example

Here is a rule that detects use of the insecure eval() function in Python:

rules:
  - id: python-eval-detection
    message: Avoid using eval(); it can execute arbitrary code.
    severity: WARNING
    languages: [python]
    pattern: eval(...)

The ... is a metavariable that matches any arguments. When Semgrep encounters eval(user_input), it will flag it.

Using Metavariables

Metavariables start with $ and let you capture and reuse parts of the matched code. This rule detects password assignment to a variable and checks if it is logged:

rules:
  - id: password-logged
    message: "Password variable $PW is being logged"
    severity: ERROR
    languages: [python]
    pattern-either:
      - pattern: print($PW)
      - pattern: logging.info($PW)
    metavariable-regex:
      metavariable: $PW
      regex: (?i)(password|passwd|pwd)

Pattern Combinations

Semgrep supports several operators for combining patterns:

This example finds functions that use requests.get without setting a timeout:

rules:
  - id: requests-missing-timeout
    message: "requests.get() called without a timeout parameter"
    severity: WARNING
    languages: [python]
    patterns:
      - pattern: requests.get(...)
      - pattern-not: requests.get(..., timeout=...)

Configuration Files

For projects that use Semgrep regularly, a configuration file keeps things organized. The default file is semgrep.yml, but you can name it anything and reference it with --config.

Project Configuration Example

rules:
  - id: no-console-log
    message: Remove console.log statements before production.
    severity: INFO
    languages: [javascript, typescript]
    pattern: console.log(...)

  - id: hardcoded-secret
    message: Detected a hardcoded API key. Use environment variables instead.
    severity: ERROR
    languages: [python]
    pattern: API_KEY = "..."
    metadata:
      category: security
      cwe: "CWE-798: Use of Hard-coded Credentials"

Run the scan with:

semgrep scan --config semgrep.yml

Ignoring Files

Semgrep respects .gitignore by default. For additional exclusions, use a .semgrepignore file or the --exclude flag:

semgrep scan --config auto --exclude "tests/*" --exclude "vendor/*"

A typical .semgrepignore file:

tests/
vendor/
node_modules/
*.min.js
docs/

Integrating Semgrep into CI/CD

Running Semgrep in continuous integration ensures that issues are caught before code is merged. Most CI systems can run Semgrep with a few lines of configuration.

GitHub Actions Example

name: Semgrep Scan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: returntocorp/semgrep-action@v1
        with:
          config: auto

GitLab CI Example

semgrep:
  image: returntocorp/semgrep
  script:
    - semgrep scan --config auto --json --output semgrep-results.json
  artifacts:
    paths:
      - semgrep-results.json

Pre-commit Hook

For immediate feedback during development, add Semgrep as a pre-commit hook:

repos:
  - repo: https://github.com/returntocorp/semgrep
    rev: v1.50.0
    hooks:
      - id: semgrep
        args: [--config, auto]

Using the Semgrep Registry

The Semgrep Registry hosts thousands of community-maintained rules organized by language, framework, and category. You can reference specific rule packs by name:

# Security rules for Python
semgrep scan --config p/python

# OWASP Top 10 rules
semgrep scan --config p/owasp-top-ten

# Django-specific rules
semgrep scan --config p/django

# Community-contributed rules
semgrep scan --config https://semgrep.dev/r/example-rule

You can also combine multiple configs:

semgrep scan --config p/python --config p/owasp-top-ten --config custom-rules.yml

Best Practices

Start with Auto, Then Customize

Begin with --config auto to get immediate value from curated rules. As you identify noise or gaps, gradually introduce custom rules tailored to your codebase and conventions.

Tune Rules to Reduce False Positives

False positives erode developer trust. Use pattern-not, pattern-inside, and path filters to narrow matches. If a registry rule is too noisy, copy it into your own config file and adjust it rather than disabling an entire rule pack.

Use Severity Levels Meaningfully

Semgrep supports INFO, WARNING, and ERROR severities. Reserve ERROR for issues that should block a build, WARNING for issues worth reviewing, and INFO for stylistic or informational findings. This mapping makes it straightforward to configure CI to fail only on errors.

Write Rules for Your Own Patterns

Beyond security, Semgrep excels at enforcing internal conventions. Use it to flag deprecated function calls, ensure error handling patterns are followed, or prevent use of banned libraries. Custom rules encode team knowledge directly into the development workflow.

Version Pin Your Configs

When referencing registry rules in CI, pin to specific versions or commit SHAs to avoid surprises when upstream rules change. This is especially important for production pipelines where a new rule suddenly failing the build can block deployments.

Review Findings Regularly

Schedule periodic reviews of Semgrep findings and rule sets. Codebases evolve, and rules that were useful six months ago may no longer apply. Pruning outdated rules keeps scan output relevant and actionable.

Leverage Autofix Where Possible

Semgrep supports an autofix field that lets a rule suggest a replacement. This is powerful for migration rules, such as renaming a deprecated API:

rules:
  - id: deprecated-oldfunction
    message: "oldFunction is deprecated, use newFunction instead"
    severity: WARNING
    languages: [javascript]
    pattern: oldFunction($X)
    fix: newFunction($X)

Apply fixes with:

semgrep scan --config autofix-rules.yml --autofix

Always review autofix changes in a diff before committing.

Conclusion

Semgrep bridges the gap between simple text search and heavyweight static analysis, giving developers a tool that is fast enough for real-time use and expressive enough to catch meaningful issues. By starting with registry rules, gradually introducing custom patterns, and integrating scans into your CI pipeline, you can build a security and code quality practice that scales with your team. The key to success with Semgrep is iteration: tune rules to match your codebase, prune what no longer applies, and treat the rule library as living documentation of your engineering standards. With thoughtful configuration and regular maintenance, Semgrep becomes a quiet but powerful ally in shipping safer, cleaner code.

— Ad —

Google AdSense will appear here after approval

← Back to all articles