Security Scan #13
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 Scan | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| # Run weekly on Monday at 09:00 UTC | |
| - cron: '0 9 * * 1' | |
| workflow_dispatch: | |
| jobs: | |
| security-scan: | |
| runs-on: ubuntu-latest | |
| if: github.repository == 'basher83/Zammad-MCP' | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | |
| with: | |
| version: "latest" | |
| - name: Set up Python | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.14' | |
| - name: Install dependencies | |
| run: | | |
| uv sync --dev --frozen | |
| - name: Run Bandit (HIGH/CRITICAL only) | |
| run: | | |
| # Full recursive scan of the entire codebase | |
| # --severity-level high: Only fail on HIGH and CRITICAL severity issues | |
| echo "Running Bandit security scan (HIGH/CRITICAL only)..." | |
| uv run bandit -r mcp_zammad/ --severity-level high -f json -o bandit-report.json | |
| - name: Generate Bandit SARIF report | |
| continue-on-error: true | |
| run: | | |
| # Generate SARIF for GitHub Security tab (includes all severities for visibility) | |
| echo "Generating Bandit SARIF report for Security tab..." | |
| uv run bandit -r mcp_zammad/ -f sarif -o bandit-sarif.json | |
| - name: Upload Bandit results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4 | |
| if: always() | |
| with: | |
| sarif_file: bandit-sarif.json | |
| category: bandit | |
| - name: Run safety scan | |
| uses: pyupio/safety-action@2591cf2f3e67ba68b923f4c92f0d36e281c65023 # v1.0.1 tag target commit | |
| continue-on-error: true | |
| with: | |
| api-key: ${{ github.repository == 'basher83/Zammad-MCP' && secrets.SAFETY_API_KEY || '' }} | |
| - name: Run pip-audit | |
| continue-on-error: true | |
| run: | | |
| echo "Running pip-audit dependency scan..." | |
| # Will exit non-zero if vulnerabilities are found | |
| uv run pip-audit --format json --output pip-audit-report.json || { | |
| EXIT_CODE=$? | |
| echo "⚠️ pip-audit found vulnerabilities (exit code: $EXIT_CODE)" | |
| # Generate a summary of vulnerabilities | |
| if [ -f pip-audit-report.json ]; then | |
| VULN_COUNT=$(jq '.vulnerabilities | length' pip-audit-report.json) | |
| echo "Found $VULN_COUNT vulnerability(ies) in dependencies" | |
| echo "" | |
| jq -r '.vulnerabilities[] | "Package: \(.name) \(.version)\nVulnerability: \(.id)\nDescription: \(.description)\nFix: \(if .fix_versions | length > 0 then "Upgrade to " + (.fix_versions | join(", ")) else "Check advisory" end)\n"' pip-audit-report.json | |
| fi | |
| exit $EXIT_CODE | |
| } | |
| echo "✅ No dependency vulnerabilities found" | |
| - name: Upload security reports | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| if: always() | |
| with: | |
| name: security-reports | |
| path: | | |
| bandit-report.json | |
| bandit-sarif.json | |
| pip-audit-report.json | |
| - name: Generate Security Summary | |
| if: always() | |
| run: | | |
| echo "## 🔒 Security Scan Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Check Bandit results | |
| if [ -f bandit-report.json ]; then | |
| HIGH_COUNT=$(jq '[.results[] | select(.issue_severity == "HIGH")] | length' bandit-report.json) | |
| MED_COUNT=$(jq '[.results[] | select(.issue_severity == "MEDIUM")] | length' bandit-report.json) | |
| LOW_COUNT=$(jq '[.results[] | select(.issue_severity == "LOW")] | length' bandit-report.json) | |
| echo "### Bandit Static Analysis" >> $GITHUB_STEP_SUMMARY | |
| if [ "$HIGH_COUNT" -gt 0 ]; then | |
| echo "❌ **Failed**: Found $HIGH_COUNT HIGH severity issues" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "✅ **Passed**: No HIGH/CRITICAL issues (Medium: $MED_COUNT, Low: $LOW_COUNT)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Check safety results | |
| echo "### Dependency Vulnerability Scans" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Safety**: Check performed via GitHub Action (see job output for details)" >> $GITHUB_STEP_SUMMARY | |
| # Check pip-audit results | |
| if [ -f pip-audit-report.json ]; then | |
| AUDIT_VULNS=$(jq '.vulnerabilities | length' pip-audit-report.json) | |
| if [ "$AUDIT_VULNS" -gt 0 ]; then | |
| echo "❌ **pip-audit**: Found $AUDIT_VULNS vulnerabilities" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "✅ **pip-audit**: No vulnerabilities" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| fi |