Skip to content

Security & Secrets Scanning #206

Security & Secrets Scanning

Security & Secrets Scanning #206

Workflow file for this run

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