Stage 5 · Platform
Testing & Quality Gates
SAST Scanning
Finding code vulnerabilities with CodeQL, Semgrep, Bandit, and SARIF pull request annotations.
What Is SAST?
Static Application Security Testing (SAST) analyzes source code without executing it. It finds security vulnerabilities like SQL injection, cross-site scripting, buffer overflows, and hardcoded credentials. SAST runs early in the pipeline, catching issues before the code is even built.
SAST tools parse the code into an abstract syntax tree and apply rules to identify vulnerable patterns. They can find issues that dynamic testing misses — insecure configurations, missing input validation, and deprecated API usage.
CodeQL
jobs:
codeql:
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read
strategy:
fail-fast: false
matrix:
language: ["javascript", "python"]
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"CodeQL creates a database from your source code and runs queries against it. The security-extended query suite includes additional checks beyond the default. Results appear in the repository's Security tab and in pull request annotations.
Semgrep
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
p/secrets
.semgrep/
generateSarif: true
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: semgrep.sarif
- name: Semgrep diff scan
if: github.event_name == 'pull_request'
run: |
semgrep scan \
--config p/security-audit \
--diff \
--sarif \
-o semgrep-diff.sarif \
--target autoSemgrep applies pattern-based rules to find vulnerabilities. The p/security-audit preset covers common security issues. The diff scan only checks code changed in the PR, reducing noise from pre-existing issues.
rules:
- id: avoid-sql-string-interpolation
pattern: |
$QUERY = f"SELECT ... FROM ... WHERE ... = {$VAR}"
...
$CONN.execute($QUERY)
message: >
SQL query uses f-string interpolation. Use parameterized
queries instead: cursor.execute("SELECT ... WHERE id = %s", (id,))
languages: [python]
severity: ERROR
metadata:
cwe:
- "CWE-89: SQL Injection"
owasp:
- A03:2021 InjectionCustom Semgrep rules catch project-specific vulnerabilities. The rule matches f-string SQL queries that are passed to execute() — a classic SQL injection pattern. The metadata links to CWE and OWASP categories.
Bandit for Python
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Bandit
run: pip install bandit[toml]
- name: Run Bandit
run: |
bandit -r src/ \
-f json \
-o bandit-results.json \
--severity-level medium \
--confidence-level medium
- name: Fail on high-severity findings
run: |
python -c "
import json
with open('bandit-results.json') as f:
results = json.load(f)
high = [r for r in results['results'] if r['issue_severity'] == 'HIGH']
if high:
print(f'Found {len(high)} high-severity issues')
for r in high:
print(f' {r["filename"]}:{r["line_number"]}: {r["issue_text"]}')
exit(1)
"Bandit analyzes Python code for common security issues. The JSON output enables custom filtering — only high-severity findings fail the pipeline. Medium-severity findings are reported but do not block merging.
SARIF Reports
SARIF (Static Analysis Results Interchange Format) is a standard format for sharing SAST results. GitHub, GitLab, and Azure DevOps all support SARIF. Upload SARIF files to get inline annotations on pull requests.
Use CodeQL's upload-sarif action to upload results from any SAST tool that produces SARIF. This consolidates findings from Semgrep, Bandit, and CodeQL into a single Security tab in your repository.
SAST in the Pipeline
- Run SAST on every pull request — catch vulnerabilities before they merge.
- Use severity thresholds — fail on high/critical, report on medium/low.
- Enable SARIF annotations — developers see findings inline on the PR.
- Scan the full codebase periodically — PR-only scans miss issues in unchanged code.
- Tune rules aggressively — disable rules that produce more noise than signal.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.