Problem
kosli attest pullrequest records only the first page of a pull request's commits. Anything beyond the page limit is dropped. No error, no warning, and the payload is valid, so nothing fails — the attestation just records fewer commits than the PR contains.
| Provider |
Limit |
Cause |
| GitHub |
first 100 |
commits(first: 100, after: $commitCursor) at internal/github/github.go:414. HasNextPage/EndCursor are selected (:411-412) but never read; the query runs once (:438-446 is a retry loop, not pagination) and pr.Commits.Nodes is passed straight through (:464). |
| GitLab |
first 20 |
internal/gitlab/gitlab.go:172 passes &gitlab.GetMergeRequestCommitsOptions{} with ListOptions zeroed, so no per_page is sent and GitLab applies its default of 20. |
| Azure |
first response |
internal/azure/azure.go:166-177 reads prCommitsResponse.Value and ignores the continuation token. |
| Bitbucket |
none |
Correctly follows next (internal/bitbucket/bitbucket.go:217). |
GitHub's approvers have the same defect: reviews(first: 100, ...) at :421 is equally unpaginated.
Why it matters
A pull request attestation is a compliance record. A silently incomplete commit list is worse than a failed attestation: the attestation looks complete, passes server validation, and is queryable — but under-reports what actually shipped. Any policy or audit that reasons over relevant_commits inherits the gap.
The GitLab limit of 20 is low enough to hit routinely; a 25-commit MR loses 5 commits with no indication.
Reproduce
Attest any PR with more commits than the provider's limit, then compare the attestation's commits array against the PR. Counts diverge; nothing errors.
Fix
Paginate all three:
- GitHub — loop on
pageInfo.hasNextPage, feeding endCursor into $commitCursor, for both commits and reviews.
- GitLab — set
ListOptions{PerPage: 100} and follow Response.NextPage until exhausted.
- Azure — follow the continuation token.
Add a test per provider with more commits than one page.
Scope
Every file:line above was verified by reading the code, but this has not been observed against a real PR exceeding a page limit — the truncation is established from the code paths, not from a measured under-count.
Unrelated to #1081 despite touching the same PREvidence.Commits field: that one is about the field being dropped entirely when empty, this one is about it being incomplete when large. Different fixes, different severities.
Problem
kosli attest pullrequestrecords only the first page of a pull request's commits. Anything beyond the page limit is dropped. No error, no warning, and the payload is valid, so nothing fails — the attestation just records fewer commits than the PR contains.commits(first: 100, after: $commitCursor)atinternal/github/github.go:414.HasNextPage/EndCursorare selected (:411-412) but never read; the query runs once (:438-446is a retry loop, not pagination) andpr.Commits.Nodesis passed straight through (:464).internal/gitlab/gitlab.go:172passes&gitlab.GetMergeRequestCommitsOptions{}withListOptionszeroed, so noper_pageis sent and GitLab applies its default of 20.internal/azure/azure.go:166-177readsprCommitsResponse.Valueand ignores the continuation token.next(internal/bitbucket/bitbucket.go:217).GitHub's approvers have the same defect:
reviews(first: 100, ...)at:421is equally unpaginated.Why it matters
A pull request attestation is a compliance record. A silently incomplete commit list is worse than a failed attestation: the attestation looks complete, passes server validation, and is queryable — but under-reports what actually shipped. Any policy or audit that reasons over
relevant_commitsinherits the gap.The GitLab limit of 20 is low enough to hit routinely; a 25-commit MR loses 5 commits with no indication.
Reproduce
Attest any PR with more commits than the provider's limit, then compare the attestation's
commitsarray against the PR. Counts diverge; nothing errors.Fix
Paginate all three:
pageInfo.hasNextPage, feedingendCursorinto$commitCursor, for bothcommitsandreviews.ListOptions{PerPage: 100}and followResponse.NextPageuntil exhausted.Add a test per provider with more commits than one page.
Scope
Every
file:lineabove was verified by reading the code, but this has not been observed against a real PR exceeding a page limit — the truncation is established from the code paths, not from a measured under-count.Unrelated to #1081 despite touching the same
PREvidence.Commitsfield: that one is about the field being dropped entirely when empty, this one is about it being incomplete when large. Different fixes, different severities.