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

Understanding Static Application Security Testing (SAST)

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

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.