# ============================================================================= # 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 "=============================="