gagoar / use-herald-action

GitHub action to add reviewers, subscribers, labels, and assignees to your PR. You can validate your PR template as well.
https://gagoar.github.io/use-herald-action/
MIT License
54 stars 6 forks source link

[FEATURE REQUEST] Segregation of Duties (SOD): Enforces segregation of duties by checking a APPROVER CODEOWNER file #546

Open austinsonger opened 1 month ago

austinsonger commented 1 month ago

I could figure out how to actually pull this off on my own.

Description

Feature that enforces segregation of duties by checking the APPROVER or CODEOWNER file and ensuring that no committer can approve or merge their own code.

Create a workflow file in your repository (e.g., .github/workflows/enforce_sod.yml) with the following content:

name: Enforce Segregation of Duties

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  enforce_sod:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'

    - name: Install jq and yq
      run: |
        sudo apt-get update
        sudo apt-get install -y jq
        sudo snap install yq

    - name: Run Segregation of Duties Check
      id: sod_check
      run: |
        APPROVER_FILE=".github/approvers.yml"
        COMMITTERS=$(jq -r '.commits | .[].author.username' $GITHUB_EVENT_PATH | sort | uniq)
        PRIMARY_APPROVERS=$(yq '.approvers[] | select(.number == 0) | .username' $APPROVER_FILE)
        SECONDARY_APPROVERS=$(yq '.approvers[] | select(.number == 1) | .username' $APPROVER_FILE)

        echo "Committers: $COMMITTERS"
        echo "Primary Approvers: $PRIMARY_APPROVERS"
        echo "Secondary Approvers: $SECONDARY_APPROVERS"

        for approver in $PRIMARY_APPROVERS; do
          if [[ ! " $COMMITTERS " =~ " $approver " ]]; then
            echo "Found primary approver: $approver"
            exit 0
          fi
        done

        for approver in $SECONDARY_APPROVERS; do
          if [[ ! " $COMMITTERS " =~ " $approver " ]]; then
            echo "Found secondary approver: $approver"
            exit 0
          fi
        done

        echo "No eligible approver found"
        exit 1
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

APPROVER

The APPROVER file should follow this format:

approvers:
  - username: "username1"
    number: 1
  - username: "username2"
    number: 0
  - username: "username3"
    number: 0
  - username: "username4"
    number: 0
  - username: "username5"
    number: 1
  - username: "username6"
    number: 1
Hierarchy

Action Logic

The action follows these steps to enforce segregation of duties:

1. Identify Committers and Approvers:

The action first identifies all committers (users who have made commits) and approvers (users who are requested to review the pull request).

2. Remove Ineligible Approvers:

If any approver has also committed code to the pull request, they are marked as ineligible for that PR.

3. Find Eligible Approver:

The action then searches for a new approver in the APPROVER file. It first checks the Primary APPROVER (users assigned 0). If all Primary APPROVER have committed code and are thus ineligible, the action moves on to check the Secondary APPROVER (users assigned 1).

4. Assign New Approver:

The first eligible user found (who has not committed code to the pull request) is marked as an eligible approver.

Example

Let's say the action encounters the following scenario:

The action then moves to the Secondary APPROVER:

Therefore, @username1 is assigned as the new approver.

approvers:
  - username: "username1"
    number: 1
  - username: "username2"
    number: 0
  - username: "username3"
    number: 0
  - username: "username4"
    number: 0
  - username: "username5"
    number: 1
  - username: "username6"
    number: 1