Skip to content

Advanced Patterns

Advanced Security Scanning Patterns

Layer Multiple Scanners

No single security scanner detects all vulnerabilities. Layer CodeQL, Trivy, language-specific tools, and SBOM scanning for comprehensive coverage. Upload all results to Security tab with unique categories.

Container Scanning in CI/CD Pipeline

Complete container security workflow with build-time and runtime scanning.

name: Container Security Pipeline
on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:
    branches: [main]

permissions:
  contents: read

jobs:
  # Job 1: Scan Dockerfile for misconfigurations
  dockerfile-scan:
    name: Dockerfile Security Scan
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1
        with:
          persist-credentials: false

      # SECURITY: Trivy scans Dockerfile for misconfigurations
      - name: Scan Dockerfile with Trivy
        uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca  # 0.16.1
        with:
          scan-type: 'config'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-dockerfile.sarif'
          severity: 'CRITICAL,HIGH,MEDIUM'
          # SECURITY: Detect Dockerfile best practice violations
          scanners: 'config'

      - name: Upload Dockerfile scan results
        if: always()
        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          sarif_file: 'trivy-dockerfile.sarif'
          category: 'trivy-dockerfile'

  # Job 2: Build and scan container image
  image-scan:
    name: Container Image Security Scan
    runs-on: ubuntu-latest
    needs: dockerfile-scan
    permissions:
      contents: read
      security-events: write
      id-token: write  # For OIDC signing
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1
        with:
          persist-credentials: false

      # SECURITY: Build container image with Podman
      - name: Build container image
        run: |
          podman build \
            --tag myapp:${{ github.sha }} \
            --label org.opencontainers.image.revision=${{ github.sha }} \
            --label org.opencontainers.image.source=${{ github.repositoryUrl }} \
            .

      # SECURITY: Scan image filesystem and OS packages
      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca  # 0.16.1
        with:
          scan-type: 'image'
          image-ref: 'myapp:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-image.sarif'
          severity: 'CRITICAL,HIGH'
          # SECURITY: Scan OS packages and application dependencies
          scanners: 'vuln,secret,config'
          # SECURITY: Exit code 1 blocks deployment on vulnerabilities
          exit-code: '1'

      - name: Upload image scan results
        if: always()
        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          sarif_file: 'trivy-image.sarif'
          category: 'trivy-image'

      # SECURITY: Generate SBOM (Software Bill of Materials)
      - name: Generate SBOM
        run: |
          # SECURITY: SBOM tracks all dependencies in container
          podman run --rm \
            -v ./:/work \
            ghcr.io/anchore/syft:latest \
            myapp:${{ github.sha }} \
            -o spdx-json=sbom.spdx.json

      - name: Upload SBOM
        uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392  # v4.0.0
        with:
          name: sbom
          path: sbom.spdx.json
          retention-days: 90

      # SECURITY: Scan SBOM for vulnerabilities
      - name: Scan SBOM with Grype
        run: |
          curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
          grype sbom:sbom.spdx.json -o sarif > grype-results.sarif || true

      - name: Upload Grype SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          sarif_file: 'grype-results.sarif'
          category: 'grype-sbom'

Multi-Tool SARIF Aggregation

Aggregate multiple security tool results into unified Security tab view.

name: Aggregated Security Scan
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

jobs:
  aggregate-security-scan:
    name: Multi-Tool Security Scan
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1
        with:
          persist-credentials: false

      - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d  # v3.8.1
        with:
          node-version: '20'

      # SECURITY: Run multiple SAST tools for comprehensive coverage
      - name: Install dependencies
        run: npm ci

      # Tool 1: CodeQL
      - uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          languages: javascript
          queries: security-extended

      - uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4

      - uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          category: 'codeql-javascript'

      # Tool 2: ESLint with security plugin
      - name: Run ESLint security scan
        run: |
          npm install --save-dev eslint @microsoft/eslint-formatter-sarif
          npx eslint . \
            --ext .js,.ts \
            --format @microsoft/eslint-formatter-sarif \
            --output-file eslint-security.sarif || true

      - name: Upload ESLint SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          sarif_file: 'eslint-security.sarif'
          category: 'eslint-security'

      # Tool 3: Semgrep for pattern-based SAST
      - name: Run Semgrep scan
        run: |
          pip install semgrep
          semgrep --config=auto --sarif --output=semgrep-results.sarif . || true

      - name: Upload Semgrep SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          sarif_file: 'semgrep-results.sarif'
          category: 'semgrep'

      # Tool 4: Trivy filesystem scan
      - name: Run Trivy filesystem scan
        uses: aquasecurity/trivy-action@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca  # 0.16.1
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-fs.sarif'
          severity: 'CRITICAL,HIGH'

      - name: Upload Trivy SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          sarif_file: 'trivy-fs.sarif'
          category: 'trivy-filesystem'

Scheduled Vulnerability Scanning

Weekly scheduled scanning to detect newly-disclosed vulnerabilities in existing code.

name: Scheduled Security Scan
on:
  schedule:
    # SECURITY: Run every Monday and Thursday at 08:00 UTC
    # Catches vulnerabilities disclosed mid-week
    - cron: '0 8 * * 1,4'
  workflow_dispatch:
    # Allow manual trigger for ad-hoc scanning

permissions:
  contents: read

jobs:
  scheduled-scan:
    name: Scheduled Vulnerability Scan
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
      issues: write  # Create issue on vulnerability detection
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1
        with:
          persist-credentials: false

      # SECURITY: CodeQL scheduled scan
      - uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          languages: javascript
          queries: security-extended

      - uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4

      - uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a  # v2.13.4
        with:
          category: 'codeql-scheduled'

      # SECURITY: Dependency vulnerability scan
      - name: Run npm audit
        id: npm-audit
        run: |
          npm audit --audit-level=moderate --json > npm-audit.json || true
          VULN_COUNT=$(jq '.metadata.vulnerabilities.total' npm-audit.json)
          echo "vuln_count=$VULN_COUNT" >> $GITHUB_OUTPUT

      # SECURITY: Create issue if vulnerabilities found
      - name: Create vulnerability issue
        if: steps.npm-audit.outputs.vuln_count > 0
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea  # v7.0.1
        with:
          script: |
            const vulnCount = '${{ steps.npm-audit.outputs.vuln_count }}';
            const issueBody = `## Scheduled Security Scan Alert

            **Date**: ${new Date().toISOString()}
            **Vulnerabilities Found**: ${vulnCount}

            Automated security scan detected ${vulnCount} vulnerabilities in dependencies.

            ### Action Required
            1. Review npm audit report artifact
            2. Update vulnerable dependencies
            3. Run \`npm audit fix\` or manually update packages
            4. Re-run security scan to verify fixes

            **Workflow Run**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
            `;

            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `[Security] ${vulnCount} vulnerabilities detected in scheduled scan`,
              body: issueBody,
              labels: ['security', 'dependencies']
            });

      # SECURITY: Container scanning (if applicable)
      - name: Scan container images
        run: |
          # SECURITY: Scan production images from registry
          # Replace with your registry and image names
          for image in myapp:latest myapp:stable; do
            echo "Scanning $image..."
            podman pull ghcr.io/${{ github.repository }}/$image || true
            trivy image \
              --severity CRITICAL,HIGH \
              --format sarif \
              --output trivy-${image//[:\/]/-}.sarif \
              ghcr.io/${{ github.repository }}/$image || true
          done

      - name: Upload scheduled scan results
        if: always()
        uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392  # v4.0.0
        with:
          name: scheduled-scan-results
          path: |
            npm-audit.json
            trivy-*.sarif
          retention-days: 90

Comments