212 lines
7.8 KiB
YAML
212 lines
7.8 KiB
YAML
# =============================================================================
|
|
# Security Workflow — Secret Scanning & Vulnerability Detection
|
|
# =============================================================================
|
|
#
|
|
# DISABLED BY DEFAULT (ENABLE_SECURITY=false in .ci/config.env).
|
|
#
|
|
# When enabled, this workflow runs:
|
|
# 1. gitleaks — scans for hardcoded secrets in the repo
|
|
# 2. osv-scanner — checks dependencies for known vulnerabilities
|
|
# 3. trivy — scans Docker images for CVEs (if a built image exists)
|
|
#
|
|
# STRICT_SECURITY=true → any finding fails the workflow
|
|
# STRICT_SECURITY=false → findings are logged as warnings (default)
|
|
#
|
|
# This is "best effort" — tools that aren't available are skipped.
|
|
# See docs/SECURITY.md for full details.
|
|
# =============================================================================
|
|
|
|
name: Security
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
pull_request:
|
|
|
|
jobs:
|
|
security:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
# -----------------------------------------------------------------------
|
|
# Step 1: Checkout
|
|
# -----------------------------------------------------------------------
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Step 2: Load configuration
|
|
# -----------------------------------------------------------------------
|
|
- name: Load config
|
|
run: |
|
|
if [ -f .ci/config.env ]; then
|
|
set -a
|
|
source .ci/config.env
|
|
set +a
|
|
fi
|
|
|
|
echo "ENABLE_SECURITY=${ENABLE_SECURITY:-false}" >> "$GITHUB_ENV"
|
|
echo "STRICT_SECURITY=${STRICT_SECURITY:-false}" >> "$GITHUB_ENV"
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Step 3: Check if security scanning is enabled
|
|
# -----------------------------------------------------------------------
|
|
- name: Check if enabled
|
|
run: |
|
|
if [ "$ENABLE_SECURITY" != "true" ]; then
|
|
echo "Security scanning is disabled (ENABLE_SECURITY=$ENABLE_SECURITY)."
|
|
echo "To enable, set ENABLE_SECURITY=true in .ci/config.env"
|
|
echo "SKIP_SECURITY=true" >> "$GITHUB_ENV"
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Step 4: Gitleaks — Secret scanning
|
|
#
|
|
# Scans the git history for accidentally committed secrets
|
|
# (API keys, passwords, tokens, etc.)
|
|
# -----------------------------------------------------------------------
|
|
- name: Run gitleaks
|
|
if: env.SKIP_SECURITY != 'true'
|
|
run: |
|
|
FINDINGS=0
|
|
|
|
# Install gitleaks
|
|
echo "Installing gitleaks..."
|
|
GITLEAKS_VERSION="8.18.4"
|
|
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" | \
|
|
tar xz -C /usr/local/bin gitleaks || {
|
|
echo "WARNING: Failed to install gitleaks, skipping secret scan."
|
|
exit 0
|
|
}
|
|
|
|
echo ">>> gitleaks detect"
|
|
if ! gitleaks detect --source . --verbose; then
|
|
FINDINGS=1
|
|
echo "gitleaks found potential secrets!"
|
|
fi
|
|
|
|
if [ "$FINDINGS" -ne 0 ]; then
|
|
if [ "$STRICT_SECURITY" = "true" ]; then
|
|
echo "ERROR: Secret scan found issues (STRICT_SECURITY=true)"
|
|
exit 1
|
|
else
|
|
echo "WARNING: Secret scan found issues (STRICT_SECURITY=false, continuing)"
|
|
fi
|
|
else
|
|
echo "gitleaks: no secrets found."
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Step 5: OSV-Scanner — Dependency vulnerability scanning
|
|
#
|
|
# Checks lockfiles (requirements.txt, package-lock.json, etc.) against
|
|
# the OSV database for known vulnerabilities.
|
|
# -----------------------------------------------------------------------
|
|
- name: Run osv-scanner
|
|
if: env.SKIP_SECURITY != 'true'
|
|
run: |
|
|
FINDINGS=0
|
|
|
|
# Check if there's anything to scan
|
|
HAS_DEPS=false
|
|
for f in requirements.txt package-lock.json yarn.lock pnpm-lock.yaml go.sum Cargo.lock; do
|
|
if [ -f "$f" ]; then
|
|
HAS_DEPS=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$HAS_DEPS" = "false" ]; then
|
|
echo "SKIP: No dependency lockfiles found for osv-scanner."
|
|
exit 0
|
|
fi
|
|
|
|
# Install osv-scanner
|
|
echo "Installing osv-scanner..."
|
|
OSV_VERSION="1.8.3"
|
|
curl -sSfL "https://github.com/google/osv-scanner/releases/download/v${OSV_VERSION}/osv-scanner_linux_amd64" \
|
|
-o /usr/local/bin/osv-scanner && chmod +x /usr/local/bin/osv-scanner || {
|
|
echo "WARNING: Failed to install osv-scanner, skipping."
|
|
exit 0
|
|
}
|
|
|
|
echo ">>> osv-scanner --recursive ."
|
|
if ! osv-scanner --recursive .; then
|
|
FINDINGS=1
|
|
echo "osv-scanner found vulnerabilities!"
|
|
fi
|
|
|
|
if [ "$FINDINGS" -ne 0 ]; then
|
|
if [ "$STRICT_SECURITY" = "true" ]; then
|
|
echo "ERROR: Dependency scan found issues (STRICT_SECURITY=true)"
|
|
exit 1
|
|
else
|
|
echo "WARNING: Dependency scan found issues (STRICT_SECURITY=false, continuing)"
|
|
fi
|
|
else
|
|
echo "osv-scanner: no vulnerabilities found."
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Step 6: Trivy — Container image scanning
|
|
#
|
|
# Scans a Docker image for OS and library CVEs.
|
|
# Only runs if a Dockerfile exists (assumes image was built).
|
|
# -----------------------------------------------------------------------
|
|
- name: Run trivy
|
|
if: env.SKIP_SECURITY != 'true'
|
|
run: |
|
|
if [ ! -f Dockerfile ]; then
|
|
echo "SKIP: No Dockerfile found, skipping Trivy image scan."
|
|
exit 0
|
|
fi
|
|
|
|
FINDINGS=0
|
|
|
|
# Install trivy
|
|
echo "Installing trivy..."
|
|
curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | \
|
|
sh -s -- -b /usr/local/bin || {
|
|
echo "WARNING: Failed to install trivy, skipping."
|
|
exit 0
|
|
}
|
|
|
|
# Build the image first so Trivy can scan it
|
|
IMAGE_TAG="security-scan:local"
|
|
echo ">>> docker build -t ${IMAGE_TAG} ."
|
|
docker build -t "${IMAGE_TAG}" . || {
|
|
echo "WARNING: Docker build failed, skipping Trivy scan."
|
|
exit 0
|
|
}
|
|
|
|
echo ">>> trivy image ${IMAGE_TAG}"
|
|
if ! trivy image --exit-code 1 --severity HIGH,CRITICAL "${IMAGE_TAG}"; then
|
|
FINDINGS=1
|
|
echo "Trivy found vulnerabilities in the Docker image!"
|
|
fi
|
|
|
|
if [ "$FINDINGS" -ne 0 ]; then
|
|
if [ "$STRICT_SECURITY" = "true" ]; then
|
|
echo "ERROR: Image scan found issues (STRICT_SECURITY=true)"
|
|
exit 1
|
|
else
|
|
echo "WARNING: Image scan found issues (STRICT_SECURITY=false, continuing)"
|
|
fi
|
|
else
|
|
echo "trivy: no HIGH/CRITICAL vulnerabilities found."
|
|
fi
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Step 7: Summary
|
|
# -----------------------------------------------------------------------
|
|
- name: Security Summary
|
|
if: always()
|
|
run: |
|
|
echo "=============================="
|
|
echo " Security Workflow Complete"
|
|
echo " Enabled: ${ENABLE_SECURITY:-false}"
|
|
echo " Strict: ${STRICT_SECURITY:-false}"
|
|
echo "=============================="
|