Static Application Security Testing (SAST)

Integrating SAST in GitHub Actions

3 min read

Automating SAST in your CI/CD pipeline ensures every code change is scanned. Here's how to set up comprehensive SAST with GitHub Actions.

Complete SAST Workflow

# .github/workflows/sast.yml
name: SAST Security Scan

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  semgrep:
    name: Semgrep Scan
    runs-on: ubuntu-latest
    container:
      image: returntocorp/semgrep

    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        run: semgrep ci
        env:
          SEMGREP_RULES: >-
            p/security-audit
            p/secrets
            p/owasp-top-ten

      - name: Upload SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif
        if: always()

  codeql:
    name: CodeQL Analysis
    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-extended

      - name: Autobuild
        uses: github/codeql-action/autobuild@v3

      - name: Perform Analysis
        uses: github/codeql-action/analyze@v3
        with:
          category: "/language:${{ matrix.language }}"

Breaking the Build on Findings

Configure when scans should block merges:

# Semgrep with severity thresholds
- name: Run Semgrep
  run: |
    semgrep ci \
      --sarif --output=semgrep.sarif \
      --error \
      --severity ERROR

# Exit codes:
# 0 = no findings
# 1 = findings found (blocks PR if --error flag)

Pull Request Integration

Add security comments directly to PRs:

# .github/workflows/pr-security.yml
name: PR Security Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  security-review:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Run Semgrep
        id: semgrep
        uses: returntocorp/semgrep-action@v1
        with:
          config: p/security-audit
          generateSarif: "1"

      - name: Comment on PR
        uses: actions/github-script@v7
        if: steps.semgrep.outputs.findings != '0'
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '## Security Scan Results\n\nFound security issues. Please review the Security tab.'
            })

Multi-Language Configuration

Handle polyglot repositories:

jobs:
  detect-languages:
    runs-on: ubuntu-latest
    outputs:
      languages: ${{ steps.detect.outputs.languages }}

    steps:
      - uses: actions/checkout@v4

      - name: Detect Languages
        id: detect
        run: |
          LANGS="[]"
          [ -f "package.json" ] && LANGS=$(echo $LANGS | jq '. + ["javascript"]')
          [ -f "requirements.txt" ] && LANGS=$(echo $LANGS | jq '. + ["python"]')
          [ -f "go.mod" ] && LANGS=$(echo $LANGS | jq '. + ["go"]')
          echo "languages=$LANGS" >> $GITHUB_OUTPUT

  sast:
    needs: detect-languages
    runs-on: ubuntu-latest
    strategy:
      matrix:
        language: ${{ fromJson(needs.detect-languages.outputs.languages) }}

    steps:
      - uses: actions/checkout@v4
      - name: Run Language-Specific SAST
        run: semgrep --config auto --lang ${{ matrix.language }} .

Caching for Speed

Speed up scans with caching:

- name: Cache Semgrep
  uses: actions/cache@v4
  with:
    path: ~/.semgrep
    key: semgrep-${{ hashFiles('.semgrep/**') }}

- name: Cache SonarQube
  uses: actions/cache@v4
  with:
    path: ~/.sonar/cache
    key: sonar-${{ runner.os }}-${{ hashFiles('**/sonar-project.properties') }}

Security Dashboard Integration

Push results to GitHub Security tab:

- name: Upload to GitHub Security
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif
    category: sast-semgrep

Results appear in:

  • SecurityCode scanning alerts
  • Pull requestsChecks tab
  • RepositorySecurityDependabot

Next, we'll cover SAST best practices and handling false positives. :::

Quiz

Module 2 Quiz: Static Application Security Testing

Take Quiz