GitHub Actions Security Scanning Configuration
This document provides complete workflow configurations for implementing security gates in GitHub Actions.
Overview
GitHub provides multiple native security features that integrate directly with Actions workflows:
| Feature | Type | License |
|---|---|---|
| CodeQL | SAST | Free (public), GHAS (private) |
| Dependabot | SCA | Free |
| Secret Scanning | Secrets | Free (public), GHAS (private) |
| Dependency Review | SCA | GHAS |
Complete Security Pipeline
This workflow implements a comprehensive security scanning pipeline:
name: Security Scanning Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
schedule:
# Run weekly on Monday at 9am UTC
- cron: '0 9 * * 1'
permissions:
contents: read
security-events: write
pull-requests: write
jobs:
# Job 1: Secret Detection
secrets-scan:
name: Secrets Detection
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for comprehensive scan
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_ENABLE_COMMENTS: true
# Job 2: SAST with CodeQL
codeql-analysis:
name: CodeQL Analysis
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ['javascript', 'python']
# Add languages: 'csharp', 'java', 'go', 'ruby', 'cpp'
steps:
- name: Checkout repository
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 }}"
# Job 3: Dependency Scanning
dependency-scan:
name: Dependency Vulnerability Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner (filesystem)
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-fs-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-fs-results.sarif'
# Job 4: Container Scanning
container-scan:
name: Container Image Scan
runs-on: ubuntu-latest
needs: [secrets-scan, codeql-analysis]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-image-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-image-results.sarif'
# Job 5: IaC Scanning
iac-scan:
name: Infrastructure as Code Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy IaC scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'config'
scan-ref: '.'
format: 'sarif'
output: 'trivy-iac-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Upload Trivy IaC results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-iac-results.sarif'
CodeQL Advanced Configuration
Custom Query Packs
Include additional security queries:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
queries: |
security-extended
security-and-quality
# Or use specific packs:
# packs: codeql/javascript-queries:AlertSuppression.ql
Configuration File
Create .github/codeql/codeql-config.yml:
name: "CodeQL Config"
# Specify query packs
queries:
- uses: security-extended
- uses: security-and-quality
# Paths to scan
paths:
- src
- lib
# Paths to exclude
paths-ignore:
- '**/test/**'
- '**/tests/**'
- '**/*.test.js'
- '**/node_modules/**'
- '**/vendor/**'
# Query filters
query-filters:
- exclude:
id: js/redundant-assignment
- exclude:
tags contain: maintainability
Reference in workflow:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript
config-file: .github/codeql/codeql-config.yml
Dependency Scanning Configuration
Dependabot Configuration
Create .github/dependabot.yml:
version: 2
updates:
# JavaScript/npm
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 10
labels:
- "dependencies"
- "security"
groups:
development-dependencies:
dependency-type: "development"
update-types:
- "minor"
- "patch"
# Python/pip
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "python"
# Docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "docker"
# GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
- "ci"
# Terraform
- package-ecosystem: "terraform"
directory: "/infrastructure"
schedule:
interval: "weekly"
# NuGet (.NET)
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"
npm Audit Integration
npm-audit:
name: npm Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run npm audit
run: npm audit --audit-level=high
continue-on-error: false
.NET Dependency Scanning
dotnet-scan:
name: .NET Dependency Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Run dotnet list package vulnerable
run: dotnet list package --vulnerable --include-transitive 2>&1 | tee vulnerabilities.txt
- name: Check for vulnerabilities
run: |
if grep -q "has the following vulnerable packages" vulnerabilities.txt; then
echo "::error::Vulnerable packages found"
exit 1
fi
Secret Scanning Configuration
Enable Secret Scanning
Configure in repository settings: 1. Settings > Code security and analysis 2. Enable Secret scanning 3. Enable Push protection
Gitleaks Configuration
Create .gitleaks.toml:
title = "Gitleaks Configuration"
[extend]
# Use default ruleset as base
useDefault = true
# Custom rules
[[rules]]
id = "custom-api-key"
description = "Custom API Key Pattern"
regex = '''(?i)my_api_key\s*[=:]\s*['"]?([a-zA-Z0-9]{32,})['"]?'''
tags = ["key", "api"]
[[rules]]
id = "internal-token"
description = "Internal Service Token"
regex = '''INT_TOKEN_[A-Za-z0-9]{16,}'''
tags = ["token", "internal"]
# Allowlist
[allowlist]
description = "Global Allowlist"
paths = [
'''(.*/)?\.gitleaks\.toml$''',
'''(.*/)?test/.*''',
'''(.*/)?tests/.*''',
'''(.*/)?__tests__/.*'''
]
regexes = [
'''EXAMPLE_API_KEY''',
'''test-api-key-12345'''
]
TruffleHog Integration
trufflehog-scan:
name: TruffleHog Secrets Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: TruffleHog OSS
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
extra_args: --only-verified
Container Scanning
Trivy Container Scan
container-security:
name: Container Security Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'table'
exit-code: '1'
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
- name: Run Trivy for SARIF
uses: aquasecurity/trivy-action@master
if: always()
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload to Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
Docker Scout Integration
docker-scout:
name: Docker Scout Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Docker Scout scan
uses: docker/scout-action@v1
with:
command: cves
image: myapp:${{ github.sha }}
sarif-file: scout-results.sarif
exit-code: true
- name: Upload Scout results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: scout-results.sarif
SBOM Generation
Generate and Attest SBOM
sbom:
name: Generate SBOM
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
attestations: write
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Generate SBOM with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'cyclonedx'
output: 'sbom.json'
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
- name: Attest SBOM
uses: actions/attest-sbom@v1
with:
subject-name: myapp
subject-digest: sha256:${{ github.sha }}
sbom-path: sbom.json
Branch Protection Rules
Configure branch protection to require security checks:
{
"required_status_checks": {
"strict": true,
"contexts": [
"Secrets Detection",
"CodeQL Analysis",
"Dependency Vulnerability Scan",
"Container Image Scan"
]
},
"required_pull_request_reviews": {
"dismiss_stale_reviews": true,
"require_code_owner_reviews": true,
"required_approving_review_count": 1
},
"enforce_admins": true,
"restrictions": null
}
Security Findings in Pull Requests
Dependency Review Action
dependency-review:
name: Dependency Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
deny-licenses: GPL-3.0, AGPL-3.0
allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause
PR Comment with Findings
- name: Comment on PR
if: github.event_name == 'pull_request' && failure()
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '⚠️ Security scan detected vulnerabilities. Please review the Security tab for details.'
})
Scheduled Scanning
Weekly Full Scan
name: Weekly Security Scan
on:
schedule:
- cron: '0 2 * * 1' # Monday at 2 AM UTC
workflow_dispatch: # Allow manual trigger
jobs:
full-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Run comprehensive scans
- name: Full Trivy scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'json'
output: 'full-scan-results.json'
severity: 'CRITICAL,HIGH,MEDIUM'
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: weekly-scan-results
path: full-scan-results.json
retention-days: 90
Reusable Workflow
Create .github/workflows/security-scan.yml:
name: Reusable Security Scan
on:
workflow_call:
inputs:
scan-type:
description: 'Type of scan to run'
required: true
type: string
severity:
description: 'Severity threshold'
required: false
type: string
default: 'CRITICAL,HIGH'
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy
uses: aquasecurity/trivy-action@master
with:
scan-type: ${{ inputs.scan-type }}
severity: ${{ inputs.severity }}
exit-code: '1'
Use in other workflows:
jobs:
call-security:
uses: ./.github/workflows/security-scan.yml
with:
scan-type: 'fs'
severity: 'CRITICAL'
Related Documentation
- README.md - Main tutorial
- azure-devops.md - Azure DevOps configuration
- tools.md - Security tools reference