--- 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