name: Security Check # ======================================== # Automated Security Scanning # ======================================== # This workflow runs on every PR and push to main # Optimized to not fail on optional security tools on: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] workflow_dispatch: jobs: # ======================================== # CRITICAL: FILE VALIDATION # ======================================== file-validation: name: Critical File Validation runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Check for .env files run: | echo "==> Checking for .env files..." if git ls-files | grep -E "^\.env$"; then echo "ERROR: .env file found in repository!" echo "This file contains sensitive data and must not be committed" exit 1 fi echo "SUCCESS: No .env files in repository" - name: Check for sensitive files run: | echo "==> Checking for sensitive files..." # Files that should never be committed sensitive_files=( "*.key" "*.pem" "*.cert" "*.p12" "*.pfx" ) found_sensitive=false for pattern in "${sensitive_files[@]}"; do # Exclude node_modules and .github files=$(git ls-files | grep -i "$pattern" | grep -v "node_modules" | grep -v ".github" || true) if [ -n "$files" ]; then echo "WARNING: Sensitive file pattern found: $pattern" echo "$files" found_sensitive=true fi done if [ "$found_sensitive" = true ]; then echo "ERROR: Sensitive files detected. Please remove them." exit 1 fi echo "SUCCESS: No sensitive files found" - name: Verify .gitignore run: | echo "==> Verifying .gitignore configuration..." if ! grep -q "^\.env$" .gitignore; then echo "ERROR: .env not found in .gitignore!" exit 1 fi if ! grep -q "^\.env\.\*$" .gitignore; then echo "WARNING: .env.* pattern not in .gitignore" fi echo "SUCCESS: .gitignore properly configured" # ======================================== # CRITICAL: ENVIRONMENT VALIDATION # ======================================== env-validation: name: Environment Configuration runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Verify .env.example exists run: | echo "==> Checking for .env.example..." if [ ! -f .env.example ]; then echo "ERROR: .env.example not found!" echo "Please create .env.example with safe placeholder values" exit 1 fi echo "SUCCESS: .env.example exists" - name: Check .env.example for real secrets run: | echo "==> Validating .env.example content..." # .env.example should NOT contain real long secrets if grep -E "(password|key|secret|token)=.{30,}" .env.example | grep -v "your_"; then echo "WARNING: .env.example may contain real credentials!" echo "Example files should only have placeholder values" exit 1 fi echo "SUCCESS: .env.example contains no real secrets" - name: Validate environment variable usage run: | echo "==> Checking environment variable usage..." if [ -f "src/contexts/AuthContext.tsx" ]; then if grep -q "import.meta.env" src/contexts/AuthContext.tsx; then echo "SUCCESS: AuthContext uses environment variables" else echo "WARNING: AuthContext may not use environment variables" fi fi # ======================================== # CODE SECURITY ANALYSIS # ======================================== code-security: name: Code Security Analysis runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Check for hardcoded secrets run: | echo "==> Scanning for hardcoded secrets in code..." has_issues=false # Check for hardcoded passwords (8+ chars) if grep -r "password\s*=\s*['\"][^'\"]\{8,\}['\"]" src/ --include="*.ts" --include="*.tsx" | grep -v "import.meta.env" | grep -v "placeholder" | grep -v "example"; then echo "WARNING: Potential hardcoded password found" has_issues=true fi # Check for hardcoded API keys (20+ chars) if grep -r "api[_-]\?key\s*=\s*['\"][^'\"]\{20,\}['\"]" src/ --include="*.ts" --include="*.tsx" | grep -v "import.meta.env" | grep -v "your_"; then echo "WARNING: Potential hardcoded API key found" has_issues=true fi if [ "$has_issues" = false ]; then echo "SUCCESS: No hardcoded secrets detected" else echo "Please use environment variables for sensitive data" fi - name: Check for console.log statements continue-on-error: true run: | echo "==> Checking for console.log statements..." if grep -r "console\.log" src/ --include="*.ts" --include="*.tsx" | head -10; then echo "INFO: console.log statements found (consider removing for production)" fi # ======================================== # DEPENDENCY SECURITY # ======================================== dependency-security: name: Dependency Security Audit runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Run npm audit continue-on-error: true run: | echo "==> Running npm audit..." npm audit --audit-level=high || echo "WARNING: Vulnerabilities found, please review" - name: Check for outdated critical packages continue-on-error: true run: | echo "==> Checking for outdated packages..." npm outdated || true # ======================================== # OPTIONAL: ADVANCED SECRET SCANNING # ======================================== advanced-secret-scan: name: Advanced Secret Scanning (Optional) runs-on: ubuntu-latest continue-on-error: true steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: TruffleHog Secret Scan continue-on-error: true uses: trufflesecurity/trufflehog@main with: path: ./ base: ${{ github.event.repository.default_branch }} head: HEAD - name: Gitleaks Secret Scan if: ${{ secrets.GITLEAKS_LICENSE != '' }} continue-on-error: true uses: gitleaks/gitleaks-action@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} - name: Basic Pattern Check run: | echo "==> Running basic secret pattern check..." if git diff-tree --no-commit-id --name-only -r HEAD 2>/dev/null | xargs grep -E "(password|secret|api[_-]?key|token)\s*=\s*['\"][A-Za-z0-9]{20,}['\"]" 2>/dev/null; then echo "INFO: Potential secrets detected, please review" else echo "SUCCESS: No obvious secrets in recent changes" fi # ======================================== # OPTIONAL: SNYK VULNERABILITY SCAN # ======================================== snyk-scan: name: Snyk Vulnerability Scan (Optional) runs-on: ubuntu-latest if: ${{ secrets.SNYK_TOKEN != '' }} continue-on-error: true steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Run Snyk uses: snyk/actions/node@master continue-on-error: true env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --severity-threshold=high # ======================================== # SUMMARY # ======================================== security-summary: name: Security Summary needs: [file-validation, env-validation, code-security, dependency-security] runs-on: ubuntu-latest if: always() steps: - name: Print Summary run: | echo "==========================================" echo "Security Check Summary" echo "==========================================" echo "" echo "Critical Checks:" echo " File Validation: ${{ needs.file-validation.result }}" echo " Environment Config: ${{ needs.env-validation.result }}" echo "" echo "Code Quality:" echo " Code Security: ${{ needs.code-security.result }}" echo " Dependency Security: ${{ needs.dependency-security.result }}" echo "" # Fail if critical checks failed if [ "${{ needs.file-validation.result }}" != "success" ] || \ [ "${{ needs.env-validation.result }}" != "success" ]; then echo "==========================================" echo "CRITICAL SECURITY ISSUES DETECTED!" echo "==========================================" echo "" echo "Please fix the issues above before merging" exit 1 fi echo "==========================================" echo "All critical security checks passed!" echo "=========================================="