Skip to content
Back to skills

Ast Grep

ASecurity

Use when doing structural code search and rewriting - ast-grep linting, refactoring, multi-language patterns

  • 21 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 2, 2026
developmentjavascripttypescriptpythonrustgojavarubyphpkotlinc++

Works with

  • cursor
  • cli

Security analysis

A92/100
  • mediumUses curl or wget to download content
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 3 files and shows the line behind each finding

Scanned October 2, 2026

npx -y skills add CodeAtCode/oss-ai-skills --skill ast-grep --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ast Grep?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ast Grep
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/codeatcode-ast-grep/badge)](https://www.skillsdirectory.com/skills/codeatcode-ast-grep)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ast-grep
description: Use when doing structural code search and rewriting - ast-grep linting, refactoring, multi-language patterns
metadata:
  author: mte90
  version: 1.0.1
  tags:
    - ast-grep
    - code-search
    - linting
    - refactoring
    - cli
    - ast
---

# ast-grep

Fast and user-friendly tool for large-scale code searching, linting, and rewriting using AST patterns.

## Overview

ast-grep (sg) is a CLI tool that searches code based on Abstract Syntax Tree patterns, similar to syntax-aware grep/sed. It supports multiple languages and can perform automated code refactoring.

- **Fast** - Written in Rust, processes code quickly
- **Polyglot** - Supports JavaScript, TypeScript, Python, Go, Rust, Java, C, C++, and more
- **Structural** - Matches code by AST patterns, not regex
- **Rewrite** - Automated code refactoring with metavariables

---

## Installation

```bash
# Via cargo
cargo install ast-grep

# Via npm
npm install -g ast-grep

# Download pre-built binary
curl -L https://github.com/ast-grep/ast-grep/releases/download/nightly/ast-grep-x86_64-unknown-linux-musl.tar.gz | tar xz
```

---

## Worked Rules: Real Defect Classes

This section shows three real-world rules that caught actual defects. Each demonstrates why ast-grep succeeds where grep fails.

### Security Rule: Unsafed SQL String Concatenation (Python)

Detects SQL queries built by string concatenation with user input — a SQL injection vulnerability.

```yaml
# rules/sql-injection.yml
id: sql-injection-concat
message: "SQL query built with string concatenation — possible injection vulnerability"
severity: error
language: Python
rule:
  all:
    - pattern: '$CURSOR.execute($SQL, $$$)'
    - has:
        pattern: '$USER_INPUT + $SQL'
        inside:
          pattern: '$SQL'
constraints:
  # $SQL must contain concatenation operator
  $SQL:
    kind: binary_expression
    has:
      pattern: '+'
severity: error
```

**Why grep cannot express this:**
- grep matches text patterns like `+` but cannot verify the context is a SQL execute call
- grep cannot distinguish `user_input + "hello"` from `user_input + "SELECT * FROM users"`
- ast-grep uses `inside` to confirm the concatenation feeds into `execute()`, and `kind` to verify it's a binary expression

**Test command:**
```bash
ast-grep scan --rule rules/sql-injection.yml src/
```

**What this catches:**
```python
# BAD — flagged
user_id = request.GET['id']
query = "SELECT * FROM users WHERE id = " + user_id
cursor.execute(query)

# OK — not flagged (parameterized query)
user_id = request.GET['id']
cursor.execute("SELECT * FROM users WHERE id = %s", [user_id])
```

---

### Correctness Rule: Empty Exception Handler

Detects `except Exception: pass` blocks that silently swallow errors — a common correctness bug.

```yaml
# rules/empty-except.yml
id: empty-except-block
message: "Empty exception handler swallows errors silently"
severity: warning
language: Python
rule:
  all:
    - pattern: 'except $EXC: pass'
    constraints:
      # $EXC must be Exception or a specific exception type
      $EXC:
        kind: identifier
        # Match 'Exception' or specific exception names
        regex: '^Exception$|^[A-Z][a-zA-Z]*Error$'
```

**Why grep cannot express this:**
- grep pattern `except.*: pass` matches too broadly (comments, multi-line, different contexts)
- grep cannot verify the `pass` is the only statement in the block
- ast-grep uses `all` to ensure both the exception clause AND the pass statement exist together structurally

**Test command:**
```bash
ast-grep scan --rule rules/empty-except.yml src/
```

**What this catches:**
```python
# BAD — flagged
try:
    process_data()
except Exception: pass  # Silent failure!

# BAD — flagged
try:
    connect_db()
except ConnectionError:
    pass  # Still silent!

# OK — not flagged (has logging)
try:
    process_data()
except Exception as e:
    logger.error(e)
```

---

### Convention Rule: `== None` vs `is None` (Python)

Detects Python code using `== None` instead of the idiomatic `is None`.

```yaml
# rules/none-comparison.yml
id: prefer-is-none
message: "Use 'is None' instead of '== None' for Pythonic code"
severity: warning
language: Python
rule:
  pattern: '$VALUE == None'
constraints:
  # Exclude None comparisons in comments
  $VALUE:
    kind:
      - identifier
      - attribute
      - call
    not:
      inside:
        kind: comment
```

**Why grep cannot express this:**
- grep pattern `== None` matches everywhere including comments and strings
- grep cannot distinguish `value == None` from `"x == None" in docstring`
- ast-grep uses `kind` constraints to match only actual comparison expressions

**Test command:**
```bash
ast-grep scan --rule rules/none-comparison.yml src/
```

**What this catches:**
```python
# BAD — flagged
if data == None:
    data = []

# OK — not flagged
if data is None:
    data = []

# OK — not flagged (in string, not actual comparison)
doc = "Check if value == None"
```

---

## False Positives: Practical Discipline

A bare pattern like `$X.foo()` matches thousands of hits across a codebase. This section explains how to narrow effectively.

### Why Patterns Blow Up

```yaml
# BAD — matches everything
rule:
  pattern: '$X.method()'
```

This matches every method call because `$X` is unconstrained. You get noise, not signal.

### Narrowing Strategies

**1. Use `constraints` to restrict metavariables:**

```yaml
rule:
  pattern: '$OBJ.value'
  constraints:
    $OBJ:
      kind: identifier  # Only simple names, not properties
      regex: '^data'    # Names starting with 'data'
```

**2. Use `inside` to require context:**

```yaml
rule:
  pattern: 'fetch($URL)'
  inside:
    pattern: 'useEffect(() => { $$$ }, $$$)'  # Only inside React effects
```

**3. Use `follows` / `precedes` for ordering:**

```yaml
rule:
  pattern: 'console.log($MSG)'
  follows:
    pattern: 'import $$$'  # Only after imports (debug logs at top)
```

**4. Use `has` for internal structure:**

```yaml
rule:
  pattern: 'function $NAME($$$)'
  has:
    pattern: 'await $$$'  # Async functions only
```

**5. Use `#` to trim leading context:**

When your pattern accidentally captures too much from the left, use `#` to anchor:

```yaml
# Matches only the statement, not the preceding line
rule:
  pattern: '# $X = $Y'  # The # anchors to statement start
```

### Filtering Results with `--json`

Pipe results into external filters for complex queries:

```bash
# Find matches in specific directories only
ast-grep -p 'console.log($$$)' --json src/ | \
  jq -r '.[] | select(.file_path | contains("component")) | .file_path'

# Count matches per file
ast-grep -p 'TODO' --json src/ | \
  jq -r '.[].file_path' | sort | uniq -c | sort -rn
```

### The Discipline

**Write rules against a known-bad sample, not against the codebase.**

1. Create a test file with the exact defect you want to catch
2. Write the rule to match that file
3. Add positive cases (should match) and negative cases (should not match)
4. Only then run against the full codebase

This avoids tuning rules on noise and ensures they catch what you intend.

---

## When NOT to Use ast-grep

Not every code search problem needs AST parsing. Use the right tool.

### Use grep/ripgrep Instead

| Scenario | Tool | Reason |
|----------|------|--------|
| Plain text search (comments, strings, literals) | `grep` / `rg` | Faster, simpler, no parsing overhead |
| Formatting/whitespace questions | `grep` | AST ignores whitespace |
| Simple literal patterns | `grep` | No AST needed for `TODO` or `FIXME` |
| Whole-file rewrites | `sed` / formatter | Safer, more predictable |
| Languages with poor parser support | `grep` | ast-grep may not parse correctly |

### The Check

- **If the pattern needs to ignore syntax** (searching text anywhere, including comments) → use grep
- **If the pattern needs to respect syntax** (only match actual function calls, not strings containing function calls) → use ast-grep

### Languages with Weaker Parsing

ast-grep parses most C-family languages well. Some languages have limitations:

| Language | Status | Notes |
|----------|--------|-------|
| JavaScript, TypeScript, Python, Go, Rust | ✅ Solid | Full AST support |
| Java, C, C++ | ✅ Good | Mature parsers |
| PHP, Ruby, Kotlin | ⚠️ Partial | Some edge cases |
| Scala, Haskell | ⚠️ Limited | Complex grammar challenges |
| Custom DSLs | ❌ Not supported | No parser available |

Check if your language parses correctly:

```bash
ast-grep -p 'pattern' --lang python --debug-query=ast file.py
```

If the query fails or produces unexpected AST nodes, the language support may be incomplete.

---

## CI Integration

### Pre-commit Hook

Run rules on staged files before commits:

```yaml
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/ast-grep/ast-grep
    rev: v0.25.0
    hooks:
      - id: ast-grep
        args: [scan, --config, sgconfig.yml]
```

Or invoke directly:

```bash
#!/bin/bash
# .git/hooks/pre-commit
ast-grep scan --config sgconfig.yml --error-on-match $(git diff --cached --name-only)
```

### CI Job (GitHub Actions)

Fail the build on rule violations:

```yaml
# .github/workflows/lint.yml
name: ast-grep lint
on: [push, pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install ast-grep
        run: npm install -g ast-grep
      - name: Scan with rules
        run: |
          ast-grep scan --config sgconfig.yml --error-on-match src/
```

The `--error-on-match` flag causes non-zero exit on any error-severity finding.

### Baseline Workflow for Existing Codebases

Adding rules to a large existing codebase creates noise — hundreds of matches on day one. Use baselining:

**Day 1: Capture existing findings as baseline**

```bash
ast-grep scan --config sgconfig.yml --json src/ > baseline-findings.json
```

**CI: Only fail on NEW findings**

```bash
ast-grep scan --config sgconfig.yml --json src/ | \
  jq -S '. - (input | .)' baseline-findings.json > new-findings.json

if [ "$(jq length new-findings.json)" -gt 0 ]; then
  echo "New issues detected:"
  jq -r '.[] | "\(.file_path):\(.range.start.line) \(.message)"' new-findings.json
  exit 1
fi
```

**Gradual cleanup:** Fix findings incrementally, updating the baseline after each batch.

---

## CLI Commands Reference

### Pattern Search

```bash
# Basic pattern search
ast-grep run --pattern 'console.log($ARG)' --lang javascript src/

# Short form
ast-grep -p 'console.log($$$ARGS)' src/

# Show context around matches
ast-grep -p 'TODO' --context 3 src/
```

### Rewrite Operations

```bash
# Search and rewrite
ast-grep -p '$OBJ.val && $OBJ.val()' --rewrite '$OBJ.val?.()' src/

# Interactive mode
ast-grep -p '$PROP && $PROP()' -r '$PROP?.()' --interactive src/

# Apply all without confirmation
ast-grep -p 'var $X' -r 'let $X' --update-all src/
```

### Linting

```bash
# Scan with rules file
ast-grep scan --rule rules/no-console.yml src/

# Scan with config
ast-grep scan --config sgconfig.yml src/

# Inline rule
ast-grep scan --inline-rules '
id: no-debugger
language: JavaScript
rule:
  pattern: debugger
' src/

# SARIF output for CI
ast-grep scan --format sarif src/
```

---

## Pattern Syntax Quick Reference

### Metavariables

| Syntax | Matches |
|--------|---------|
| `$VAR` | Single AST node |
| `$$$VARGS` | Zero or more nodes (variadic) |

### Relational Operators

| Operator | Meaning |
|----------|---------|
| `inside` | Pattern must be inside another pattern |
| `has` | Pattern must contain a sub-pattern |
| `follows` | Pattern appears after another |
| `precedes` | Pattern appears before another |
| `all` | All sub-patterns must match |
| `any` | At least one sub-pattern must match |
| `not` | Negation |

---

## Rule Configuration

### YAML Rule Structure

```yaml
id: unique-rule-id
message: "Human-readable message"
severity: error|warning|info
language: JavaScript|Python|Rust|...
rule:
  pattern: 'code pattern with $METAVARIABLES'
  # Optional relational operators
  inside:
    pattern: 'containing context'
  constraints:
    $METAVARIABLE:
      kind: node_kind
      regex: 'pattern'
fix:
  rewrite: 'replacement pattern'
```

### Project Configuration (sgconfig.yml)

```yaml
rules:
  - id: no-console
    message: "No console.log in production"
    severity: warning
    language: JavaScript
    rule:
      pattern: console.log($ARG)
```

---

## Best Practices

1. **Test patterns before rewriting** — Run without `--update-all` first
2. **Use `--interactive` for important changes** — Confirm each replacement
3. **Explicit language flag** — Avoid ambiguity with `--lang`
4. **Commit before mass rewrites** — Easy rollback if needed
5. **Write rules against known-bad samples** — Not against full codebase

---

## Deep Dives

For advanced topics, load these reference files on demand:

- **references/rule-authoring.md** — Deep dive into rule syntax, constraints, and advanced patterns (load when writing complex rules)
- **references/migration-guide.md** — ast-grep version migration notes (load when upgrading)

---

## References

- **ast-grep Docs**: https://ast-grep.github.io/
- **ast-grep GitHub**: https://github.com/ast-grep/ast-grep
- **Rule Schema**: https://ast-grep.github.io/reference/schema.html

Files in this skill

  • SKILL.md13 KB
  • references/migration-guide.md727 B
  • references/rule-authoring.md2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…