gitleaks / gitleaks/gitleaks-action
First-parent-only scan on push/pull_request silently misses content merged in via a commit's second parent
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 646
- Forks
- 195
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`gitleaks-action` hardcodes `--no-merges --first-parent` for the git log range on both `push` and `pull_request` events, with no way to opt out. `--first-parent` only walks a branch's first-parent chain, so any commit reachable solely through a merge commit's **second** parent is silently excluded from the scan. This is a false negative, not a crash: the action reports clean even though a secret is present in the branch's history.
This is a different bug from #163 and #199, which are about `--first-parent` producing `fatal: ambiguous argument` and failing the job outright. Here the job succeeds and reports no leaks, which is worse: nothing signals that content went unscanned.
## Where this comes from
https://github.com/gitleaks/gitleaks-action/blob/main/src/gitleaks.js#L92-L117
```js
if (eventType == "push") {
if (scanInfo.baseRef == scanInfo.headRef) {
args.push(`--log-opts=-1`);
} else {
args.push(
`--log-opts=--no-merges --first-parent ${scanInfo.baseRef}^..${scanInfo.headRef}`
);
}
} else if (eventType == "pull_request") {
args.push(
`--log-opts=--no-merges --first-parent ${scanInfo.baseRef}^..${scanInfo.headRef}`
);
}
```
There's no input to change this range or drop the flags.
## Reproduction
1. Create a branch `feature` off `main`.
2. Create a second branch `side` off `main`, and add a commit on `side` that introduces a secret (e.g. an obviously-fake but pattern-matching value like an API key literal).
3. On `feature`, run `git merge side` (an ordinary merge commit, not a rebase).
4. Open a PR from `feature` into `main`, or push `feature` directly if it's your default branch flow.
5. `gitleaks-action` runs `git log --no-merges --first-parent ^..`. The commit that introduced the secret lives only on `side`'s history, reachable from `feature` through the merge commit's *second* parent. `--first-parent` never visits it, so its diff is never scanned.
6. The action reports no leaks. The secret is present in the branch and will land on the base branch if merged.
This is a realistic workflow, not a contrived edge case: merging a sibling branch into a working branch, or merging the base branch back into a stuck PR branch to force a mergeable ref (a documented workaround for GitHub's stacked-PR "no computable merge ref" state), both produce exactly this shape.
## What we'd expect
Either:
- Drop `--first-parent`/`--no-merges` by default and use a plain two-dot range (`base..head`), which walks every commit reachable from `head` but not `base` across *all* parents, or
- Add an input (e.g. `GITLEAKS_LOG_OPTS` or similar) so callers can override the log-opts entirely when their branch history includes merge commits.
## Workaround we're using
We stopped using `gitleaks-action` and run the raw scanner ourselves via `docker://ghcr.io/gitleaks/gitleaks:` with an explicit two-dot range computed per event (`pull_request.base.sha..pull_request.head.sha`, `before..after` for push, etc.), no `--first-parent`/`--no-merges`. That closes the gap, but it means we lose gitleaks-action's built-in PR comment and org-license convenience to get correct scan coverage.
## Environment
- `gitleaks-action` pinned commit: `e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e` (labeled `v3.0.0`)
- `GITLEAKS_VERSION: 8.30.1`
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/gitleaks.js around lines 92-117, where push and pull_request log options are assembled. Trace how baseRef and headRef become the scan range, then reproduce the merge-with-second-parent case described in the issue. Done means the action scans commits reachable through merged branches instead of silently reporting a clean result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, github-actions, javascript
- Domain
- ci-cd, devops, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100