Security & Secrets Scanning #206
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
| name: Security & Secrets Scanning | |
| on: | |
| push: | |
| branches: [ main, develop, feature/*, claude/* ] | |
| pull_request: | |
| branches: [ main, develop ] | |
| schedule: | |
| # Run security scans daily at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| workflow_dispatch: | |
| jobs: | |
| # ==================== SECRETS DETECTION ==================== | |
| gitleaks: | |
| name: Gitleaks Secret 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_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} | |
| - name: Upload Gitleaks report | |
| uses: actions/upload-artifact@v4 | |
| if: failure() | |
| with: | |
| name: gitleaks-report | |
| path: gitleaks-report.json | |
| retention-days: 30 | |
| trufflehog: | |
| name: TruffleHog Secret Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Run TruffleHog (filesystem scan) | |
| uses: trufflesecurity/trufflehog@main | |
| with: | |
| path: ./ | |
| # Only scan verified secrets to reduce false positives | |
| extra_args: --only-verified | |
| custom-patterns: | |
| name: Custom Pattern Detection | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Scan for API keys and tokens | |
| run: | | |
| echo "Scanning for potential secrets..." | |
| # Create patterns file | |
| cat > patterns.txt << 'EOL' | |
| GEMINI_API_KEY\s*=\s*["']?[A-Za-z0-9_-]{30,}["']? | |
| GOOGLE_API_KEY\s*=\s*["']?[A-Za-z0-9_-]{30,}["']? | |
| api_key\s*=\s*["'][A-Za-z0-9_-]{20,}["'] | |
| password\s*=\s*["'][^"']{8,}["'] | |
| secret\s*=\s*["'][A-Za-z0-9_-]{20,}["'] | |
| token\s*=\s*["'][A-Za-z0-9._-]{20,}["'] | |
| EOL | |
| # Scan files (exclude test fixtures) | |
| FOUND=0 | |
| while IFS= read -r pattern; do | |
| echo "Checking pattern: $pattern" | |
| if grep -rE "$pattern" . \ | |
| --exclude-dir=.git \ | |
| --exclude-dir=.github \ | |
| --exclude-dir=node_modules \ | |
| --exclude-dir=venv \ | |
| --exclude-dir=__pycache__ \ | |
| --exclude="*.pyc" \ | |
| --exclude="patterns.txt" \ | |
| --exclude=".env.example" \ | |
| | grep -v "^\s*#"; then | |
| echo "WARNING: Potential secret found matching pattern: $pattern" | |
| FOUND=1 | |
| fi | |
| done < patterns.txt | |
| if [ $FOUND -eq 1 ]; then | |
| echo "ERROR: Potential secrets detected in codebase!" | |
| exit 1 | |
| else | |
| echo "No secrets detected - scan passed!" | |
| fi | |
| env-validation: | |
| name: Environment File Validation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Validate .env files | |
| run: | | |
| echo "Validating environment files..." | |
| # Check that .env.example has no real secrets | |
| if [ -f .env.example ]; then | |
| echo "Checking .env.example..." | |
| # These patterns indicate placeholder values (good) | |
| if grep -q "your-api-key-here" .env.example && \ | |
| ! grep -qE "[A-Za-z0-9_-]{32,}" .env.example | grep -v "your-api-key-here"; then | |
| echo "✅ .env.example contains only placeholders" | |
| else | |
| echo "❌ .env.example may contain real API keys!" | |
| cat .env.example | |
| exit 1 | |
| fi | |
| fi | |
| # Ensure .env is gitignored | |
| if [ -f .gitignore ]; then | |
| if grep -q "^\.env$" .gitignore || grep -q "^\.env$" .gitignore; then | |
| echo "✅ .env is properly gitignored" | |
| else | |
| echo "❌ .env is not in .gitignore!" | |
| exit 1 | |
| fi | |
| else | |
| echo "WARNING: No .gitignore file found" | |
| fi | |
| # Check that .env does not exist in repo | |
| if [ -f .env ]; then | |
| echo "❌ ERROR: .env file should not be committed!" | |
| exit 1 | |
| else | |
| echo "✅ No .env file in repository" | |
| fi | |
| # ==================== CODE & DEPENDENCY SECURITY ==================== | |
| bandit: | |
| name: Bandit SAST | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Bandit | |
| run: | | |
| pip install bandit[toml]>=1.7.0 | |
| - name: Run Bandit security scan | |
| run: | | |
| bandit -r flamehaven_filesearch/ \ | |
| -f json \ | |
| -o bandit-report.json \ | |
| --exit-zero | |
| - name: Check Bandit results | |
| run: | | |
| # Fail if HIGH severity issues found | |
| HIGH_COUNT=$(python -c "import json; data=json.load(open('bandit-report.json')); print(sum(1 for r in data['results'] if r['issue_severity'] == 'HIGH'))") | |
| echo "High severity issues found: $HIGH_COUNT" | |
| if [ "$HIGH_COUNT" -gt "0" ]; then | |
| echo "ERROR: High severity security issues detected!" | |
| cat bandit-report.json | |
| exit 1 | |
| fi | |
| - name: Upload Bandit report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: bandit-report | |
| path: bandit-report.json | |
| retention-days: 30 | |
| safety: | |
| name: Dependency Vulnerability Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: | | |
| pip install safety>=3.0.0 | |
| pip install -r requirements.txt | |
| - name: Run Safety scan | |
| run: | | |
| # Use safety scan (new command) instead of deprecated safety check | |
| safety scan --output json > safety-report.json || true | |
| - name: Check Safety results | |
| run: | | |
| # Fail if critical or high severity vulnerabilities found | |
| if grep -q '"severity": "critical"' safety-report.json || grep -q '"severity": "high"' safety-report.json; then | |
| echo "ERROR: Critical or high severity vulnerabilities detected!" | |
| cat safety-report.json | |
| exit 1 | |
| fi | |
| - name: Upload Safety report | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: safety-report | |
| path: safety-report.json | |
| retention-days: 30 | |
| trivy: | |
| name: Trivy Container Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Build Docker image | |
| id: build | |
| continue-on-error: true | |
| run: | | |
| docker build -t flamehaven-filesearch:test . | |
| - name: Run Trivy vulnerability scanner | |
| if: steps.build.outcome == 'success' | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: 'flamehaven-filesearch:test' | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| exit-code: '0' # Don't fail on vulnerabilities, just report | |
| - name: Upload Trivy results to GitHub Security | |
| if: steps.build.outcome == 'success' | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| - name: Skip notice | |
| if: steps.build.outcome != 'success' | |
| run: | | |
| echo "::notice::Docker build failed or skipped - Trivy scan not performed" | |
| codeql: | |
| name: CodeQL Analysis | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| actions: read | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| language: [ 'python' ] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Initialize CodeQL | |
| uses: github/codeql-action/init@v3 | |
| with: | |
| languages: ${{ matrix.language }} | |
| queries: security-extended | |
| - name: Autobuild | |
| uses: github/codeql-action/autobuild@v3 | |
| - name: Perform CodeQL Analysis | |
| uses: github/codeql-action/analyze@v3 | |
| with: | |
| category: "/language:${{ matrix.language }}" | |
| security-summary: | |
| name: Security & Secrets Summary | |
| runs-on: ubuntu-latest | |
| needs: | |
| - gitleaks | |
| - trufflehog | |
| - custom-patterns | |
| - env-validation | |
| - bandit | |
| - safety | |
| - trivy | |
| - codeql | |
| if: always() | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| - name: Generate security summary | |
| run: | | |
| echo "# Security & Secrets Scan Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Secrets Detection" >> $GITHUB_STEP_SUMMARY | |
| echo "- Gitleaks: ${{ needs.gitleaks.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- TruffleHog: ${{ needs.trufflehog.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Custom Patterns: ${{ needs.custom-patterns.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Environment Validation: ${{ needs.env-validation.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Code & Dependency Security" >> $GITHUB_STEP_SUMMARY | |
| echo "- Bandit SAST: ${{ needs.bandit.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Safety Dependencies: ${{ needs.safety.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Trivy Container: ${{ needs.trivy.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- CodeQL Analysis: ${{ needs.codeql.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Check all results | |
| if [ "${{ needs.gitleaks.result }}" == "success" ] && \ | |
| [ "${{ needs.trufflehog.result }}" == "success" ] && \ | |
| [ "${{ needs.custom-patterns.result }}" == "success" ] && \ | |
| [ "${{ needs.env-validation.result }}" == "success" ] && \ | |
| [ "${{ needs.bandit.result }}" == "success" ] && \ | |
| [ "${{ needs.safety.result }}" == "success" ] && \ | |
| [ "${{ needs.trivy.result }}" == "success" ] && \ | |
| [ "${{ needs.codeql.result }}" == "success" ]; then | |
| echo "✅ **All security & secrets scans passed!**" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Security scans failed - review artifacts**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Remediation Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Review failed scan artifacts above" >> $GITHUB_STEP_SUMMARY | |
| echo "2. If secrets detected: Rotate credentials immediately" >> $GITHUB_STEP_SUMMARY | |
| echo "3. If vulnerabilities found: Update dependencies or apply patches" >> $GITHUB_STEP_SUMMARY | |
| echo "4. Rerun scans after fixes" >> $GITHUB_STEP_SUMMARY | |
| fi |