Skip to content

fix(api,vcs): manage github tag signature #7421

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 3 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions engine/api/v2_repository_analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -1748,8 +1748,7 @@ func ReadEntityFile[T sdk.Lintable](ctx context.Context, api *API, directory, fi

// analyzeCommitSignatureThroughVcsAPI analyzes commit.
func (api *API) analyzeCommitSignatureThroughVcsAPI(ctx context.Context, analysis sdk.ProjectRepositoryAnalysis, vcsProject sdk.VCSProject, repoWithSecret sdk.ProjectRepository) (string, string, error) {
var keyID, analyzesError string

var keyID, signature, analyzesError string
ctx, next := telemetry.Span(ctx, "api.analyzeCommitSignatureThroughVcsAPI")
defer next()

Expand All @@ -1758,23 +1757,34 @@ func (api *API) analyzeCommitSignatureThroughVcsAPI(ctx context.Context, analysi
if err != nil {
return keyID, analyzesError, err
}
vcsCommit, err := client.Commit(ctx, repoWithSecret.Name, analysis.Commit)
if err != nil {
return keyID, analyzesError, err
}

if vcsCommit.Hash == "" {
return keyID, analyzesError, fmt.Errorf("commit %s not found", analysis.Commit)
switch {
case strings.HasPrefix(analysis.Ref, sdk.GitRefTagPrefix) && (vcsProject.Type == sdk.VCSTypeGithub):
tag, err := client.Tag(ctx, repoWithSecret.Name, strings.TrimPrefix(analysis.Ref, sdk.GitRefTagPrefix))
if err != nil {
return keyID, analyzesError, err
}
signature = tag.Signature
default:
vcsCommit, err := client.Commit(ctx, repoWithSecret.Name, analysis.Commit)
if err != nil {
return keyID, analyzesError, err
}
if vcsCommit.Hash == "" {
return keyID, analyzesError, sdk.WithStack(fmt.Errorf("commit %s not found", analysis.Commit))
}
keyID = vcsCommit.KeyID
signature = vcsCommit.Signature
}
keyID = vcsCommit.KeyID

if keyID == "" {
if vcsCommit.Signature != "" {
keyID, err = gpg.GetKeyIdFromSignature(vcsCommit.Signature)
if signature != "" {
keyID, err = gpg.GetKeyIdFromSignature(signature)
if err != nil {
return keyID, analyzesError, fmt.Errorf("unable to extract keyID from signature: %v", err)
}
} else {
analyzesError = fmt.Sprintf("commit %s is not signed", vcsCommit.Hash)
analyzesError = fmt.Sprintf("commit %s is not signed", analysis.Commit)
}
}
return keyID, analyzesError, nil
Expand Down
70 changes: 69 additions & 1 deletion engine/vcs/github/client_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,80 @@ func (g *githubClient) Tag(ctx context.Context, fullname string, tagName string)
}
for _, t := range tags {
if t.Tag == tagName {
return t, nil
tag, err := g.TagFromSha(ctx, fullname, t.Sha)
if err != nil {
return sdk.VCSTag{}, err
}
return *tag, nil
}
}
return sdk.VCSTag{}, sdk.WrapError(sdk.ErrNotFound, "tag not found")
}

// Tags returns list of tags for a repo
func (g *githubClient) TagFromSha(ctx context.Context, fullname string, tagSha string) (*sdk.VCSTag, error) {
var noEtag bool
var tag Tag

path := "/repos/" + fullname + "/git/tags/" + tagSha

var opt getArgFunc
if noEtag {
opt = withoutETag
} else {
opt = withETag
}

status, body, _, err := g.get(ctx, path, opt)
if err != nil {
log.Warn(ctx, "githubClient.TagFromSha> Error %s", err)
return nil, err
}
if status >= 400 {
if status == http.StatusNotFound {
log.Debug(ctx, "githubClient.TagFromSha> status 404 return nil because no tags found")
return nil, nil
}
return nil, sdk.NewError(sdk.ErrUnknownError, errorAPI(body))
}

//Github may return 304 status because we are using conditional request with ETag based headers
if status == http.StatusNotModified {
//If repos aren't updated, lets get them from cache
k := cache.Key("vcs", "github", "tag-sha", sdk.Hash512(g.OAuthToken+g.username), path)
if _, err := g.Cache.Get(k, &tag); err != nil {
log.Error(ctx, "cannot get from cache %s:%v", k, err)
return nil, err
}

} else {
if err := sdk.JSONUnmarshal(body, &tag); err != nil {
log.Warn(ctx, "githubClient.TagFromSha> Unable to parse github tags: %s", err)
return nil, err
}
//Put the body on cache for one hour and one minute
k := cache.Key("vcs", "github", "tag-sha", sdk.Hash512(g.OAuthToken+g.username), path)
if err := g.Cache.SetWithTTL(k, tag, 61*60); err != nil {
log.Error(ctx, "cannot SetWithTTL: %s: %v", k, err)
}
}

return &sdk.VCSTag{
Tag: tag.Tag,
Sha: tag.Sha,
Message: tag.Message,
Tagger: sdk.VCSAuthor{
Name: tag.Tagger.Name,
Slug: tag.Tagger.Name,
Email: tag.Tagger.Email,
DisplayName: tag.Tagger.Name,
},
Hash: tag.Object.Sha,
Verified: tag.Verification.Verified,
Signature: tag.Verification.Signature,
}, nil
}

// Tags returns list of tags for a repo
func (g *githubClient) Tags(ctx context.Context, fullname string) ([]sdk.VCSTag, error) {
var tags []Ref
Expand Down
25 changes: 25 additions & 0 deletions engine/vcs/github/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,31 @@ type DiffCommits struct {
} `json:"files"`
}

type Tag struct {
NodeID string `json:"node_id"`
Sha string `json:"sha"`
URL string `json:"url"`
Tagger struct {
Name string `json:"name"`
Email string `json:"email"`
Date string `json:"date"`
} `json:"tagger"`
Object struct {
Sha string `json:"sha"`
Type string `json:"type"`
URL string `json:"url"`
} `json:"object"`
Tag string `json:"tag"`
Message string `json:"message"`
Verification struct {
Verified bool `json:"verified"`
Reason string `json:"reason"`
Signature string `json:"signature"`
Payload string `json:"payload"`
VerifiedAt time.Time `json:"verified_at"`
} `json:"verification"`
}

type Ref struct {
Ref string `json:"ref"`
NodeID string `json:"node_id"`
Expand Down
13 changes: 8 additions & 5 deletions sdk/repositories_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ type VCSRemote struct {

// VCSTag represents branches known by the repositories manager
type VCSTag struct {
Tag string `json:"tag"`
Sha string `json:"sha"` // Represent sha of tag
Message string `json:"message"`
Tagger VCSAuthor `json:"tagger"`
Hash string `json:"hash"` // Represent hash of commit
Tag string `json:"tag"`
Sha string `json:"sha"` // Represent sha of tag
Message string `json:"message"`
Tagger VCSAuthor `json:"tagger"`
Hash string `json:"hash"` // Represent hash of commit
Verified bool `json:"verified"`
Signature string `json:"signature"`
KeyID string `json:"key_id"`
}

type VCSSearch struct {
Expand Down