From 648ce4e7c113a098619692a72bec2793ef88f3b7 Mon Sep 17 00:00:00 2001 From: Aconite33 Date: Wed, 1 Apr 2026 13:39:08 -0600 Subject: [PATCH 1/2] fix CLA workflow to properly skip bots and check all committers Replace the single PR-author org membership check with a unified step that iterates all committers on the PR and checks each against: 1. GitHub API account type (type == "Bot" for App accounts) 2. Org membership Commits with no associated GitHub login are treated as non-exempt (prevents bypass via spoofed email with no GitHub account). Tested on aconite33/cla-workflow-test: - Human committer: correctly required CLA - github-actions[bot]: correctly skipped via API type check --- .github/workflows/cla.yml | 99 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 .github/workflows/cla.yml diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml new file mode 100644 index 0000000000..b688e53caf --- /dev/null +++ b/.github/workflows/cla.yml @@ -0,0 +1,99 @@ +name: "CLA Assistant" +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +permissions: + actions: write + contents: write + pull-requests: write + statuses: write + +jobs: + CLAAssistant: + runs-on: ubuntu-latest + steps: + - name: Generate token from GitHub App + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: blacklanternsecurity + + - name: Check all committers against org and allowlist + id: cla-check + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + if [ "${{ github.event_name }}" = "pull_request_target" ]; then + PR_NUM="${{ github.event.pull_request.number }}" + else + PR_NUM="${{ github.event.issue.number }}" + fi + + COMMITTERS=$(gh api "repos/${{ github.repository }}/pulls/$PR_NUM/commits" --paginate --jq '.[].author.login' | sort -u) + ALL_EXEMPT=true + + for LOGIN in $COMMITTERS; do + # treat commits with no associated GitHub login as non-exempt + if [ -z "$LOGIN" ] || [ "$LOGIN" = "null" ]; then + echo "Unknown committer (no GitHub login) — not exempt" + ALL_EXEMPT=false + break + fi + + EXEMPT=false + + # check if account type is Bot (GitHub App accounts) + AUTHOR_TYPE=$(gh api "users/${LOGIN}" --jq '.type' 2>/dev/null || echo "Unknown") + if [ "$AUTHOR_TYPE" = "Bot" ]; then + echo "$LOGIN is a Bot account — exempt" + EXEMPT=true + fi + + # check org membership + if [ "$EXEMPT" = "false" ]; then + if gh api "orgs/blacklanternsecurity/members/$LOGIN" > /dev/null 2>&1; then + echo "$LOGIN is an org member — exempt" + EXEMPT=true + fi + fi + + if [ "$EXEMPT" = "false" ]; then + echo "$LOGIN is not exempt — CLA required" + ALL_EXEMPT=false + break + fi + done + + echo "all_exempt=$ALL_EXEMPT" >> "$GITHUB_OUTPUT" + + - name: Skip CLA when all committers are exempt + if: steps.cla-check.outputs.all_exempt == 'true' && github.event_name == 'pull_request_target' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api --method POST "repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }}" \ + -f state=success \ + -f context="CLAAssistant" \ + -f description="CLA check skipped — all committers are org members or bots" + + - name: "CLA Assistant" + if: | + (steps.cla-check.outputs.all_exempt != 'true') && + ((github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target') + uses: contributor-assistant/github-action@v2.6.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PERSONAL_ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} + with: + path-to-signatures: "signatures/version1/cla.json" + path-to-document: "https://github.com/blacklanternsecurity/CLA/blob/main/ICLA.md" + branch: "main" + allowlist: "dependabot[bot],github-actions[bot],renovate[bot]" + remote-organization-name: "blacklanternsecurity" + remote-repository-name: "CLA" + lock-pullrequest-aftermerge: "false" From 83833e71ccca8fd11216200732a67a2f8d1cb465 Mon Sep 17 00:00:00 2001 From: Aconite33 Date: Wed, 1 Apr 2026 13:48:09 -0600 Subject: [PATCH 2/2] drop unnecessary actions:write and contents:write permissions Tested on aconite33/cla-workflow-test with only pull-requests:write and statuses:write -- both human CLA and bot skip paths work correctly. The CLA assistant uses PERSONAL_ACCESS_TOKEN (app token) for writing signatures to the remote CLA repo, not GITHUB_TOKEN. --- .github/workflows/cla.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index b688e53caf..3fadea2fe3 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -6,8 +6,6 @@ on: types: [opened, closed, synchronize] permissions: - actions: write - contents: write pull-requests: write statuses: write