Static Application Security Testing (SAST)

SAST Best Practices & False Positive Management

3 min read

The biggest challenge with SAST isn't finding vulnerabilities—it's managing the noise. Here's how to make SAST actionable.

The False Positive Problem

SAST tools often report issues that aren't real vulnerabilities:

Finding TypeDescriptionAction
True PositiveReal vulnerabilityFix it
False PositiveNot actually exploitableSuppress it
True NegativeNo issue, correctly ignoredNone
False NegativeMissed vulnerabilityDangerous!

A noisy SAST tool leads to:

  • Alert fatigue: Developers ignore all findings
  • Wasted time: Investigating non-issues
  • Slow pipelines: Reviewing hundreds of findings

Tuning SAST Rules

1. Start with High-Confidence Rules

# Semgrep: Start strict, expand later
semgrep --config p/security-audit \
        --severity ERROR \
        --exclude-rule "generic.secrets.gitleaks.*"

2. Create Baseline Files

Track known findings to avoid re-reviewing:

# Create baseline (first run)
semgrep --config auto --json > .semgrep-baseline.json

# Run with baseline (subsequent runs)
semgrep --config auto --baseline-commit HEAD~1

3. Customize Rule Severity

# .semgrep/settings.yml
rules:
  - id: hardcoded-password
    severity: ERROR  # Block pipeline

  - id: missing-csrf-token
    severity: WARNING  # Warn but don't block

Suppressing False Positives

Inline Suppression (When Necessary)

# nosemgrep: hardcoded-password
TEST_PASSWORD = "test123"  # This is intentional for testing

# Or with reason
password = get_from_vault()  # nosemgrep: hardcoded-password (fetched from Vault)

File-Level Exclusions

# .semgrep.yml
exclude:
  - "tests/**"
  - "*.test.js"
  - "migrations/**"
  - "vendor/**"

Pattern-Based Suppression

# Suppress specific patterns
rules:
  - id: sql-injection
    patterns:
      - pattern: cursor.execute($QUERY)
      - pattern-not: cursor.execute($QUERY, $PARAMS)  # Parameterized = safe

Triage Workflow

Establish a consistent process:

┌─────────────┐
│ New Finding │
└──────┬──────┘
┌─────────────────┐     Yes    ┌──────────────┐
│ Is it a real    │ ─────────▶ │ Create Issue │
│ vulnerability?  │            │ & Fix        │
└────────┬────────┘            └──────────────┘
         │ No
┌─────────────────┐     Yes    ┌──────────────┐
│ Will it recur   │ ─────────▶ │ Update Rule  │
│ frequently?     │            │ or Baseline  │
└────────┬────────┘            └──────────────┘
         │ No
┌─────────────────┐
│ Add inline      │
│ suppression     │
└─────────────────┘

Metrics to Track

MetricTargetWhy
False Positive Rate< 20%Trust in tool
Mean Time to Triage< 1 hourDeveloper velocity
Mean Time to Remediate< 1 weekSecurity posture
Findings per 1000 LOCTrending downImprovement
Suppression Rate< 30%Rule quality

Best Practices Checklist

Tool Configuration

  • Start with curated rule sets (e.g., p/security-audit)
  • Exclude test files and generated code
  • Set appropriate severity thresholds
  • Enable incremental scanning for PRs

Process

  • Assign security champions to triage
  • Document suppression decisions
  • Review suppressions quarterly
  • Track trends over time

Developer Experience

  • Provide clear remediation guidance
  • Link to security training
  • Enable IDE integration for instant feedback
  • Celebrate security fixes

Common SAST Anti-Patterns

Anti-PatternProblemSolution
Scan everythingNoise overwhelms signalStart with critical paths
Block on all findingsDevelopers disable toolBlock only on critical/high
No baselineRe-review old findingsCreate and maintain baseline
No ownershipFindings rotAssign to code owners
Set and forgetRules become staleReview rules quarterly

In the next module, we'll tackle dependency and container security with SCA tools. :::

Quick check: how does this lesson land for you?

Quiz

Module 2: Static Application Security Testing (SAST)

Take Quiz
FREE WEEKLY NEWSLETTER

Stay on the Nerd Track

One email per week — courses, deep dives, tools, and AI experiments.

No spam. Unsubscribe anytime.