The interface is localized. Skill descriptions and documents are machine-translated by AI into your language; code, commands, paths and technical terms stay in the original, and the author's document governs.
Explore skills
Data & research · SKILL.md

semgrep

semgrep

Semgrep is an open-source static analysis tool that finds bugs, security vulnerabilities and anti-patterns by matching code patterns. Use it to scan a repository, write custom YAML rules, auto-fix findings, or add a Semgrep scan with SARIF output to GitHub Actions or other CI.

Independent GitHub snapshot · Automated classification · Original documentation governs

Tterminalskills / skillsApache-2.0

Skill documentation

GitHub

name: semgrep description: Semgrep is an open-source static analysis tool that finds bugs, security vulnerabilities and anti-patterns by matching code patterns. Use it to scan a repository, write custom YAML rules, auto-fix findings, or add a Semgrep scan with SARIF output to GitHub Actions or other CI. license: Apache-2.0 compatibility: "Python 3.10+ (pip), Homebrew on macOS/Linux, or the semgrep/semgrep Docker image." metadata: author: terminal-skills version: 1.1.0 category: devops tags:


Semgrep

Overview

Semgrep is an open-source static analysis engine (LGPL-2.1 CLI, repository semgrep/semgrep) that matches code structure instead of text, so a rule reads almost like the source it finds. It supports 30+ languages. The CLI runs locally and offline with your own rules; community rules come from the Semgrep Registry; the paid Semgrep AppSec Platform adds dashboards, triage and diff-aware CI. This skill was checked against Semgrep 1.178 and 1.179 (September 2026).

Instructions

Install

python3 -m pip install semgrep      # needs Python 3.10+
# or: brew install semgrep          # Homebrew no longer supports Intel Macs
# or: docker run --rm -v "$PWD:/src" semgrep/semgrep semgrep scan --config p/security-audit
semgrep --version

Scan

semgrep scan --config p/security-audit src/        # a registry ruleset
semgrep scan --config p/owasp-top-ten --config p/typescript
semgrep scan --config .semgrep/ --metrics=off      # local rules only, fully offline
semgrep scan --config auto .                       # rules picked for the project's languages

--config auto sends the project URL to the Registry and enables metrics; for private code or air-gapped runs use explicit p/... rulesets or local files with --metrics=off. Registry rulesets are listed at semgrep.dev/explore.

Useful flags: --severity=ERROR, --error (exit code 1 when findings exist), --json, --sarif --output=semgrep.sarif, --exclude-rule, --include='*.ts', -a/--autofix (applies each rule's fix), --baseline-commit=origin/main (report only findings new since that commit). Ignore a line with a trailing // nosemgrep: rule-id; a .semgrepignore file excludes paths. Since 1.117 .gitignore is no longer read automatically.

Custom rules

Required keys are id, message, severity, languages and one pattern operator. Severities in rules are ERROR, WARNING, INFO (newer docs also list LOW to CRITICAL). Operator logic: pattern-either is OR, patterns is AND, and pattern-not and metavariable-regex only work inside patterns. A string such as "sk_live_..." is not a prefix match ("..." matches any string); use metavariable-regex for that.

# .semgrep/node-security.yml
rules:
  - id: sql-query-built-from-input
    message: SQL built from user input. Use a parameterized query, for example db.query("SELECT * FROM users WHERE id = $1", [userId]).
    severity: ERROR
    languages: [typescript, javascript]
    pattern-either:
      - pattern: $DB.query(`...${$INPUT}...`)
      - pattern: $DB.query("..." + $INPUT)
    metadata:
      cwe: ["CWE-89: SQL Injection"]
      confidence: HIGH

  - id: hardcoded-stripe-key
    message: Hardcoded Stripe key. Read it from process.env.STRIPE_SECRET_KEY.
    severity: ERROR
    languages: [typescript, javascript]
    patterns:
      - pattern-either:
          - pattern: $KEY = "$VALUE"
          - pattern: const $KEY = "$VALUE"
      - metavariable-regex:
          metavariable: $VALUE
          regex: ^sk_(live|test)_

  - id: route-without-auth
    message: Route handler without authMiddleware.
    severity: WARNING
    languages: [typescript, javascript]
    patterns:
      - pattern: router.$METHOD($PATH, async (req, res) => { ... })
      - pattern-not: router.$METHOD($PATH, authMiddleware, async (req, res) => { ... })

  - id: unsanitized-inner-html
    message: dangerouslySetInnerHTML with unsanitized input risks XSS.
    severity: ERROR
    languages: [typescript, javascript]
    pattern: |
      <$TAG dangerouslySetInnerHTML={{__html: $INPUT}} />
    fix: |
      <$TAG dangerouslySetInnerHTML={{__html: DOMPurify.sanitize($INPUT)}} />

Quote or use | for any message or pattern that contains : , otherwise YAML fails with "mapping values are not allowed here". Check a rule file with semgrep --validate --config .semgrep/, and unit-test rules by putting // ruleid: rule-id above lines that must match and // ok: rule-id above lines that must not in a file next to the rule, then run semgrep --test .semgrep/.

CI (GitHub Actions)

The semgrep/semgrep-action is the old way; the documented setup runs semgrep ci in the official container. semgrep ci needs SEMGREP_APP_TOKEN (a platform account) and scans only changed files on pull requests.

# .github/workflows/semgrep.yml
name: Semgrep
on:
  pull_request: {}
  push:
    branches: [main]
jobs:
  semgrep:
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep
    steps:
      - uses: actions/checkout@v4
      - run: semgrep ci
        env:
          SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

Without an account, run the open-source CLI directly and upload SARIF:

      - run: pip install semgrep
      - run: semgrep scan --config p/security-audit --config .semgrep/ --metrics=off --sarif --output=semgrep.sarif --error --baseline-commit=origin/${{ github.base_ref }}

Use fetch-depth: 0 on checkout so --baseline-commit can see the base branch.

Examples

Example 1: First security scan of a Node.js API

User request: "Scan our Express API in src/ for security problems and give me a report I can attach to the PR."

python3 -m pip install semgrep
semgrep scan --config p/security-audit --config p/nodejs --metrics=off --sarif --output=semgrep.sarif src/

The agent reports the summary line (for example Findings: 7 (5 blocking)), groups findings by rule id and file, fixes the clear ones (parameterized queries, secrets moved to environment variables) and leaves semgrep.sarif for upload.

Example 2: A team rule that bans a deprecated helper

User request: "Every call to legacyAuth.check(req) must be replaced by session.require(req). Make Semgrep flag it and fix it automatically."

The agent writes .semgrep/migrate-auth.yml with pattern: legacyAuth.check($REQ) and fix: session.require($REQ), validates it with semgrep --validate --config .semgrep/, previews with semgrep scan --config .semgrep/ --severity=WARNING src/, then applies it with --autofix and reviews the diff.

Guidelines

  • Keep ERROR for findings that should fail the build and WARNING for the rest; pair --error with --severity=ERROR to gate CI only on the former.
  • Start with a registry ruleset, then add project rules for your own auth and data-access conventions; always add metadata (CWE, confidence) so reviewers know why a line is flagged.
  • Review --autofix output before committing; a fix is a text substitution and can break formatting or semantics.
  • Semgrep is pattern-based, not a full data-flow prover: expect false positives and negatives; suppress deliberately with nosemgrep plus a reason.
  • Cross-file (interfile) analysis and many Pro rules need a Semgrep account; the open-source CLI analyses one file at a time.
  • Do not commit SEMGREP_APP_TOKEN; keep it in CI secrets.
Saved skills