Security Scan #156
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Comprehensive Security Scanning Suite | |
# | |
# Purpose: Unified security scanning combining CodeQL, dependency scanning, | |
# and secret detection. | |
# | |
# Triggers: | |
# - Push to main/develop/staging branches | |
# - PRs to main/develop/staging branches | |
# - Daily scheduled scan at 2 AM UTC | |
# | |
# Scan Types: | |
# 1. Code security analysis (CodeQL) | |
# 2. Dependency vulnerabilities (Trivy) | |
# 3. Secret detection (TruffleHog) | |
# 4. Container security (when applicable) | |
# | |
# Results: | |
# - Uploaded to GitHub Security tab | |
# - Available in SARIF format | |
# - Fails build on critical issues | |
name: Security Scan | |
on: | |
push: | |
branches: [main, develop, staging] | |
paths-ignore: | |
- '**/*.md' | |
- 'docs/**' | |
- '.github/*.md' | |
- '.github/ISSUE_TEMPLATE/**' | |
- '.github/PULL_REQUEST_TEMPLATE.md' | |
- 'LICENSE' | |
- 'CHANGELOG.md' | |
- 'README.md' | |
- '**/*.txt' | |
pull_request: | |
branches: [main, develop, staging] | |
paths-ignore: | |
- '**/*.md' | |
- 'docs/**' | |
- '.github/*.md' | |
- '.github/ISSUE_TEMPLATE/**' | |
- '.github/PULL_REQUEST_TEMPLATE.md' | |
- 'LICENSE' | |
- 'CHANGELOG.md' | |
- 'README.md' | |
- '**/*.txt' | |
schedule: | |
# Daily scan ensures we catch newly disclosed vulnerabilities | |
- cron: '0 2 * * *' | |
# Cancel in-progress security scans when new commits are pushed | |
concurrency: | |
group: security-${{ github.ref }} | |
cancel-in-progress: true | |
permissions: | |
contents: read # Read repository content | |
security-events: write # Upload security findings | |
jobs: | |
codeql-analysis: | |
name: CodeQL Security Analysis | |
runs-on: ubuntu-latest | |
permissions: | |
security-events: write | |
packages: read | |
actions: read | |
contents: read | |
strategy: | |
fail-fast: false | |
matrix: | |
language: [ 'javascript-typescript' ] | |
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 }}" | |
dependency-scan: | |
name: Dependency Vulnerability Scan | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
# Scan for known vulnerabilities in dependencies | |
- name: Run Trivy vulnerability scanner | |
uses: aquasecurity/trivy-action@0.24.0 | |
with: | |
scan-type: 'fs' # Filesystem scan | |
scan-ref: '.' # Scan entire repository | |
format: 'sarif' # GitHub-compatible format | |
output: 'trivy-results.sarif' | |
severity: 'CRITICAL,HIGH,MEDIUM' # Report these severity levels | |
ignore-unfixed: true # Skip vulnerabilities without fixes | |
# Make results visible in GitHub Security tab | |
- name: Upload Trivy scan results to GitHub Security | |
uses: github/codeql-action/upload-sarif@v3 | |
with: | |
sarif_file: 'trivy-results.sarif' | |
secret-scan: | |
name: Secret Scanning | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 # Full history needed for accurate secret detection | |
# Scan for exposed secrets and credentials | |
- name: TruffleHog Secret Scan | |
uses: trufflesecurity/trufflehog@v3.63.7 | |
with: | |
path: ./ # Scan entire repository | |
base: ${{ github.event.repository.default_branch }} # Compare against default branch | |
head: HEAD # Current commit | |
extra_args: --only-verified # Only report verified secrets | |
docker-scan: | |
name: Container Security Scan | |
runs-on: ubuntu-latest | |
# Only run when Docker-related changes are made | |
if: contains(github.event.head_commit.message, 'docker') || contains(github.event.pull_request.title, 'docker') | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
# Build Docker image if Dockerfile exists | |
- name: Build Docker image | |
run: | | |
if [ -f Dockerfile ]; then | |
docker build -t zopio:scan . | |
else | |
echo "No Dockerfile found, skipping container scan" | |
exit 0 | |
fi | |
# Scan built container for vulnerabilities | |
- name: Run Trivy container scan | |
if: success() | |
uses: aquasecurity/trivy-action@0.24.0 | |
with: | |
image-ref: 'zopio:scan' # Local image to scan | |
format: 'sarif' | |
output: 'container-results.sarif' | |
severity: 'CRITICAL,HIGH' # Only critical and high severity | |
# Upload container scan results | |
- name: Upload container scan results | |
if: success() | |
uses: github/codeql-action/upload-sarif@v3 | |
with: | |
sarif_file: 'container-results.sarif' | |
# Summary job that creates the required "Security Scan" status check | |
security-scan-summary: | |
name: Security Scan | |
runs-on: ubuntu-latest | |
needs: [dependency-scan, secret-scan, docker-scan] | |
if: always() | |
steps: | |
- name: Check Security Scan Status | |
run: | | |
# Check if any required job failed (docker-scan is optional) | |
if [[ "${{ needs.dependency-scan.result }}" == "failure" || \ | |
"${{ needs.secret-scan.result }}" == "failure" ]]; then | |
echo "Security Scan failed" | |
exit 1 | |
else | |
echo "Security Scan passed" | |
fi |