From d5098732dcbdcedd8b3a688be669098da12be044 Mon Sep 17 00:00:00 2001 From: Aconite33 Date: Wed, 1 Apr 2026 12:33:26 -0600 Subject: [PATCH 1/3] skip CLA check for bot accounts in CI workflow Add a bot detection step that checks if the PR author's login ends with [bot] (e.g. dependabot[bot], github-actions[bot], renovate[bot]). When detected, the CLA check is skipped with a success status, and the CLA assistant action is not invoked at all. This fixes CLA failures on automated dependency PRs where the allowlist in the CLA assistant action was not matching correctly. --- .github/workflows/cla.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index bb9c379173..2a9d87fe55 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -50,9 +50,34 @@ jobs: -f context="CLAAssistant" \ -f description="CLA check skipped — author is an org member" + - name: Check if author is a bot + id: bot-check + if: github.event_name == 'pull_request_target' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + AUTHOR="${{ github.event.pull_request.user.login }}" + AUTHOR_TYPE=$(gh api "users/${AUTHOR}" --jq '.type' 2>/dev/null || echo "Unknown") + if [ "$AUTHOR_TYPE" = "Bot" ]; then + echo "is_bot=true" >> "$GITHUB_OUTPUT" + else + echo "is_bot=false" >> "$GITHUB_OUTPUT" + fi + + - name: Skip CLA for bots + if: steps.bot-check.outputs.is_bot == '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 — author is a bot" + - name: "CLA Assistant" if: | (steps.membership.outputs.is_member != 'true') && + (steps.bot-check.outputs.is_bot != '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: From 8c6a79fa204db0d76e11e4ebab40c3d874a40729 Mon Sep 17 00:00:00 2001 From: liquidsec Date: Wed, 1 Apr 2026 15:19:04 -0400 Subject: [PATCH 2/3] check all PR committers against org membership and bot allowlist Instead of only checking the PR author, iterate over every unique commit author and verify each is either an org member or a known bot. Fixes CLA failures on PRs with mixed committers (e.g. dependabot PRs that include org member commits due to branch divergence). --- .github/workflows/cla.yml | 70 ++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 2a9d87fe55..b33f62fddd 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -23,61 +23,55 @@ jobs: private-key: ${{ secrets.APP_PRIVATE_KEY }} owner: blacklanternsecurity - - name: Check org membership - id: membership + - 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 - AUTHOR="${{ github.event.pull_request.user.login }}" + PR_NUM="${{ github.event.pull_request.number }}" else PR_NUM="${{ github.event.issue.number }}" - AUTHOR=$(gh api "repos/${{ github.repository }}/pulls/$PR_NUM" --jq '.user.login' 2>/dev/null) - fi - if [ -n "$AUTHOR" ] && gh api "orgs/blacklanternsecurity/members/$AUTHOR" > /dev/null 2>&1; then - echo "is_member=true" >> "$GITHUB_OUTPUT" - else - echo "is_member=false" >> "$GITHUB_OUTPUT" - fi - - - name: Skip CLA for org members - if: steps.membership.outputs.is_member == '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 — author is an org member" - - - name: Check if author is a bot - id: bot-check - if: github.event_name == 'pull_request_target' - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - run: | - AUTHOR="${{ github.event.pull_request.user.login }}" - AUTHOR_TYPE=$(gh api "users/${AUTHOR}" --jq '.type' 2>/dev/null || echo "Unknown") - if [ "$AUTHOR_TYPE" = "Bot" ]; then - echo "is_bot=true" >> "$GITHUB_OUTPUT" - else - echo "is_bot=false" >> "$GITHUB_OUTPUT" fi + BOT_ALLOWLIST="dependabot[bot] github-actions[bot] renovate[bot]" + COMMITTERS=$(gh api "repos/${{ github.repository }}/pulls/$PR_NUM/commits" --paginate --jq '.[].author.login' | sort -u) + ALL_EXEMPT=true + for LOGIN in $COMMITTERS; do + [ -z "$LOGIN" ] && continue + # check bot allowlist + EXEMPT=false + for BOT in $BOT_ALLOWLIST; do + if [ "$LOGIN" = "$BOT" ]; then + EXEMPT=true + break + fi + done + # check org membership + if [ "$EXEMPT" = "false" ]; then + if gh api "orgs/blacklanternsecurity/members/$LOGIN" > /dev/null 2>&1; then + EXEMPT=true + fi + fi + if [ "$EXEMPT" = "false" ]; then + ALL_EXEMPT=false + break + fi + done + echo "all_exempt=$ALL_EXEMPT" >> "$GITHUB_OUTPUT" - - name: Skip CLA for bots - if: steps.bot-check.outputs.is_bot == 'true' && github.event_name == 'pull_request_target' + - 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 — author is a bot" + -f description="CLA check skipped — all committers are org members or bots" - name: "CLA Assistant" if: | - (steps.membership.outputs.is_member != 'true') && - (steps.bot-check.outputs.is_bot != 'true') && + (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: From 3e579127ebadb05d700d68f41f4647e393ba1fb0 Mon Sep 17 00:00:00 2001 From: Aconite33 Date: Wed, 1 Apr 2026 13:35:28 -0600 Subject: [PATCH 3/3] use GitHub API type field for bot detection and harden null logins Replace string-based bot allowlist with API lookup of the account's type field (returns "Bot" for GitHub App accounts, enforced server-side and not spoofable). Also treat commits with no associated GitHub login as non-exempt instead of silently skipping them. Tested on aconite33/cla-workflow-test: - PR #1 (human): correctly required CLA - PR #3 (github-actions[bot]): correctly skipped via API type check --- .github/workflows/cla.yml | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index b33f62fddd..35817b8dd0 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -33,26 +33,36 @@ jobs: else PR_NUM="${{ github.event.issue.number }}" fi - BOT_ALLOWLIST="dependabot[bot] github-actions[bot] renovate[bot]" COMMITTERS=$(gh api "repos/${{ github.repository }}/pulls/$PR_NUM/commits" --paginate --jq '.[].author.login' | sort -u) ALL_EXEMPT=true + for LOGIN in $COMMITTERS; do - [ -z "$LOGIN" ] && continue - # check bot allowlist + # 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 - for BOT in $BOT_ALLOWLIST; do - if [ "$LOGIN" = "$BOT" ]; then - EXEMPT=true - break - fi - done + + # 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