Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/branch-protection-direct-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Branch Protection — Direct Push Detector

# Detecta pushes diretos em main sem padrão de PR/bot/release.
# Camada de defesa em profundidade complementar à branch protection rule do GitHub.
# NÃO confundir com `branch-protection-sentinel.yml` (que checa qualidade em PRs).

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

jobs:
verify-merge-pattern:
name: Verify push to main is from PR merge
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Inspect last commit on main
run: |
MSG=$(git log -1 --pretty=%B)
AUTHOR=$(git log -1 --pretty=%an)
Comment on lines +26 to +27
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

No GitHub Actions (evento push), git log --pretty=%an pode ser forjado por quem criou o commit? Qual campo representa o ator autenticado do push (github.actorougithub.event.sender.login)?

💡 Result:

Sim. Quem tiver permissão para empurrar (push) commits para o repositório pode forjar o autor exibido pelo git log --pretty=%an, porque %an é o “author name” armazenado no objeto commit do Git (campo “author” do commit), e ele pode ser definido/alterado no momento do commit (ex.: com --author ou GIT_AUTHOR_NAME/EMAIL). O GitHub Actions também pode reagir a esses commits sem validar se o nome do autor bate com a conta do “pusher”. [1][2] Sobre qual campo representa o ator autenticado do push no GitHub Actions: - O contexto github.actor é “o username do usuário que iniciou a execução do workflow”. [3] - No payload do webhook de push, existe sender.login (no payload de webhook “sender” é o usuário que enviou o evento). [4][5] Assim, em geral para on: push, para identificar o ator que disparou o workflow, use github.actor (não github.event.sender.login diretamente, já que ambos apontam para a mesma noção de “triggering user” no payload/context). [3][4] Alternativa: se você estiver usando o REST API “events”, o campo comum é actor.login (equivalente ao “quem disparou”). [6] Resumo prático: - git log --pretty=%an: representa o “author” do commit (forjável por quem cria/reescreve o commit). [1] - github.actor: representa o usuário que iniciou a execução do workflow (ator do push que gerou o evento). [3] - github.event.sender.login: também representa o “sender” do webhook (ator do evento). [4]

Citations:


🏁 Script executed:

fd -t f "branch-protection-direct-push.yml" --exec cat -n {} \;

Repository: adm01-debug/zapp-web

Length of output: 46


🏁 Script executed:

find .github/workflows -type f -name "*.yml" -o -name "*.yaml" | head -20

Repository: adm01-debug/zapp-web

Length of output: 294


🏁 Script executed:

cat -n .github/workflows/branch-protection-direct-push.yml

Repository: adm01-debug/zapp-web

Length of output: 2624


Use ${{ github.actor }} para autorizar bots — git log --pretty=%an é spoofável.

O campo %an (author name) é metadado do commit e pode ser forjado por quem faz o push — alguém consegue registrar --author="github-actions[bot]" e contornar esse sentinel. Use ${{ github.actor }} (identidade autenticada do webhook), que não pode ser falsificada.

Patch
       - name: Inspect last commit on main
         run: |
           MSG=$(git log -1 --pretty=%B)
           AUTHOR=$(git log -1 --pretty=%an)
+          ACTOR="${{ github.actor }}"
           SHA=$(git log -1 --pretty=%H)
 
           echo "### Commit inspecionado" >> $GITHUB_STEP_SUMMARY
           echo "- SHA: \`$SHA\`" >> $GITHUB_STEP_SUMMARY
           echo "- Autor: $AUTHOR" >> $GITHUB_STEP_SUMMARY
           echo "- Mensagem: $MSG" >> $GITHUB_STEP_SUMMARY
 
           if echo "$MSG" | head -1 | grep -qE '\(#[0-9]+\)$'; then
             echo "OK: squash merge de PR" >> $GITHUB_STEP_SUMMARY
             exit 0
           fi
 
           if echo "$MSG" | head -1 | grep -qE '^Merge pull request #[0-9]+'; then
             echo "OK: merge commit de PR" >> $GITHUB_STEP_SUMMARY
             exit 0
           fi
 
-          case "$AUTHOR" in
+          case "$ACTOR" in
             "github-actions[bot]"|"dependabot[bot]"|"lovable-dev[bot]"|"renovate[bot]")
-              echo "OK: bot oficial ($AUTHOR)" >> $GITHUB_STEP_SUMMARY
+              echo "OK: bot oficial ($ACTOR)" >> $GITHUB_STEP_SUMMARY
               exit 0
               ;;
           esac
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
MSG=$(git log -1 --pretty=%B)
AUTHOR=$(git log -1 --pretty=%an)
- name: Inspect last commit on main
run: |
MSG=$(git log -1 --pretty=%B)
AUTHOR=$(git log -1 --pretty=%an)
ACTOR="${{ github.actor }}"
SHA=$(git log -1 --pretty=%H)
echo "### Commit inspecionado" >> $GITHUB_STEP_SUMMARY
echo "- SHA: \`$SHA\`" >> $GITHUB_STEP_SUMMARY
echo "- Autor: $AUTHOR" >> $GITHUB_STEP_SUMMARY
echo "- Mensagem: $MSG" >> $GITHUB_STEP_SUMMARY
if echo "$MSG" | head -1 | grep -qE '\(#[0-9]+\)$'; then
echo "OK: squash merge de PR" >> $GITHUB_STEP_SUMMARY
exit 0
fi
if echo "$MSG" | head -1 | grep -qE '^Merge pull request #[0-9]+'; then
echo "OK: merge commit de PR" >> $GITHUB_STEP_SUMMARY
exit 0
fi
case "$ACTOR" in
"github-actions[bot]"|"dependabot[bot]"|"lovable-dev[bot]"|"renovate[bot]")
echo "OK: bot oficial ($ACTOR)" >> $GITHUB_STEP_SUMMARY
exit 0
;;
esac
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/branch-protection-direct-push.yml around lines 26 - 27,
The script currently sets AUTHOR from the commit metadata using git log
--pretty=%an which is spoofable; change the AUTHOR assignment to use the
authenticated actor value provided by GitHub Actions (GITHUB_ACTOR /
github.actor) instead of git log --pretty=%an so bots cannot be impersonated;
update the assignment that references AUTHOR to pull from the environment
variable (e.g., AUTHOR from GITHUB_ACTOR) and leave MSG (git log -1 --pretty=%B)
as-is for the commit message.

SHA=$(git log -1 --pretty=%H)

echo "### Commit inspecionado" >> $GITHUB_STEP_SUMMARY
echo "- SHA: \`$SHA\`" >> $GITHUB_STEP_SUMMARY
echo "- Autor: $AUTHOR" >> $GITHUB_STEP_SUMMARY
echo "- Mensagem: $MSG" >> $GITHUB_STEP_SUMMARY

if echo "$MSG" | head -1 | grep -qE '\(#[0-9]+\)$'; then
echo "OK: squash merge de PR" >> $GITHUB_STEP_SUMMARY
exit 0
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The branch protection workflow fails to recognize "Rebase and merge" as a valid PR merge strategy, causing false positive alerts

Fix on Vercel


if echo "$MSG" | head -1 | grep -qE '^Merge pull request #[0-9]+'; then
Comment on lines +35 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow PRs merged with rebase

If the repository has GitHub’s “Rebase and merge” option enabled, a legitimate PR merge lands as the PR’s individual commits without a Merge pull request line or appended (#NNN) squash title, so this check will classify that valid PR merge as a direct push and fail the main-branch workflow. Either disable rebase merges in repo settings/documentation or add a way to recognize the push as PR-originated instead of only accepting squash/merge-commit message formats.

Useful? React with 👍 / 👎.

echo "OK: merge commit de PR" >> $GITHUB_STEP_SUMMARY
exit 0
fi

case "$AUTHOR" in
"github-actions[bot]"|"dependabot[bot]"|"lovable-dev[bot]"|"renovate[bot]")
echo "OK: bot oficial ($AUTHOR)" >> $GITHUB_STEP_SUMMARY
exit 0
;;
esac

if echo "$MSG" | head -1 | grep -qE '^chore\(release\):'; then
echo "OK: release commit" >> $GITHUB_STEP_SUMMARY
exit 0
fi

echo "::error::Push em main sem padrao de PR/bot/release detectado."
echo "### Direct push detectado" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Verificar se branch protection esta ativa em:" >> $GITHUB_STEP_SUMMARY
Comment on lines +57 to +60
echo "https://github.com/${{ github.repository }}/settings/branches" >> $GITHUB_STEP_SUMMARY
Comment on lines +30 to +61
Comment on lines +26 to +61
exit 1
Loading