Security Scanning #116
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 Scanning | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| paths: | |
| - '**/*.py' | |
| - '**/*.js' | |
| - '**/*.ts' | |
| - '**/*.jsx' | |
| - '**/*.tsx' | |
| - '**/*.sh' | |
| - '**/*.yml' | |
| - '**/*.yaml' | |
| - '**/requirements*.txt' | |
| - '**/package*.json' | |
| - '**/Dockerfile*' | |
| - '**/.env*' | |
| - '**/nginx.conf' | |
| - '**/*.toml' | |
| - '**/*.conf' | |
| - '**/*.config' | |
| - '.github/workflows/security-scan.yml' | |
| pull_request: | |
| branches: [ main ] | |
| paths: | |
| - '**/*.py' | |
| - '**/*.js' | |
| - '**/*.ts' | |
| - '**/*.jsx' | |
| - '**/*.tsx' | |
| - '**/*.sh' | |
| - '**/*.yml' | |
| - '**/*.yaml' | |
| - '**/requirements*.txt' | |
| - '**/package*.json' | |
| - '**/Dockerfile*' | |
| - '**/.env*' | |
| - '**/nginx.conf' | |
| - '**/*.toml' | |
| - '**/*.conf' | |
| - '**/*.config' | |
| - '.github/workflows/security-scan.yml' | |
| schedule: | |
| # Daily quick scan at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| # Weekly comprehensive scan on Sundays at 3 AM UTC | |
| - cron: '0 3 * * 0' | |
| workflow_dispatch: | |
| inputs: | |
| scan_type: | |
| description: 'Type of security scan' | |
| required: false | |
| default: 'standard' | |
| type: choice | |
| options: | |
| - standard | |
| - comprehensive | |
| - quick | |
| permissions: | |
| contents: read | |
| security-events: write | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| # Monitor queue time before scans | |
| queue-monitor: | |
| name: Queue Time Monitor | |
| uses: ./.github/workflows/queue-monitor-reusable.yml | |
| security-headers-check: | |
| name: Security Headers Validation | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor] | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Validate nginx security headers | |
| run: | | |
| echo "🔍 Checking nginx security headers..." | |
| # Check for required security headers in nginx.conf | |
| REQUIRED_HEADERS=( | |
| "Content-Security-Policy" | |
| "X-Frame-Options" | |
| "X-Content-Type-Options" | |
| "Strict-Transport-Security" | |
| "Referrer-Policy" | |
| "Permissions-Policy" | |
| ) | |
| MISSING_HEADERS=() | |
| for header in "${REQUIRED_HEADERS[@]}"; do | |
| if ! grep -q "$header" fly/web/nginx.conf; then | |
| MISSING_HEADERS+=("$header") | |
| fi | |
| done | |
| if [ ${#MISSING_HEADERS[@]} -eq 0 ]; then | |
| echo "✅ All required security headers are present" | |
| else | |
| echo "❌ Missing security headers:" | |
| printf '%s\n' "${MISSING_HEADERS[@]}" | |
| exit 1 | |
| fi | |
| - name: Check for wildcard CORS | |
| run: | | |
| echo "🔍 Checking for wildcard CORS configurations..." | |
| if grep -r "Access-Control-Allow-Origin.*\*" fly/; then | |
| echo "❌ Found wildcard CORS configuration - this is a security risk!" | |
| exit 1 | |
| else | |
| echo "✅ No wildcard CORS found" | |
| fi | |
| dependency-security-scan: | |
| name: Dependency Security Scan | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor] | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Python Safety Check | |
| run: | | |
| echo "🔍 Checking Python dependencies for vulnerabilities..." | |
| pip install safety | |
| cd fly/eagle-monitor | |
| safety check -r requirements.txt || true | |
| - name: Cache npm dependencies | |
| uses: actions/cache@v3 | |
| if: hashFiles('package-lock.json') != '' | |
| with: | |
| path: ~/.npm | |
| key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-node- | |
| - name: Run npm audit | |
| run: | | |
| echo "🔍 Checking npm dependencies for vulnerabilities..." | |
| if [ -f "package.json" ]; then | |
| npm audit --production || true | |
| fi | |
| code-security-scan: | |
| name: Code Security Analysis | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor] | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Semgrep Security Scan | |
| uses: returntocorp/semgrep-action@v1 | |
| with: | |
| config: >- | |
| p/security-audit | |
| p/owasp-top-ten | |
| p/flask | |
| p/python | |
| p/javascript | |
| p/typescript | |
| generateSarif: true | |
| continue-on-error: true | |
| - name: Upload SARIF file | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: semgrep.sarif | |
| if: always() | |
| continue-on-error: true | |
| api-security-check: | |
| name: API Security Validation | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor] | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for API authentication | |
| run: | | |
| echo "🔍 Checking API authentication implementation..." | |
| # Check for API key authentication in eagle-monitor | |
| if grep -q "require_api_key" fly/eagle-monitor/app.py; then | |
| echo "✅ API authentication decorator found" | |
| else | |
| echo "⚠️ Warning: API authentication may not be properly implemented" | |
| fi | |
| # Check for rate limiting | |
| if grep -q "rate_limit" fly/eagle-monitor/app.py; then | |
| echo "✅ Rate limiting implementation found" | |
| else | |
| echo "⚠️ Warning: Rate limiting may not be implemented" | |
| fi | |
| secrets-scan: | |
| name: Secrets Detection | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor] | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Gitleaks | |
| uses: gitleaks/gitleaks-action@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| continue-on-error: true | |
| docker-security-scan: | |
| name: Docker Security Scan | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor] | |
| timeout-minutes: 20 | |
| strategy: | |
| matrix: | |
| service: [web, eagle-monitor, grafana, influxdb] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'config' | |
| scan-ref: 'fly/${{ matrix.service }}/Dockerfile' | |
| format: 'sarif' | |
| output: 'trivy-${{ matrix.service }}-results.sarif' | |
| continue-on-error: true | |
| - name: Upload Trivy scan results | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: 'trivy-${{ matrix.service }}-results.sarif' | |
| if: always() | |
| configuration-security-scan: | |
| name: Configuration Security Scan | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor] | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for exposed secrets in configs | |
| run: | | |
| echo "🔍 Scanning configuration files for potential secrets..." | |
| # Define patterns to search for | |
| PATTERNS=( | |
| "password.*=.*['\"].*['\"]" | |
| "api_key.*=.*['\"].*['\"]" | |
| "secret.*=.*['\"].*['\"]" | |
| "token.*=.*['\"].*['\"]" | |
| "private_key.*=.*['\"].*['\"]" | |
| "aws_access_key.*=.*['\"].*['\"]" | |
| ) | |
| # Define files to scan | |
| CONFIG_FILES=$(find . -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.toml" -o -name "*.json" -o -name "*.conf" -o -name "*.config" -o -name ".env*" \) -not -path "./.git/*" -not -path "./node_modules/*") | |
| FOUND_ISSUES=false | |
| # Scan configuration files | |
| for pattern in "${PATTERNS[@]}"; do | |
| echo "Checking pattern: $pattern" | |
| if echo "$CONFIG_FILES" | xargs grep -l -E -i "$pattern" 2>/dev/null; then | |
| echo "::warning::Potential secret pattern found in configuration files" | |
| FOUND_ISSUES=true | |
| fi | |
| done | |
| if [ "$FOUND_ISSUES" = false ]; then | |
| echo "✅ No potential secrets found in configuration files" | |
| fi | |
| - name: Check shell script security | |
| if: github.event.inputs.scan_type != 'quick' | |
| run: | | |
| echo "🔍 Scanning shell scripts for security issues..." | |
| # Find all shell scripts | |
| SHELL_SCRIPTS=$(find . -type f -name "*.sh" -not -path "./.git/*" -not -path "./node_modules/*") | |
| if [ -z "$SHELL_SCRIPTS" ]; then | |
| echo "No shell scripts found to scan" | |
| exit 0 | |
| fi | |
| # Check for common security issues | |
| echo "Checking for hardcoded credentials..." | |
| echo "$SHELL_SCRIPTS" | xargs grep -l -E "password=|PASSWORD=|token=|TOKEN=" 2>/dev/null || echo "✅ No hardcoded credentials found" | |
| echo "Checking for unsafe curl usage..." | |
| echo "$SHELL_SCRIPTS" | xargs grep -l "curl.*-k\|curl.*--insecure" 2>/dev/null || echo "✅ No insecure curl usage found" | |
| echo "Checking for unsafe file permissions..." | |
| echo "$SHELL_SCRIPTS" | xargs grep -l "chmod 777\|chmod 666" 2>/dev/null || echo "✅ No unsafe file permissions found" | |
| - name: Validate YAML files | |
| run: | | |
| echo "🔍 Validating YAML configuration files..." | |
| # Install yamllint | |
| pip install yamllint | |
| # Find and validate YAML files | |
| find . -type f \( -name "*.yml" -o -name "*.yaml" \) -not -path "./.git/*" -not -path "./node_modules/*" | while read -r file; do | |
| if ! yamllint -d relaxed "$file" 2>/dev/null; then | |
| echo "::warning::YAML validation failed for $file" | |
| fi | |
| done || echo "✅ YAML validation complete" | |
| security-report: | |
| name: Security Report Summary | |
| runs-on: ubuntu-latest | |
| needs: [queue-monitor, security-headers-check, dependency-security-scan, code-security-scan, api-security-check, secrets-scan, docker-security-scan, configuration-security-scan] | |
| if: always() | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Generate Security Report | |
| run: | | |
| echo "## 🔒 Security Scan Report" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Scan Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Check job statuses | |
| if [ "${{ needs.security-headers-check.result }}" == "success" ]; then | |
| echo "✅ **Security Headers**: All required headers present" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ **Security Headers**: Missing or incorrect headers" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.api-security-check.result }}" == "success" ]; then | |
| echo "✅ **API Security**: Authentication and rate limiting implemented" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ **API Security**: Review authentication implementation" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.configuration-security-scan.result }}" == "success" ]; then | |
| echo "✅ **Configuration Security**: No exposed secrets in config files" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ **Configuration Security**: Review configuration files for issues" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Scan Type" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ github.event_name }}" == "schedule" ]; then | |
| if [ "$(date +%w)" == "0" ]; then | |
| echo "🔍 **Weekly Comprehensive Scan**" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "🔍 **Daily Quick Scan**" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| echo "🔍 **Manual Scan**: ${{ github.event.inputs.scan_type || 'standard' }}" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "🔍 **Triggered by**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Review any security findings in the detailed logs" >> $GITHUB_STEP_SUMMARY | |
| echo "2. Update dependencies with known vulnerabilities" >> $GITHUB_STEP_SUMMARY | |
| echo "3. Address any code security issues identified by Semgrep" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Report Generated**: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_STEP_SUMMARY | |
| notify-security-issues: | |
| name: Notify Security Issues | |
| runs-on: ubuntu-latest | |
| needs: [security-report] | |
| if: failure() | |
| steps: | |
| - name: Create Issue for Security Findings | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const title = '🔒 Security Scan Found Issues'; | |
| const body = ` | |
| ## Security Scan Results | |
| The automated security scan has identified potential security issues. | |
| **Scan Date**: ${new Date().toISOString()} | |
| **Workflow Run**: [#${context.runNumber}](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) | |
| ### Required Actions: | |
| 1. Review the security scan results in the workflow logs | |
| 2. Address any high-priority findings | |
| 3. Update this issue with remediation status | |
| ### Security Checklist: | |
| - [ ] Security headers properly configured | |
| - [ ] No wildcard CORS configurations | |
| - [ ] API authentication implemented | |
| - [ ] Rate limiting active | |
| - [ ] No secrets in code | |
| - [ ] Dependencies up to date | |
| cc @${context.actor} | |
| `; | |
| // Check if similar issue already exists | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'security' | |
| }); | |
| const existingIssue = issues.data.find(issue => | |
| issue.title.includes('Security Scan Found Issues') | |
| ); | |
| if (!existingIssue) { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: title, | |
| body: body, | |
| labels: ['security', 'automated'] | |
| }); | |
| } |