name: Security & Dependency Updates on: schedule: # Run security scans daily at 3 AM UTC - cron: "0 3 * * *" workflow_dispatch: push: paths: - "requirements*.txt" - "Dockerfile" - ".gitea/workflows/security.yml" env: PYTHON_VERSION: "3.11" # GitHub token for better rate limits and authentication GH_TOKEN: ${{ secrets.GH_TOKEN }} jobs: # Dependency vulnerability scan dependency-scan: name: Dependency Security Scan runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.CI_BOT_TOKEN }} - name: Install jq (for parsing JSON) run: sudo apt-get update && sudo apt-get install -y jq - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install dependencies run: | python -m pip install --upgrade pip --root-user-action=ignore # Pin safety to a v2 line where `check --json` is stable pip install --root-user-action=ignore "safety>=2,<3" bandit semgrep - name: Run Safety check run: | if [ -f requirements.txt ]; then safety check -r requirements.txt --json --output safety-report.json || true else echo "[]" > safety-report.json fi if [ -f requirements-dev.txt ]; then safety check -r requirements-dev.txt --json --output safety-dev-report.json || true else echo "[]" > safety-dev-report.json fi - name: Run Bandit security scan run: | if [ -d src ]; then bandit -r src/ -f json -o bandit-report.json || true else echo '{"results":[]}' > bandit-report.json fi - name: Run Semgrep security scan run: | if [ -d src ]; then semgrep --config=auto src/ --json --output=semgrep-report.json || true else echo '{"results":[]}' > semgrep-report.json fi - name: Upload security reports uses: actions/upload-artifact@v3 with: name: security-reports-${{ github.run_number }} path: | safety-report.json safety-dev-report.json bandit-report.json semgrep-report.json - name: Check for critical vulnerabilities run: | echo "🔍 Checking for critical vulnerabilities..." # --- Safety results (robust to array/object formats) --- summarize_safety () { f="$1" if [ -f "$f" ]; then count=$(jq -r ' if type=="array" then length else ((.vulnerabilities // []) | length) end ' "$f" 2>/dev/null || echo "0") if [ "${count:-0}" -gt 0 ]; then echo "⚠️ Found $count dependency vulnerabilities in $f" jq -r ' if type=="array" then .[] | "- \(.package_name // .name) \(.installed_version // ""): \(.vulnerability_id // .advisory // "N/A")" else (.vulnerabilities // [])[] | "- \(.package_name) \(.installed_version): \(.vulnerability_id)" end ' "$f" || true else echo "✅ No dependency vulnerabilities found in $f" fi fi } summarize_safety safety-report.json summarize_safety safety-dev-report.json # --- Bandit results (count HIGH severity properly) --- if [ -f bandit-report.json ]; then high_severity=$(jq '[.results[]? | select(.issue_severity=="HIGH")] | length' bandit-report.json 2>/dev/null || echo "0") if [ "${high_severity:-0}" -gt 0 ]; then echo "⚠️ Found $high_severity high-severity security issues" else echo "✅ No high-severity security issues found" fi fi # License compliance check license-check: name: License Compliance runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.CI_BOT_TOKEN }} - name: Install jq (for parsing JSON) run: sudo apt-get update && sudo apt-get install -y jq - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install pip-licenses run: | python -m pip install --upgrade pip --root-user-action=ignore pip install --root-user-action=ignore pip-licenses if [ -f requirements.txt ]; then pip install --root-user-action=ignore -r requirements.txt fi - name: Check licenses run: | echo "📄 Checking dependency licenses..." pip-licenses --format=json --output-file=licenses.json pip-licenses --format=markdown --output-file=licenses.md # Check for potentially problematic licenses problematic_licenses=("GPL" "AGPL" "LGPL") for license in "${problematic_licenses[@]}"; do if grep -iq "$license" licenses.json; then echo "⚠️ Found potentially problematic license: $license" fi done echo "✅ License check completed" - name: Upload license report uses: actions/upload-artifact@v3 with: name: license-report-${{ github.run_number }} path: | licenses.json licenses.md # Dependency update check dependency-update: name: Check for Dependency Updates runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: token: ${{ secrets.CI_BOT_TOKEN }} - name: Install jq (for parsing JSON) run: sudo apt-get update && sudo apt-get install -y jq - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Check for outdated packages run: | echo "📦 Checking for outdated packages..." if [ -f requirements.txt ]; then python -m pip install --upgrade pip --root-user-action=ignore pip install --root-user-action=ignore -r requirements.txt fi pip list --outdated --format=json > outdated-packages.json || true if [ -s outdated-packages.json ] && [ "$(cat outdated-packages.json)" != "[]" ]; then echo "📋 Outdated packages found:" jq -r '.[] | "- \(.name): \(.version) -> \(.latest_version)"' outdated-packages.json else echo "✅ All packages are up to date" fi - name: Create dependency update issue if: github.event_name == 'schedule' run: | if [ -s outdated-packages.json ] && [ "$(cat outdated-packages.json)" != "[]" ]; then echo "📝 Creating dependency update issue..." cat > issue-body.md << 'EOF' ## 📦 Dependency Updates Available The following packages have updates available: EOF jq -r '.[] | "- **\(.name)**: \(.version) →