--- # Security Analysis Workflow # Self-contained security scan (Bandit + OSV-Scanner). # # Note: this workflow is intentionally standalone and does NOT call the org # reusable python-security-analysis.yml, which has been failing at startup # (startup_failure) for months and silently withheld the required # "Security Gate Validation" status check. CodeQL and dependency review are # covered by codeql.yml and dependency-review.yml respectively. name: Security Analysis "on": push: branches: [main, master] pull_request: branches: [main, master] schedule: # Run security scans weekly on Monday at 9 AM UTC - cron: '0 9 * * 1' workflow_dispatch: # Cancel in-progress runs for same PR/branch concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: contents: read jobs: security: name: Security Scan runs-on: ubuntu-latest permissions: contents: read steps: - name: Harden the runner uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install uv uses: astral-sh/setup-uv@18d369b23da25a81e9458561248e1aafff6a70fa # v10.3.1 with: enable-cache: true - name: Set up Python run: uv python install 3.12 - name: Install dependencies run: uv sync --all-extras - name: Run Bandit security scan run: uv run bandit -r src/ -c pyproject.toml -f json -o bandit-report.json || true - name: Run OSV-Scanner dependency scan uses: google/osv-scanner-action/osv-scanner-action@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 with: scan-args: |- --output=osv-scanner-report.json --format=json --recursive ./ continue-on-error: true - name: Upload security reports uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: security-reports path: | bandit-report.json osv-scanner-report.json retention-days: 30 # Required status check context: "Security Gate Validation" # (matches the org ruleset; gated on the standalone security scan result). security-gate-success: name: Security Gate Validation runs-on: ubuntu-latest needs: [security] if: always() permissions: contents: read steps: - name: Harden the runner uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 with: egress-policy: audit - name: Check security scan results env: SECURITY_RESULT: ${{ needs.security.result }} run: | if [ "$SECURITY_RESULT" == "success" ]; then echo "Security Gate passed" else echo "Security Gate failed: $SECURITY_RESULT" exit 1 fi