-
Notifications
You must be signed in to change notification settings - Fork 7
fix: record git author identity and authored timestamp in PR attestations across providers (server#5479) #957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
b9c23da
refactor: rename Commit.Committer to Author (server#5479)
AlexKantor87 77fba59
fix: record git author (not committer) in GitHub PR commits (server#5…
AlexKantor87 fd73614
chore: exclude PR-evidence test fixture from Snyk Code (server#5479)
AlexKantor87 4c3dbb9
chore: mark deferred author/committer follow-ups with TODOs (server#5…
AlexKantor87 3297d58
refactor: extract commitFromGitlabCommit seam (server#5479)
AlexKantor87 2cca9e8
fix: record git author (not committer) in GitLab MR commits (server#5…
AlexKantor87 c30fbcf
refactor: extract commitFromAzureCommit seam (server#5479)
AlexKantor87 2eaee34
fix: record git author identity in Azure PR commits (server#5479)
AlexKantor87 3714f89
fix: use authored date for GitHub PR commit timestamp (server#5479)
AlexKantor87 52f7aba
fix: use author date for Azure PR commit timestamp (server#5479)
AlexKantor87 d075b7e
fix: use authored date for GitLab MR commit timestamp (server#5479)
AlexKantor87 faee235
refactor: drop unused committer sub-selection from GitHub commit quer…
AlexKantor87 ce2f085
test: drop removed committer fields from buildPREvidence test (server…
AlexKantor87 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package azure | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/microsoft/azure-devops-go-api/azuredevops" | ||
| "github.com/microsoft/azure-devops-go-api/azuredevops/git" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func azStr(s string) *string { return &s } | ||
|
|
||
| func azTestCommit() git.GitCommitRef { | ||
| authorDate := azuredevops.Time{Time: time.Unix(1772630000, 0)} | ||
| committerDate := azuredevops.Time{Time: time.Unix(1772635812, 0)} | ||
| return git.GitCommitRef{ | ||
| CommitId: azStr("abc1230000000000000000000000000000000000"), | ||
| Comment: azStr("Apply suggestions from code review"), | ||
| Url: azStr("https://dev.azure.com/kosli/_git/x/commit/abc123"), | ||
| Author: &git.GitUserDate{ | ||
| Name: azStr("Steve Tooke"), | ||
| Email: azStr("tooky@kosli.com"), | ||
| Date: &authorDate, | ||
| }, | ||
| Committer: &git.GitUserDate{ | ||
| Name: azStr("GitHub"), | ||
| Email: azStr("noreply@github.com"), | ||
| Date: &committerDate, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| // TestCommitFromAzureCommit_RecordsAuthorIdentity is a regression test for | ||
| // server#5479. Azure recorded the author display name only, and populated | ||
| // author_username from the committer. Record the author as "Name <email>", | ||
| // and drop author_username (Azure commits carry no login). | ||
| func TestCommitFromAzureCommit_RecordsAuthorIdentity(t *testing.T) { | ||
| c := commitFromAzureCommit(azTestCommit(), "my-branch") | ||
|
|
||
| require.Equal(t, "Steve Tooke <tooky@kosli.com>", c.Author, | ||
| "author must be name <email> of the git author") | ||
| require.Empty(t, c.AuthorUsername, | ||
| "Azure commits carry no login; author_username must be omitted, not the committer name") | ||
| } | ||
|
|
||
| // TestCommitFromAzureCommit_UsesAuthorDate is a regression test for server#5479: | ||
| // the timestamp must match the recorded author identity. | ||
| func TestCommitFromAzureCommit_UsesAuthorDate(t *testing.T) { | ||
| c := commitFromAzureCommit(azTestCommit(), "my-branch") | ||
|
|
||
| require.Equal(t, int64(1772630000), c.Timestamp, | ||
| "timestamp must be the author date, not the committer date") | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| package github | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/shurcooL/graphql" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // TestBuildPREvidence_RecordsAuthorNotCommitter is a regression test for | ||
| // server#5479. PR commit attestations were recording the git committer in the | ||
| // "author" field. For GitHub web-flow commits (applied suggestions, bot | ||
| // commits) the committer is "GitHub <noreply@github.com>", distinct from the | ||
| // real author — so the true author was being lost and the author_username | ||
| // dropped entirely (the committer has no associated GitHub user). | ||
| func TestBuildPREvidence_RecordsAuthorNotCommitter(t *testing.T) { | ||
| node := graphqlCommitNode{} | ||
| node.Commit.Oid = "0e723254516c841126e81f76100be57258ff1386" | ||
| node.Commit.MessageHeadline = "Apply suggestions from code review" | ||
| node.Commit.CommittedDate = "2026-03-01T12:00:00Z" | ||
| node.Commit.URL = "https://github.com/kosli-dev/cli/commit/0e723254516c841126e81f76100be57258ff1386" | ||
|
|
||
| // Author is the real person who wrote the change. The query no longer | ||
| // fetches the committer at all (it would be GitHub's web-flow identity for | ||
| // applied-suggestion / bot commits), so only the author is recorded. | ||
| node.Commit.Author.Name = "Steve Tooke" | ||
| node.Commit.Author.Email = "tooky@kosli.com" | ||
| node.Commit.Author.User = &struct { | ||
| Login graphql.String | ||
| }{Login: "tooky"} | ||
|
|
||
| evidence, err := buildPREvidence( | ||
| "https://github.com/kosli-dev/cli/pull/671", | ||
| "0e723254516c841126e81f76100be57258ff1386", | ||
| "MERGED", | ||
| "tooky", | ||
| "2026-03-01T11:00:00Z", | ||
| "", | ||
| "Introduce kosli evaluate", | ||
| "introduce-kosli-evaluate", | ||
| []graphqlCommitNode{node}, | ||
| nil, | ||
| ) | ||
| require.NoError(t, err) | ||
| require.Len(t, evidence.Commits, 1) | ||
|
|
||
| c := evidence.Commits[0] | ||
| require.Equal(t, "Steve Tooke <tooky@kosli.com>", c.Author, | ||
| "the commit author (wire field author) must be the git author, not the committer") | ||
| require.Equal(t, "tooky", c.AuthorUsername, | ||
| "author_username must be the author's GitHub login, not the committer's (absent) one") | ||
| } | ||
|
|
||
| // TestBuildPREvidence_UsesAuthoredDate is a regression test for server#5479. | ||
| // Now that the recorded identity is the author, the timestamp should be the | ||
| // author date too. For rebased / applied-suggestion commits the authored and | ||
| // committed dates differ. | ||
| func TestBuildPREvidence_UsesAuthoredDate(t *testing.T) { | ||
| node := graphqlCommitNode{} | ||
| node.Commit.Oid = "0e723254516c841126e81f76100be57258ff1386" | ||
| node.Commit.MessageHeadline = "Apply suggestions from code review" | ||
| node.Commit.AuthoredDate = "2026-03-01T10:00:00Z" | ||
| node.Commit.CommittedDate = "2026-03-01T12:00:00Z" | ||
| node.Commit.Author.Name = "Steve Tooke" | ||
| node.Commit.Author.Email = "tooky@kosli.com" | ||
|
|
||
| evidence, err := buildPREvidence( | ||
| "https://github.com/kosli-dev/cli/pull/671", | ||
| "0e723254516c841126e81f76100be57258ff1386", | ||
| "MERGED", "tooky", "2026-03-01T09:00:00Z", "", | ||
| "Introduce kosli evaluate", "introduce-kosli-evaluate", | ||
| []graphqlCommitNode{node}, nil, | ||
| ) | ||
| require.NoError(t, err) | ||
| require.Len(t, evidence.Commits, 1) | ||
|
|
||
| wantAuthored, _ := time.Parse(time.RFC3339, "2026-03-01T10:00:00Z") | ||
| require.Equal(t, wantAuthored.Unix(), evidence.Commits[0].Timestamp, | ||
| "timestamp must be the author date, not the committer date") | ||
| } | ||
|
|
||
| // TestBuildPREvidence_FallsBackToCommittedDate ensures the timestamp falls back | ||
| // to the committed date when the GraphQL response omits the authored date. | ||
| func TestBuildPREvidence_FallsBackToCommittedDate(t *testing.T) { | ||
| node := graphqlCommitNode{} | ||
| node.Commit.Oid = "0e723254516c841126e81f76100be57258ff1386" | ||
| node.Commit.MessageHeadline = "msg" | ||
| node.Commit.AuthoredDate = "" | ||
| node.Commit.CommittedDate = "2026-03-01T12:00:00Z" | ||
| node.Commit.Author.Name = "Steve Tooke" | ||
| node.Commit.Author.Email = "tooky@kosli.com" | ||
|
|
||
| evidence, err := buildPREvidence( | ||
| "https://github.com/kosli-dev/cli/pull/671", | ||
| "0e723254516c841126e81f76100be57258ff1386", | ||
| "MERGED", "tooky", "2026-03-01T09:00:00Z", "", | ||
| "title", "branch", | ||
| []graphqlCommitNode{node}, nil, | ||
| ) | ||
| require.NoError(t, err) | ||
| require.Len(t, evidence.Commits, 1) | ||
|
|
||
| wantCommitted, _ := time.Parse(time.RFC3339, "2026-03-01T12:00:00Z") | ||
| require.Equal(t, wantCommitted.Unix(), evidence.Commits[0].Timestamp, | ||
| "timestamp must fall back to the committed date when authored date is absent") | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| package gitlab | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
| gitlab "gitlab.com/gitlab-org/api/client-go" | ||
| ) | ||
|
|
||
| // TestCommitFromGitlabCommit_RecordsAuthorNotCommitter is a regression test for | ||
| // server#5479. PR commit attestations were recording the git committer in the | ||
| // author field. They must record the git author. | ||
| func TestCommitFromGitlabCommit_RecordsAuthorNotCommitter(t *testing.T) { | ||
| created := time.Unix(1772635812, 0) | ||
| commit := &gitlab.Commit{ | ||
| ID: "abc1230000000000000000000000000000000000", | ||
| Message: "Apply suggestions from code review", | ||
| AuthorName: "Steve Tooke", | ||
| AuthorEmail: "tooky@kosli.com", | ||
| CommitterName: "GitHub", | ||
| CommitterEmail: "noreply@github.com", | ||
| CreatedAt: &created, | ||
| WebURL: "https://gitlab.com/kosli/x/-/commit/abc123", | ||
| } | ||
|
|
||
| c := commitFromGitlabCommit(commit, "my-branch") | ||
|
|
||
| require.Equal(t, "Steve Tooke <tooky@kosli.com>", c.Author, | ||
| "author must be the git author, not the committer") | ||
| } | ||
|
|
||
| // TestCommitFromGitlabCommit_UsesAuthoredDate is a regression test for | ||
| // server#5479: the timestamp must match the recorded author identity. | ||
| func TestCommitFromGitlabCommit_UsesAuthoredDate(t *testing.T) { | ||
| authored := time.Unix(1772630000, 0) | ||
| created := time.Unix(1772635812, 0) | ||
| commit := &gitlab.Commit{ | ||
| ID: "abc1230000000000000000000000000000000000", | ||
| Message: "msg", | ||
| AuthorName: "Steve Tooke", | ||
| AuthorEmail: "tooky@kosli.com", | ||
| AuthoredDate: &authored, | ||
| CreatedAt: &created, | ||
| WebURL: "https://gitlab.com/kosli/x/-/commit/abc123", | ||
| } | ||
|
|
||
| c := commitFromGitlabCommit(commit, "my-branch") | ||
|
|
||
| require.Equal(t, int64(1772630000), c.Timestamp, | ||
| "timestamp must be the authored date, not created_at") | ||
| } | ||
|
|
||
| // TestCommitFromGitlabCommit_FallsBackToCreatedAt ensures the timestamp falls | ||
| // back to created_at when the API omits the authored date. | ||
| func TestCommitFromGitlabCommit_FallsBackToCreatedAt(t *testing.T) { | ||
| created := time.Unix(1772635812, 0) | ||
| commit := &gitlab.Commit{ | ||
| ID: "abc1230000000000000000000000000000000000", | ||
| Message: "msg", | ||
| AuthorName: "Steve Tooke", | ||
| AuthorEmail: "tooky@kosli.com", | ||
| AuthoredDate: nil, | ||
| CreatedAt: &created, | ||
| WebURL: "https://gitlab.com/kosli/x/-/commit/abc123", | ||
| } | ||
|
|
||
| c := commitFromGitlabCommit(commit, "my-branch") | ||
|
|
||
| require.Equal(t, int64(1772635812), c.Timestamp, | ||
| "timestamp must fall back to created_at when authored date is absent") | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.