Skip to content
Back to Quality Assurance

Static Analysis (SAST)

Scan source code for security patterns without running it — catch injection, auth flaws, and crypto misuse early.

Why

SAST analyzes the source code (or an intermediate representation) for patterns that indicate vulnerabilities: a SQL query built by string concatenation, a password hashed with MD5, a route handler with no auth check, a user input flowing into an eval. It runs in seconds on every PR, before the code is deployed, so it catches the vulnerability at the earliest possible point — when the developer is still in the context and the fix is a one-line change. Compared to DAST (which finds the bug after the app is running) or pen testing (which finds it after deploy), SAST is the cheapest and fastest feedback loop for code-level vulnerabilities. The false-positive rate is the main challenge, which is why tool selection and rule tuning matter.

When

On every PR (fast ruleset, <30s) and nightly on the full repo (comprehensive ruleset). Tune the ruleset to suppress false positives within the first two weeks of adoption — an untuned SAST tool is ignored within a month.

How

1. Choose a SAST tool that fits your language and noise tolerance
   - Semgrep (free, custom rules, fast, low noise — best for most teams)
   - CodeQL (GitHub, semantic analysis, deep but slower)
   - SonarQube (broad, quality + security, self-hosted or cloud)
   - Commercial: Checkmarx, Veracode, Snyk Code (deeper, pricier)

2. Start with the curated ruleset, then tune
   - Enable the language's standard rules (security, best-practices)
   - Run on the codebase, collect findings, and triage each: true positive or false positive
   - Suppress false positives with inline comments (// nosemgrep) and documented reasons

3. Wire into CI on every PR
   - Run the fast ruleset (<30s) on changed files only (Semgrep --diff)
   - Fail the PR on new findings (not pre-existing — track those separately)
   - Post findings as PR review comments so the developer sees them in context

4. Run the comprehensive scan nightly on the full repo
   - Catches vulnerabilities introduced by refactors that touch many files
   - Produces a trend report — is the finding count going down or up over time?

5. Treat SAST as a guide, not a gate, for low-severity findings
   - Block on high/critical (SQL injection, hardcoded secrets) — these are real bugs
   - Warn on medium/low — let the developer acknowledge and move on, do not block the PR

Example

# .semgrep.yml — custom rules for a Node.js API
rules:
  - id: no-sql-string-concat
    pattern: db.query(`...${...}...`)
    message: "SQL query built with string interpolation — use parameterized queries"
    severity: ERROR
    languages: [typescript]

  - id: no-md5-password
    pattern: crypto.createHash("md5")
    message: "MD5 is broken — use bcrypt or argon2 for passwords"
    severity: ERROR

  - id: route-missing-auth
    pattern: app.get($ROUTE, $HANDLER)
    message: "Route may be missing auth middleware — verify"
    severity: WARNING

# CI: .github/workflows/sast.yml
- uses: returntocorp/semgrep-action@v1
  with:
    config: .semgrep.yml
  # Fails the PR on ERROR findings, posts WARNINGs as review comments.

# Sample finding in a PR:
#  ❌ src/db/orders.ts:14
#     db.query(`SELECT * FROM orders WHERE id = ${req.params.id}`)
#     → SQL injection: user input flows into query. Fix:
#       db.query("SELECT * FROM orders WHERE id = $1", [req.params.id])

Common Problems

  • ⚠An untuned SAST tool produces hundreds of false positives, the team marks them all as "ignored," and then a real vulnerability is also ignored because the signal-to-noise ratio trained everyone to dismiss findings.
  • ⚠Running SAST only on the main branch (not on PRs) means the developer gets the finding days after they wrote the code, when they have lost the context and the fix is no longer a one-line change.
  • ⚠Blocking the PR on every finding including low-severity style issues frustrates developers and leads to the tool being disabled — block on high/critical, warn on the rest.
  • ⚠Using only the default ruleset without customizing for your codebase — the generic rules miss your specific patterns (a custom auth middleware) and flag patterns that are safe in your context (a sanitized input).

Best Practices

  • ✓Tune the ruleset in the first two weeks: triage every finding, suppress false positives with documented reasons, and write custom rules for your codebase's specific patterns — a tuned SAST tool is valued, an untuned one is ignored.
  • ✓Run the fast ruleset on changed files in the PR (Semgrep --diff) so the developer gets findings in seconds while still in context, and run the comprehensive scan nightly on the full repo.
  • ✓Post findings as PR review comments with the fix suggestion inline — the developer sees the problem and the solution in the same view, and the fix takes minutes not days.
  • ✓Track the finding-count trend over time — a rising trend means new code is introducing more vulnerabilities, which is a process problem (training, code review) not just a tooling problem.