mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
420 lines
8.6 KiB
Markdown
420 lines
8.6 KiB
Markdown
---
|
|||
|
|
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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install via pip
|
||
|
|
pip install semgrep
|
||
|
|
|
||
|
|
# Or via Homebrew
|
||
|
|
brew install semgrep
|
||
|
|
```
|
||
|
|
|
||
|
|
### Basic Usage
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Run with default rules
|
||
|
|
semgrep --config auto .
|
||
|
|
|
||
|
|
# Run specific rulesets
|
||
|
|
semgrep --config p/security-audit .
|
||
|
|
semgrep --config p/owasp-top-ten .
|
||
|
|
semgrep --config p/ci .
|
||
|
|
|
||
|
|
# Scan specific languages
|
||
|
|
semgrep --config p/python .
|
||
|
|
semgrep --config p/javascript .
|
||
|
|
|
||
|
|
# Output formats
|
||
|
|
semgrep --config auto --json -o results.json .
|
||
|
|
semgrep --config auto --sarif -o results.sarif .
|
||
|
|
```
|
||
|
|
|
||
|
|
### Custom Rules
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# .semgrep/custom-rules.yaml
|
||
|
|
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: "CWE-89"
|
||
|
|
|
||
|
|
- id: insecure-random
|
||
|
|
pattern: random.random()
|
||
|
|
message: Use secrets module for security-sensitive randomness
|
||
|
|
severity: WARNING
|
||
|
|
languages: [python]
|
||
|
|
fix: secrets.token_hex()
|
||
|
|
```
|
||
|
|
|
||
|
|
### CI Configuration
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# .github/workflows/semgrep.yml
|
||
|
|
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
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# .github/workflows/codeql.yml
|
||
|
|
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
|
||
|
|
|
||
|
|
- name: Perform CodeQL Analysis
|
||
|
|
uses: github/codeql-action/analyze@v3
|
||
|
|
with:
|
||
|
|
category: "/language:${{ matrix.language }}"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Custom Queries
|
||
|
|
|
||
|
|
```ql
|
||
|
|
// 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
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# docker-compose.yml
|
||
|
|
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
|
||
|
|
|
||
|
|
```properties
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# GitHub Actions
|
||
|
|
- 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)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install
|
||
|
|
pip install bandit
|
||
|
|
|
||
|
|
# Run scan
|
||
|
|
bandit -r src/ -f json -o bandit-report.json
|
||
|
|
|
||
|
|
# With configuration
|
||
|
|
bandit -r src/ -c bandit.yaml
|
||
|
|
```
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# bandit.yaml
|
||
|
|
skips: ['B101', 'B601']
|
||
|
|
exclude_dirs: ['tests', 'venv']
|
||
|
|
|
||
|
|
assert_used:
|
||
|
|
skips: ['*_test.py', '*_tests.py']
|
||
|
|
```
|
||
|
|
|
||
|
|
### JavaScript (ESLint Security)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install
|
||
|
|
npm install eslint eslint-plugin-security --save-dev
|
||
|
|
```
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// .eslintrc.js
|
||
|
|
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)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install
|
||
|
|
gem install brakeman
|
||
|
|
|
||
|
|
# Run scan
|
||
|
|
brakeman -o brakeman-report.json -f json
|
||
|
|
|
||
|
|
# CI configuration
|
||
|
|
brakeman --no-exit-on-warn --no-exit-on-error -o report.html
|
||
|
|
```
|
||
|
|
|
||
|
|
## Quality Gates
|
||
|
|
|
||
|
|
### SonarQube Quality Gate
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
#!/bin/bash
|
||
|
|
# security-gate.sh
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
- [dast-scanning](../dast-scanning/) - Dynamic testing
|
||
|
|
- [dependency-scanning](../dependency-scanning/) - Dependency vulnerabilities
|
||
|
|
- [github-actions](../../../devops/ci-cd/github-actions/) - CI integration
|