Skip to main content
Cloud & AI Hub
Browse
Glossary AI Directory Playgrounds Models Prompts Explainers Strategy Matrix Benchmark Decoder
intermediate 7 mins Read

Understanding SAST Security Testing

How SAST tools analyze source code for vulnerabilities without execution, using data flow analysis, taint tracking, and pattern matching.

Last reviewed: July 27, 2026

Introduction

Static Application Security Testing (SAST) analyzes source code, bytecode, or binary code for security vulnerabilities without executing the program. Unlike penetration testing (which requires a running application) or DAST (Dynamic Application Security Testing, which sends live attack traffic), SAST runs early in the development cycle — in your IDE or CI pipeline — finding vulnerabilities before they reach staging. Common vulnerabilities found: SQL injection, XSS, hardcoded secrets, insecure cryptography, and path traversal.

flowchart TD
    A["Parse source into an Abstract Syntax Tree"] --> B["Trace tainted (external) data through the program"]
    B --> C["Match against vulnerability rule patterns"]
    C --> D["Flag findings in the CI pipeline"]
    D --> E["Triage: true positive vs. false positive"]

How SAST Works Technically

1. Abstract Syntax Tree (AST) Analysis

SAST tools parse source code into an Abstract Syntax Tree and traverse it looking for dangerous patterns:

Source code:
  query = "SELECT * FROM users WHERE id = " + user_input

AST:
  BinaryExpression (+)
    ├── StringLiteral("SELECT * FROM users WHERE id = ")
    └── Identifier(user_input)   ← TAINT SOURCE (external input)

Pattern match: String concatenation into SQL query → SQL injection

2. Data Flow / Taint Analysis

More sophisticated SAST traces tainted data (data from external sources) through the program:

# SAST taint tracking example
def get_user(request):
    user_id = request.GET['id']    # TAINT SOURCE: HTTP parameter
    
    user_id = user_id.strip()      # Sanitization? Not for SQL injection
    
    query = f"SELECT * FROM users WHERE id = {user_id}"  # TAINT SINK: SQL query
    # SAST flags: tainted data flows from HTTP request to SQL query without parameterization
    
    return db.execute(query)
# SAST sees this as safe (parameterized — taint doesn't reach SQL syntax)
def get_user_safe(request):
    user_id = request.GET['id']    # TAINT SOURCE
    result = db.execute("SELECT * FROM users WHERE id = %s", [user_id])  # TAINT SINK (safe)
    return result

3. Common SAST Tools

# Semgrep — fast, rule-based, supports 20+ languages
pip install semgrep
semgrep --config=p/python-security . --output=results.json

# Bandit — Python-specific security linter
pip install bandit
bandit -r src/ -f json -o bandit-report.json

# SonarQube — comprehensive SAST platform (runs as a service)
# ESLint with security plugins (JavaScript)
npm install eslint-plugin-security

4. CI/CD Integration

# GitHub Actions: Run SAST on every PR
name: SAST Security Scan

on:
  pull_request:
    branches: [main, develop]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: semgrep/semgrep-action@v1
      with:
        config: >-
          p/python-security
          p/secrets
          p/owasp-top-ten
      env:
        SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}

  bandit:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: pip install bandit
    - run: bandit -r src/ -f json -o bandit-report.json
    - uses: actions/upload-artifact@v4
      with:
        name: bandit-report
        path: bandit-report.json

5. Interpreting Results — False Positives

# Bandit finding (may be false positive in some contexts)
# [B608:hardcoded_sql_expressions] Possible SQL injection via format string
query = "SELECT * FROM {} WHERE id = {}".format(table_name, record_id)

# If table_name is from a whitelist (not user input), this is a false positive
ALLOWED_TABLES = {'users', 'products', 'orders'}
if table_name not in ALLOWED_TABLES:
    raise ValueError("Invalid table name")

# Add suppression comment to acknowledge and justify
query = "SELECT * FROM {} WHERE id = %s".format(table_name)  # nosec B608 — table from allowlist

Key Takeaways

  • SAST analyzes source code without execution — it integrates into CI pipelines and finds vulnerabilities before deployment.
  • Taint analysis tracks data from external sources (HTTP, files, DB) to dangerous sinks (SQL queries, system calls, HTML output) — the core of modern SAST.
  • SAST has a false positive problem — some findings require human judgment. Use suppression comments with justifications to manage noise.
  • Run SAST in CI on every PR with a high-severity block policy — low-severity findings can be reported without blocking.
  • SAST complements but doesn’t replace DAST and penetration testing — some vulnerabilities (authentication logic, business logic flaws) require dynamic testing.

Historical figures, architectures, and capabilities are for informational purposes only. Not technical, professional, legal, or financial advice. Sources: Research papers, developer documentation.