Static analysis (SAST) — tools, false positives, CI integration
SAST (Static Application Security Testing) examines source code — or bytecode — without executing the application. It is the first automated line of defense in the development cycle: it catches problems early, when the cost of fixing them is lowest.
How SAST Works
The tool traverses the syntactic tree of the code and applies rules that detect insecure patterns:
Source code
│
▼
Parser / AST (Abstract Syntax Tree)
│
▼
Rules engine (taint analysis, dataflow, pattern matching)
│
▼
Findings report (vulnerability, line, severity)
Taint analysis tracks data from untrusted sources (HTTP input) to dangerous sinks (SQL query, exec, eval). If data reaches a sink without sanitization, the tool raises a finding.
Popular Tools by Language
| Language | Free / OSS | Commercial |
|---|---|---|
| Python | Bandit, Semgrep | Checkmarx, Veracode |
| JavaScript/TS | ESLint security plugin, Semgrep | Snyk Code |
| Java | SpotBugs + FindSecBugs | SonarQube |
| Go | Gosec, Semgrep | — |
| PHP | PHPCS Security Audit | Snyk Code |
| Multi-lang | Semgrep | SonarQube, Checkmarx |
Example with Bandit (Python)
# Install
pip install bandit
# Scan directory
bandit -r ./src -ll
# Typical output
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection via string-based query construction.
Severity: Medium Confidence: Medium
Location: src/db.py:42
Example with Semgrep
# Install
pip install semgrep
# Run with security ruleset
semgrep --config=p/owasp-top-ten ./src
# Output
src/auth.py:87: ERROR: Use of MD5 for password hashing detected.
False Positives — How to Handle Them
SAST has a false positive rate. A generic rule may flag secure code. Strategies:
- Triage by severity — focus on HIGH/CRITICAL first.
- Inline suppression — mark the finding as accepted with a justification comment:
result = hashlib.md5(data).hexdigest() # nosec B324 — non-crypto hash used for cache key
- Rule tuning — disable rules that generate noise in your project context.
- Human review — SAST surfaces candidates; the developer confirms whether it is a real vulnerability.
Never suppress findings without reading them. A masked false positive can hide a real vulnerability.
CI/CD Integration
SAST should run on every pull request and block merging when critical findings are detected.
# .github/workflows/sast.yml
name: SAST
on: [pull_request]
jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Bandit
run: |
pip install bandit
bandit -r ./src -ll --exit-zero-on-skipped -f json -o bandit-report.json
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: bandit-report
path: bandit-report.json
To block merges on critical findings, use --severity-level high and let the command return a non-zero exit code.
SAST Limitations
- Does not detect runtime vulnerabilities (race conditions, server misconfiguration).
- Does not test data flow between services (requires DAST or manual analysis).
- Quality depends on the rules: bad rules mean high noise or high miss rate.
SAST is complementary — it does not replace human code review or dynamic testing.