| name | sast-scanning |
| description | Perform static application security testing with tools like Semgrep, CodeQL, and SonarQube. Identify security vulnerabilities in source code before deployment. Use when implementing secure SDLC, code review automation, or security gates in CI/CD pipelines. |
| license | MIT |
| metadata | {"author":"devops-skills","version":"1.0"} |
SAST Scanning
Identify security vulnerabilities in source code through static analysis.
When to Use This Skill
Use this skill when:
- Implementing secure SDLC practices
- Adding security gates to CI/CD
- Automating code security reviews
- Finding vulnerabilities before deployment
- Meeting compliance requirements
Prerequisites
- Source code access
- CI/CD pipeline
- SAST tool installation
Tool Comparison
| Tool | License | Languages | Best For |
|---|
| Semgrep | OSS/Commercial | 30+ | Custom rules, speed |
| CodeQL | Free (GitHub) | 10+ | Deep analysis |
| SonarQube | OSS/Commercial | 25+ | Quality + Security |
| Bandit | OSS | Python | Python projects |
| Brakeman | OSS | Ruby | Rails apps |
Semgrep
Installation
pip install semgrep
brew install semgrep
Basic Usage
semgrep --config auto .
semgrep --config p/security-audit .
semgrep --config p/owasp-top-ten .
semgrep --config p/ci .
semgrep --config p/python .
semgrep --config p/javascript .
semgrep --config auto --json -o results.json .
semgrep --config auto --sarif -o results.sarif .
Custom Rules
rules:
- id: hardcoded-password
patterns:
- pattern-either:
- pattern: password = "..."
- pattern: PASSWORD = "..."
- pattern: passwd = "..."
message: Hardcoded password detected
severity: ERROR
languages: [python, javascript, java]
metadata:
cwe: "CWE-798"
owasp: "A3:2017"
- id: sql-injection
patterns:
- pattern: |
$QUERY = "..." + $USER_INPUT + "..."
$DB.execute($QUERY)
message: Potential SQL injection
severity: ERROR
languages: [python]
metadata:
cwe:
[]
CI Configuration
name: Semgrep
on:
push:
branches: [main]
pull_request:
jobs:
semgrep:
runs-on: ubuntu-latest
container:
image: returntocorp/semgrep
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
run: semgrep ci
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
CodeQL
Setup
name: CodeQL Analysis
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * 0'
jobs:
analyze:
runs-on: ubuntu-latest
permissions:
security-events: write
actions: read
contents: read
strategy:
matrix:
language: ['javascript', 'python']
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v3
-
Custom Queries
// queries/sql-injection.ql
/**
* @name SQL Injection
* @description User input in SQL query
* @kind path-problem
* @problem.severity error
* @security-severity 9.0
* @precision high
* @id py/sql-injection
* @tags security
*/
import python
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.security.dataflow.SqlInjectionQuery
from SqlInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "SQL injection from $@.", source.getNode(), "user input"
SonarQube
Docker Setup
version: '3.8'
services:
sonarqube:
image: sonarqube:lts-community
ports:
- "9000:9000"
environment:
- SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_logs:/opt/sonarqube/logs
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_logs:
postgresql_data:
Scanner Configuration
# sonar-project.properties
sonar.projectKey=myproject
sonar.projectName=My Project
sonar.projectVersion=1.0
sonar.sources=src
sonar.tests=tests
sonar.exclusions=**/node_modules/**,**/vendor/**
sonar.language=py
sonar.python.coverage.reportPaths=coverage.xml
sonar.qualitygate.wait=true
CI Integration
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
- name: Quality Gate
uses: sonarsource/sonarqube-quality-gate-action@master
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Language-Specific Tools
Python (Bandit)
pip install bandit
bandit -r src/ -f json -o bandit-report.json
bandit -r src/ -c bandit.yaml
skips: ['B101', 'B601']
exclude_dirs: ['tests', 'venv']
assert_used:
skips: ['*_test.py', '*_tests.py']
JavaScript (ESLint Security)
npm install eslint eslint-plugin-security --save-dev
module.exports = {
plugins: ['security'],
extends: ['plugin:security/recommended'],
rules: {
'security/detect-object-injection': 'error',
'security/detect-non-literal-regexp': 'warn',
'security/detect-unsafe-regex': 'error',
'security/detect-buffer-noassert': 'error',
'security/detect-eval-with-expression': 'error',
'security/detect-no-csrf-before-method-override': 'error',
'security/detect-possible-timing-attacks': 'warn'
}
};
Ruby (Brakeman)
gem install brakeman
brakeman -o brakeman-report.json -f json
brakeman --no-exit-on-warn --no-exit-on-error -o report.html
Quality Gates
SonarQube Quality Gate
{
"name": "Security Gate",
"conditions": [
{
"metric": "new_security_rating",
"op": "GT",
"error": "1"
},
{
"metric": "new_vulnerabilities",
"op": "GT",
"error": "0"
},
{
"metric": "new_security_hotspots_reviewed",
"op": "LT",
"error": "100"
}
]
}
Custom Gate Script
#!/bin/bash
CRITICAL=$(cat results.json | jq '[.results[] | select(.severity == "critical")] | length')
HIGH=$(cat results.json | jq '[.results[] | select(.severity == "high")] | length')
echo "Critical: $CRITICAL, High: $HIGH"
if [ "$CRITICAL" -gt 0 ]; then
echo "FAILED: Critical vulnerabilities found"
exit 1
fi
if [ "$HIGH" -gt 5 ]; then
echo "FAILED: Too many high severity vulnerabilities"
exit 1
fi
echo "PASSED: Security gate"
exit 0
Common Issues
Issue: Too Many False Positives
Problem: Alerts on safe code patterns
Solution: Tune rules, add suppressions, use baseline
Issue: Slow Scans
Problem: SAST taking too long in CI
Solution: Incremental scanning, parallel execution, exclude test files
Issue: Missing Coverage
Problem: Vulnerabilities not detected
Solution: Add custom rules, combine multiple tools
Best Practices
- Run on every PR/commit
- Establish baseline for existing code
- Prioritize by severity and exploitability
- Maintain custom rules for your codebase
- Integrate with IDE for early feedback
- Track trends over time
- Document false positive suppressions
- Combine with DAST for comprehensive coverage
Related Skills