segrep-sast scanner

Opt-in native Tree-sitter SAST engine and rule system

Native static analysis engine for Segrep, built in Go with Tree-sitter parsing, YAML AST rules, and optional dataflow/taint analysis.

segrep-sast is an opt-in scanner alongside the default core set (Trivy, Semgrep, Syft, Gitleaks, Checkov). Semgrep remains the default SAST source; enable segrep-sast when you want Tree-sitter–based rules, structural dataflow paths, and per-finding AI enrichment.


Architecture

Repository
    │
    ▼
File discovery (Go worker pool)
    │
    ▼
Tree-sitter parsers (Go, JS, TS, Python)
    │
    ▼
Normalized AST
    ├──────────────────┐
    ▼                  ▼
Pattern engine    Dataflow engine
    │                  │
    └────────┬─────────┘
             ▼
       Findings (JSON)
             │
             ▼
  packages/scanner-segrep-sast  →  scanner-core  →  CLI / worker / dashboard
ComponentLocation
Go engineengines/sast/
TypeScript adapterpackages/scanner-segrep-sast/
Built-in rulesengines/sast/rules/
Example configdocs/segrep.example.yaml
Vulnerable fixturesapps/cli/fixtures/sast-vulnerable/

Quick start

The Docker image and local dev builds include the segrep-sast binary.

# Opt-in scan (Semgrep is not run unless also listed)
pnpm segrep scan . --scanners segrep-sast

# Combine with other scanners
pnpm segrep scan . --scanners semgrep,segrep-sast

# JSON output for CI
pnpm segrep scan . --scanners segrep-sast --format json

Docker:

docker run --rm -v "$(pwd):/repo:ro" security-scanner:local \
  scan /repo --scanners segrep-sast

Standalone engine

segrep-sast scan /path/to/repo \
  --format json \
  --rules ./engines/sast/rules \
  --baseline=false

Configuration

Engine-specific settings live in segrep.yaml at the repo root (or .segrep/sast.yaml). Scan policy (fail_on, allowlists, ignore paths) stays in .segrep/policy.yml — same as other scanners.

Copy the example:

cp docs/segrep.example.yaml segrep.yaml

segrep.yaml reference

FieldDescription
languagesLanguages to scan: go, javascript, typescript, python
rulesRule packs: builtin://owasp, builtin://secrets, builtin://injection, or paths to custom YAML
excludeGlob patterns skipped during file discovery
dataflow.enabledEnable taint analysis (default: true)
dataflow.max_call_depthInterprocedural call depth (default: 1)
baseline.pathSQLite DB for baselines and suppressions
workersParallel parse workers (default: CPU count)

Policy integration

To run only segrep-sast in CI, add to .segrep/policy.yml:

scanners:
  - segrep-sast
fail_on: high

Allowlist SAST rule IDs by ruleId or fingerprint, same as Semgrep findings.


CLI reference (segrep-sast)

segrep-sast scan <root> [flags]
segrep-sast version
FlagDescription
--configPath to segrep.yaml (auto-detected if omitted)
--rulesBuilt-in rules directory (default: next to binary or engines/sast/rules)
--formatOutput format (json only)
--outputWrite JSON to file instead of stdout
--languagesComma-separated language override
--excludeAdditional exclude globs
--workersWorker pool size
--changed-filesComma-separated paths for incremental scan
--no-dataflowDisable taint analysis
--baselineApply baseline filtering (true/false, default: true)
--scan-idScan ID recorded in baseline DB

Rule format

Rules are YAML files under engines/sast/rules/ or custom directories.

Pattern mode

Matches AST nodes directly (secrets, dangerous APIs):

id: eval-js
language: javascript
mode: pattern
pattern:
  kind: call_expression
  callee: eval
message: Use of eval() can lead to code injection
severity: HIGH
cwe: CWE-94

Taint mode

Tracks user input from sources to sinks:

id: sql-injection-js
language: javascript
mode: taint
taint:
  sources:
    - kind: member_expression
      name: req.body
    - kind: member_expression
      name: req.query
  sinks:
    - kind: call_expression
      callee: query
  sanitizers:
    - kind: call_expression
      callee: escape
message: User input reaches SQL query without sanitization
severity: HIGH
cwe: CWE-89

Pattern constraints

FieldDescription
kindTree-sitter node type (e.g. call_expression, identifier)
nameIdentifier or member path (e.g. req.body)
textSubstring match on node text
calleeCallee name for call expressions
parentNested pattern for parent node
childNested pattern for a child node
ancestorNested pattern for any ancestor
whereField constraints (e.g. callee: query)

Built-in rule packs

PackExamples
secretsHardcoded passwords, API key patterns
owaspSQL injection (taint), XSS sinks, path traversal
injectioneval, exec, os.system, dangerouslySetInnerHTML

Dataflow analysis

Phase 1 capabilities:

  • Per-function control-flow from AST
  • Intraprocedural taint propagation (assignments, direct data dependencies)
  • Source / sink / sanitizer matching from YAML taint rules
  • Same-file call graph
  • One-level interprocedural taint (caller → callee → sink)

Limits (Phase 1): no pointer/alias analysis, closure capture across scopes, cross-package resolution, or reflection handling.

Taint findings include a dataflowPath in the JSON report:

{
  "dataflowPath": [
    { "file": "src/api.js", "line": 10, "symbol": "req.body", "role": "source" },
    { "file": "src/api.js", "line": 12, "symbol": "query", "role": "sink" }
  ]
}

JSON output schema

Version 1.0.0 — consumed by packages/scanner-segrep-sast:

{
  "schemaVersion": "1.0.0",
  "scanner": "segrep-sast",
  "version": "0.1.0",
  "root": "/repo",
  "scannedAt": "2026-06-28T12:00:00Z",
  "filesScanned": 42,
  "rulesApplied": 18,
  "findings": [
    {
      "id": "abc123...",
      "ruleId": "sql-injection-js",
      "title": "User input reaches SQL query without sanitization",
      "severity": "high",
      "filePath": "src/api.js",
      "line": 12,
      "snippet": "db.query('SELECT ...' + req.body.id)",
      "cweId": "CWE-89",
      "fingerprint": "...",
      "category": "sast",
      "dataflowPath": []
    }
  ]
}

AI enrichment

When segrep-sast findings are present and OPENAI_API_KEY is set, Segrep runs per-finding enrichment (explanation, exploit path, confidence, suggested fix). Results are stored in findings.raw_json.ai and shown in the dashboard on expandable rows.

Scan-level AI summaries (all scanners) still run via @segrep/ai-remediation.


SQLite storage (engine-local)

The Go engine uses SQLite for:

TablePurpose
baselinesSuppress findings seen in prior scans
suppressionsRule/file/line ignore entries
parse_cacheFile content hash for incremental optimization

Default path: .segrep/sast-baseline.db (per repo).

Platform scan history remains in PostgreSQL for the hosted dashboard — SQLite is not a replacement.


Development

Build the Go engine

Requires Go 1.22+ and CGO (Tree-sitter bindings):

cd engines/sast
CGO_ENABLED=1 go build -o segrep-sast ./cmd/segrep-sast

Without local Go, build via Docker:

docker run --rm -v "$(pwd)/engines/sast:/src" -w /src golang:1.23-bookworm \
  bash -c "apt-get update -qq && apt-get install -y -qq gcc libc6-dev && \
           CGO_ENABLED=1 go build -o segrep-sast ./cmd/segrep-sast"

Run tests

# Go matcher tests
cd engines/sast && CGO_ENABLED=1 go test ./...

# TypeScript parser tests
pnpm nx run scanner-segrep-sast:test

Scan fixtures

./engines/sast/segrep-sast scan ./apps/cli/fixtures/sast-vulnerable \
  --rules ./engines/sast/rules --baseline=false

Docker image

The multi-stage infrastructure/docker/Dockerfile builds segrep-sast and installs:

  • Binary: /usr/local/bin/segrep-sast
  • Rules: /usr/local/share/segrep-sast/rules

Build arg: SEGREP_SAST_VERSION (default 0.1.0).


Dashboard

For segrep-sast findings, the scan detail page shows:

  • Rule ID and line number
  • Expandable dataflow path
  • AI enrichment (explanation, confidence, suggested fix)

No database migration is required — fields are read from raw_json.


Comparison with Semgrep

Semgrep (core)segrep-sast (opt-in)
Default scanYes (--scanners all)No — explicit opt-in
Rule formatSemgrep YAMLSegrep YAML (AST/taint)
ParserSemgrep (OCaml)Tree-sitter (in-process Go)
Dataflow pathsLimited in unified modelNative dataflowPath
AI per-findingNoYes (when API key set)
Best forBroad rule ecosystemCustom rules, dataflow, AI UX

Use both together when you want Semgrep's rule corpus plus Segrep-native dataflow and enrichment:

pnpm segrep scan . --scanners semgrep,segrep-sast