Findings from pull requests
How Prodgator carries findings from a pull request scan over to the tracked branch when the pull request merges.
Availability
Most teams scan pull requests. Prodgator keeps those results out of the Findings view, because only scan results on the tracked branch open findings. When the pull request merges, Prodgator checks whether the merged code is exactly the code that was scanned. If it is, the findings carry over to the tracked branch, open or update issues there, and wait for a scan of the tracked branch to confirm or clear them.
When a finding carries over
All of these must be true:
- The scan ran in your own repository, not a fork, and Prodgator trusts it.
- It scanned the latest commit of the pull request.
- The pull request merged into the tracked branch.
- The merged code is exactly the code that was scanned.
Prodgator compares git trees, so merge commits, squash merges and rebase merges all work. What it compares depends on the provider:
| Provider | What Prodgator compares with the merged commit |
|---|---|
| GitHub | The merge preview commit that the scan checked out |
| GitLab | The merged results commit, or the merge request head in a plain merge request pipeline |
| Bitbucket | The source commit. Bitbucket Pipelines merges the destination branch inside the build and keeps no record of it |
| Azure Repos | The merge preview commit that the scan checked out. The merged commit must be on the target branch |
What you see
A carried finding shows a merge badge with the pull request number (or merge request, on GitLab). Hover or focus the badge for an explanation. The badge stays while the finding waits for a scan of the tracked branch, and goes away once that scan confirms it.
The finding's details show "Introduced by", with the pull request, who merged it and when. If the pull request merged with a gate override or past a failing check, the line says so.
On the Security page, the Pull requests filter has three options: Carried over, waiting for a scan, Introduced by a pull request and Not carried over. You can also filter by the pull request that introduced a finding.
The pull request page lists the findings the pull request introduced, with the outcome of each scan and a link to show them in Security.
Prodgator sends one notification per merged pull request when the carry-over opens critical or high issues. It goes to the Security and compliance category, like other security notifications.
When a finding does not carry over
A finding that was not carried over shows a note. Each note says what to do:
| Note | Meaning | What to do |
|---|---|---|
| The merged code differs from what was scanned | The merge changed the code, for example a conflict fix or other changes merged at the same time. | Scan the tracked branch. |
| The scan was of an older commit | A newer commit was pushed to the pull request after this scan. | Run the scan again on the latest commit. |
| Prodgator could not compare the merged code with what was scanned | The provider did not answer, or no longer has the merge commit. | Scan the tracked branch. |
| The scan finished more than 7 days after the merge | See the next section. | Scan the tracked branch. |
| A scan of the tracked branch after the merge already covers this code | A newer source exists. | Nothing. |
Scans from forks, and scans of branches other than the tracked branch, write no note.
Scans that finish after the merge
A scan can finish after the pull request merges. Prodgator still carries its findings, up to 7 days after the merge. It does not carry them if a scan of the tracked branch that contains the merge has already run, because that scan is the better source.
How a scan of the tracked branch settles carried findings
A carried finding stays pending until a scan of the tracked branch settles it. Settling can:
- Confirm the finding, when the scan reports it. It becomes a normal finding of the tracked branch and the badge goes away.
- Clear it, when the scan does not report it. The finding is marked fixed with the reason "Not found by the scan of main on abc1234" (the branch and commit of that scan).
- Mark it as already on the tracked branch, when the finding was open there before the merge.
Prodgator is strict about what counts as a settling scan:
- It must be the latest trusted, complete scan of the tracked branch by the same tool. For ASH, the scanner inside ASH that found the finding must have run. A different scanner never clears a finding it did not report.
- It must be of a commit that contains the merge. A scan of a commit that does not contain the commit where the finding was last seen never clears it.
- A scan attestation with set to never confirms or clears carried findings, and never marks findings fixed. Without , a scan counts as full. See Upload from GitHub Actions.
- Direct uploads from the Security page or the API count as scans of the tracked branch when a member of your organization or one of its API keys made them.
Anything Prodgator cannot verify leaves the finding waiting.
Issues that already existed
If an issue already existed before the carry-over, the carry-over only adds to it. The issue follows its normal rules and shows no clear reason. Only an issue that the carry-over opened can end up fixed with that reason.
Provenance
Prodgator records the pull request that introduced each carried finding. The record stays after a scan of the tracked branch confirms or clears the finding. If two pull requests carry the same finding, the first one is the one recorded. The second pull request's page still lists the finding, and says it was introduced earlier.
Findings from before this feature show no pull request. Prodgator does not look back at past merges, because it did not keep the merge details needed to compare the code exactly.
Introduced in
For code findings in SARIF scans, the report step also runs on the lines of each finding and sends the commit that last changed them. The finding's details then show an "Introduced in" line, for every finding with blame, not only carried ones. Today it names the commit alone: "Introduced in abc1234." On a carried finding, the "Introduced by" line about the pull request that carried it follows.
Prodgator names a pull request on this line only when it can confirm that the pull request merged into the tracked branch and contains the commit. It does not yet check which branch a pull request merged into, so for now the line shows the commit alone. Once it can confirm the branch, a line with a pull request reads "Introduced in abc1234 (pull request #118). Carried to main by pull request #123, merged by octocat." If several pull requests contain the commit, the one merged first is named. A commit that no merged pull request explains yet keeps the commit alone, and Prodgator checks again for a few days.
Blame reads the lines the finding points to from the file as committed at the scanned commit (HEAD), not from the files on disk, so changes a build step made in the workspace do not count. It runs with , so a change that only reformats whitespace, and lines moved or copied from another file, are followed back to the commit that wrote them. A rename or a reformat does not become the introducing commit.
Blame is not applicable to some findings:
- Dependency findings, which point at a manifest or lock file rather than code you wrote.
- Findings with no file or line.
- Findings in files that are not in the workspace, are binary, or that git cannot blame.
Blame needs history. A shallow clone limits it: when a line's last change is older than the clone, the finding shows no commit. Fetch the full history:
| CI system | Setting |
|---|---|
| GitHub Actions | on |
| GitLab CI | |
| Bitbucket Pipelines | with |
| Azure Pipelines | on the step |
The report step deepens a shallow clone a few times by itself before it gives up, and it stops after 60 seconds or 500 findings per step, so a very large scan can leave some findings without a commit.
The report step also sends a hash of each finding's code, so the finding keeps its identity when lines above it are added or removed. Hashing has its own limit, apart from blame: 30 seconds and 5,000 findings per step. A finding past that limit is identified by its line, as it is without blame.
To turn blame off, set the action input , run the CLI with or , set the Azure Pipelines task input to false, or set for the Bitbucket pipe and the GitLab component. Findings without blame show the "Introduced by" line described above, or nothing.
Scan your tracked branch
Carry-over gets findings onto the tracked branch early. A scan of the tracked branch is what confirms them, and clears the ones that are gone. Scan it on every push and on a schedule, so a quiet repository still gets settled:
on:
pull_request:
push:
branches: [main]
schedule:
- cron: "0 3 * * *"
permissions:
id-token: write
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: trivy fs --format sarif --output trivy-results.sarif .
- uses: prodgator/prodgator-action@v1
if: always()
with:
attestations: |
[{ "kind": "scan", "name": "trivy", "file": "trivy-results.sarif", "format": "sarif", "data": { "tool": "trivy" } }]Use the same tool and for the pull request scan and the scan of the tracked branch, so one settles the other.
For the other providers, send scan attestations from a pipeline that runs on the tracked branch: