dev v2.0.3 #172
Workflow file for this run
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: | |
| pull_request: | |
| branches: [ '*' ] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| security-scan: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: write | |
| actions: read | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: 1. Checkout repository code | |
| uses: actions/checkout@v6 | |
| - name: 2. Set up Python 3.14 with caching | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| cache: 'pip' | |
| - name: 3. Install security tools | |
| run: pip install bandit[sarif] | |
| - name: 4. Run Bandit Scan | |
| working-directory: ./backend | |
| run: | | |
| echo "Running Bandit security scan..." | |
| if bandit -r . -f json -o bandit-results.json --configfile pyproject.toml; then | |
| echo "BANDIT_PASSED=true" >> $GITHUB_ENV | |
| echo "✅ No security issues found by Bandit" | |
| else | |
| echo "BANDIT_PASSED=false" >> $GITHUB_ENV | |
| echo "⚠️ Bandit found security issues" | |
| fi | |
| bandit -r . -f sarif -o bandit-results.sarif --configfile pyproject.toml || true | |
| continue-on-error: true | |
| - name: 5. Upload Bandit SARIF to GitHub | |
| if: always() | |
| uses: github/codeql-action/upload-sarif@v4 | |
| with: | |
| sarif_file: backend/bandit-results.sarif | |
| continue-on-error: true | |
| - name: 6. Create Security Summary | |
| if: github.event_name == 'pull_request' | |
| working-directory: ./backend | |
| run: | | |
| cat > ../security-report.md << 'EOL' | |
| ## 🔒 Security Scan Results | |
| EOL | |
| # Check actual issue count from JSON results instead of exit code | |
| ACTUAL_ISSUES=0 | |
| if [ -f bandit-results.json ]; then | |
| ACTUAL_ISSUES=$(python3 -c "import json; data=json.load(open('bandit-results.json')); metrics=data.get('metrics',{}); print(metrics.get('SEVERITY.HIGH',0) + metrics.get('SEVERITY.MEDIUM',0) + metrics.get('SEVERITY.LOW',0))" 2>/dev/null || echo "0") | |
| fi | |
| if [[ "$ACTUAL_ISSUES" == "0" ]]; then | |
| cat >> ../security-report.md << 'EOL' | |
| ### ✅ Bandit: **Passed** | |
| No security issues found. Your code passed all security checks! 🎉 | |
| EOL | |
| else | |
| cat >> ../security-report.md << 'EOL' | |
| ### ⚠️ Bandit: **Security Issues Found** | |
| EOL | |
| # Parse and display results if file exists | |
| if [ -f bandit-results.json ]; then | |
| echo '<details><summary>Click to view security findings</summary>' >> ../security-report.md | |
| echo '' >> ../security-report.md | |
| echo '```' >> ../security-report.md | |
| python3 << 'PYTHON_SCRIPT' >> ../security-report.md 2>/dev/null || echo "Could not parse results" >> ../security-report.md | |
| import json | |
| import sys | |
| try: | |
| with open('bandit-results.json', 'r') as f: | |
| data = json.load(f) | |
| # Print metrics | |
| metrics = data.get('metrics', {}) | |
| total_issues = sum([ | |
| metrics.get('SEVERITY.HIGH', 0), | |
| metrics.get('SEVERITY.MEDIUM', 0), | |
| metrics.get('SEVERITY.LOW', 0) | |
| ]) | |
| print(f'📊 Summary:') | |
| print(f'Total issues: {total_issues}') | |
| print(f'Files with issues: {metrics.get("files_with_issues", 0)}') | |
| print(f'') | |
| print(f'🔴 High severity: {metrics.get("SEVERITY.HIGH", 0)}') | |
| print(f'🟡 Medium severity: {metrics.get("SEVERITY.MEDIUM", 0)}') | |
| print(f'🟢 Low severity: {metrics.get("SEVERITY.LOW", 0)}') | |
| print() | |
| # Group results by severity | |
| results = data.get('results', []) | |
| high = [r for r in results if r['issue_severity'] == 'HIGH'] | |
| medium = [r for r in results if r['issue_severity'] == 'MEDIUM'] | |
| low = [r for r in results if r['issue_severity'] == 'LOW'] | |
| # Display high severity first (up to 5) | |
| if high: | |
| print('🔴 HIGH SEVERITY ISSUES:') | |
| for i, result in enumerate(high[:5], 1): | |
| print(f"{i}. {result['issue_text']}") | |
| print(f" 📍 {result['filename']}:{result['line_number']}") | |
| print(f" CWE: {result.get('issue_cwe', {}).get('id', 'N/A')}") | |
| print() | |
| if len(high) > 5: | |
| print(f" ... and {len(high) - 5} more high severity issues") | |
| print() | |
| # Display medium severity (up to 3) | |
| if medium: | |
| print('🟡 MEDIUM SEVERITY ISSUES:') | |
| for i, result in enumerate(medium[:3], 1): | |
| print(f"{i}. {result['issue_text']}") | |
| print(f" 📍 {result['filename']}:{result['line_number']}") | |
| print() | |
| if len(medium) > 3: | |
| print(f" ... and {len(medium) - 3} more medium severity issues") | |
| print() | |
| # Display low severity count only if there are issues | |
| if low: | |
| print(f'🟢 LOW SEVERITY: {len(low)} issues found') | |
| except Exception as e: | |
| print(f'Error parsing bandit results: {str(e)}') | |
| PYTHON_SCRIPT | |
| echo '```' >> ../security-report.md | |
| echo '</details>' >> ../security-report.md | |
| echo '' >> ../security-report.md | |
| echo '**Action Required:** Please review and fix the security issues before merging.' >> ../security-report.md | |
| else | |
| echo 'Could not find detailed results file.' >> ../security-report.md | |
| fi | |
| fi | |
| cat >> ../security-report.md << 'EOL' | |
| --- | |
| *Security scans help identify potential vulnerabilities in your code. [Learn more about Bandit](https://bandit.readthedocs.io/)* | |
| <!-- security-scan-comment-marker --> | |
| EOL | |
| - name: 7. Post PR Comment | |
| if: github.event_name == 'pull_request' | |
| uses: peter-evans/create-or-update-comment@v5 | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body-path: security-report.md | |
| edit-mode: replace |