Detect evil merge commits
before they ship
Evil Merge Detector finds merge commits that introduce changes not present in either parent branch — the attack vector your code review misses.
Hidden in the merge,
invisible in the PR
When both parent branches contain identical files, Git’s three-way merge algorithm outputs them unchanged. The only way to get a different result is to manually edit files during the merge.
GitHub’s PR diff doesn’t show merge commits. git log doesn’t surface the change. SAST tools scan files, not merge history. The injection is invisible.
This is how malicious code ran undetected in a production repository for several months — on every developer machine and every CI build.
It’s a supply chain attack via code injection — and it bypasses every standard git security tool.
GitHub’s response: “This is an intentional design decision and is working as expected. We may make this functionality more strict in the future, but don’t have anything to announce right now.” The responsibility to detect it falls entirely on your team.
Simple detection,
no false positives
For each merge commit, we reconstruct what Git should have produced and compare it to what the commit actually contains.
Find the merge base
Identify the common ancestor of the two parent commits — the starting point for the three-way merge algorithm.
Reconstruct expected tree
Run a clean three-way merge of the parent trees. This is what Git would produce with no manual intervention.
Compare against reality
Diff the expected tree against the actual merge commit. Any difference is a file manually edited during the merge.
Works where you
already work
Multiple ways to add evil merge detection — pick what fits your workflow.
Command Line
Scan any repository from the terminal. Supports JSON and SARIF output for GitHub Code Scanning.
brew install fimskiy/tap/evilmerge evilmerge scan .
GitHub Action
Add to your workflow and get annotations directly on pull requests. Zero configuration.
- uses: fimskiy/Evil-merge-detector@v1
with:
fail-on: warning
GitHub App
Install once, get automatic checks on every PR. No workflow changes needed.
Install from GitHub Marketplace → automatic on every pull request → results in GitHub Checks
Simple, per-organization
pricing
The CLI and GitHub Action are always free and open source.
- ✓ Public repositories
- ✓ 50 PR scans / month
- ✓ GitHub Checks integration
- – Private repositories
- ✓ Scan history dashboard
- – Unlimited scans
No credit card required
- ✓ Public & private repositories
- ✓ Unlimited PR scans
- ✓ GitHub Checks integration
- ✓ Scan history dashboard
- ✓ Priority support
Cancel anytime · No lock-in
Common questions
Does it need access to my source code?
The GitHub App requests read-only access to repository contents and checks — the minimum required to scan merge commits. The CLI runs entirely locally; nothing leaves your machine.
What counts as a PR scan?
One scan = one pull request event (opened or synchronized). Scans that find no merge commits in the PR are not counted against your limit.
Can it produce false positives?
No. The detection is deterministic: if both parent branches have identical file content, Git's algorithm cannot produce a different output. Any difference is a manual edit — there is no ambiguity.
Does it work with GitLab or Bitbucket?
The CLI works with any Git repository regardless of host. Ready-to-use CI templates for GitLab CI and Bitbucket Pipelines are available in the repository. The GitHub App and GitHub Checks integration are GitHub-only.
Doesn’t enabling “Dismiss stale reviews” prevent this?
Partially — and only going forward. “Dismiss stale reviews” forces re-review when new commits are pushed, which makes the attack harder to execute. But it doesn’t scan your existing history for past injections, requires manual setup on every repository, and can be changed by any admin at any time. GitHub themselves confirmed this attack vector is working as designed and has no plans to address it — making detection, not just prevention, essential.
What happens when I exceed 50 scans on the Free plan?
Additional PRs will not be scanned and the check run will be skipped with a note explaining the limit. No errors, no failed checks — just a nudge to upgrade.
Your next merge could be
hiding something.
Install the GitHub App and start scanning automatically — no workflow changes needed.