ci: add direct-push detector sentinel (replaces #68)#89
Conversation
Add a complementary CI sentinel that runs on push to main and verifies the latest commit follows expected merge patterns: PR squash merge, PR merge commit, oficial bot, or release commit. Falls into failure state for direct pushes that don't match. Defense-in-depth alongside the GitHub branch protection rule and the existing branch-protection-sentinel.yml (which checks code quality on PRs — different responsibility). Replaces obsolete PR #68. Renamed file to avoid name collision with the quality sentinel that now lives in main.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughNovo workflow GitHub Actions que detecta e bloqueia pushes diretos não-autorizados em ChangesProteção de Branch — Detector de Push Direto
Estimated code review effort🎯 2 (Simples) | ⏱️ ~8 minutos 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 25ae0c2af8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| 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 |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with 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.
Inline comments:
In @.github/workflows/branch-protection-direct-push.yml:
- Around line 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.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 74a5375d-5e8c-4ae5-8974-6b68eb8ff784
📒 Files selected for processing (1)
.github/workflows/branch-protection-direct-push.yml
| MSG=$(git log -1 --pretty=%B) | ||
| AUTHOR=$(git log -1 --pretty=%an) |
There was a problem hiding this comment.
🧩 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:
- 1: https://git-scm.com/docs/pretty-formats.html
- 2: https://stackoverflow.com/questions/3128993/is-there-some-way-to-fake-the-name-email-info-for-the-author-of-a-git-commit-t
- 3: https://docs.github.com/en/actions/reference/contexts-reference
- 4: https://docs.github.com/developers/webhooks-and-events/webhooks/webhook-events-and-payloads
- 5: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads
- 6: https://docs.github.com/en/rest/using-the-rest-api/github-event-types?apiVersion=2026-03-10
🏁 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 -20Repository: adm01-debug/zapp-web
Length of output: 294
🏁 Script executed:
cat -n .github/workflows/branch-protection-direct-push.ymlRepository: 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.
| 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.
There was a problem hiding this comment.
Pull request overview
Adds a complementary GitHub Actions workflow to detect direct pushes to main that don’t match expected PR-merge/bot/release patterns, providing defense-in-depth alongside GitHub branch protection settings.
Changes:
- Introduces
branch-protection-direct-push.ymlthat runs onpushtomain(andworkflow_dispatch) and inspects the latest commit’s author/message. - Emits a clear failure (
exit 1) plusGITHUB_STEP_SUMMARYguidance when the push doesn’t match allowed patterns.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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 | ||
| "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 | ||
| echo "https://github.com/${{ github.repository }}/settings/branches" >> $GITHUB_STEP_SUMMARY |
| 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 |
| MSG=$(git log -1 --pretty=%B) | ||
| AUTHOR=$(git log -1 --pretty=%an) | ||
| 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 | ||
| "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 | ||
| echo "https://github.com/${{ github.repository }}/settings/branches" >> $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 |
Resumo
Adiciona um workflow CI complementar que detecta pushes diretos em
mainsem padrão de PR/bot/release — defesa em profundidade junto com a regra de branch protection do GitHub.Substitui PR #68 (fechado)
O #68 tentava criar um arquivo
branch-protection-sentinel.ymlmas esse nome já está ocupado em main por um sentinel diferente (que checa qualidade —console.loge:anycount > 200 em PRs). Mergear o #68 sobrescreveria o sentinel de qualidade.Solução: arquivo renomeado pra
branch-protection-direct-push.yml— os dois coexistem.Diferenças vs sentinel existente
branch-protection-sentinel.yml(já em main)pull_requestbranch-protection-direct-push.yml(este PR)pushpara mainO que o workflow faz
Roda em
pushem main e emworkflow_dispatch. Inspeciona o último commit:(#NNN)(squash merge)Merge pull request #NNNgithub-actions[bot],dependabot[bot],lovable-dev[bot], ourenovate[bot]chore(release):exit 1) com aviso no STEP_SUMMARYTest plan
main@7b1f478e(atualizada)pushpara main)docs/BRANCH_PROTECTION.md)Notas
name:ajustado e 4 linhas de comentário explicando coexistênciaSummary by CodeRabbit