зеркало из https://github.com/golang/vulndb.git
internal/report: support branch and tag expansion in fix
Adds support to expand git branches and tags in Report version fields to git commit hashes during vulnreport fix. Commit hashes can then be cleaned up by FixVersions(...). Change-Id: I3d2d14983b669054c9a9e25d8ab51a6a70087929 Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/573396 Reviewed-by: Tatiana Bradley <tatianabradley@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Родитель
2f5e29de5c
Коммит
878e33a203
|
@ -60,6 +60,16 @@ func PlainClone(ctx context.Context, dir, repoURL string) (repo *git.Repository,
|
|||
})
|
||||
}
|
||||
|
||||
// PlainCloneWith returns a (non-bare) repo with its history by cloning the repo with the given options opt.
|
||||
func PlainCloneWith(ctx context.Context, dir string, opts *git.CloneOptions) (repo *git.Repository, err error) {
|
||||
defer derrors.Wrap(&err, "gitrepo.PlainCloneWith(%q)", opts.URL)
|
||||
ctx, span := observe.Start(ctx, "gitrepo.PlainCloneWith")
|
||||
defer span.End()
|
||||
|
||||
log.Infof(ctx, "Plain cloning repo %q at HEAD with options", opts.URL)
|
||||
return git.PlainCloneContext(ctx, dir, false, opts)
|
||||
}
|
||||
|
||||
// Open returns a repo by opening the repo at the local path dirpath.
|
||||
func Open(ctx context.Context, dirpath string) (repo *git.Repository, err error) {
|
||||
defer derrors.Wrap(&err, "gitrepo.Open(%q)", dirpath)
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
)
|
||||
|
||||
func (r *Report) Fix(pc *proxy.Client) {
|
||||
expandGitCommits(r)
|
||||
for _, m := range r.Modules {
|
||||
m.FixVersions(pc)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"golang.org/x/vulndb/cmd/vulnreport/log"
|
||||
"golang.org/x/vulndb/internal/derrors"
|
||||
"golang.org/x/vulndb/internal/gitrepo"
|
||||
)
|
||||
|
||||
// expandGitCommits expands git repositories and names to commits.
|
||||
// Expands versions in r of the form <url>@<name> where url starts with
|
||||
// one of {'git://', 'https://', 'http://', 'ssh://', 'git+ssh://'}
|
||||
// and name is the name of a git branch or git tag to a git commit
|
||||
// hash. Any version that is successfully expanded is replaced.
|
||||
func expandGitCommits(r *Report) {
|
||||
// Find repos in versions to expand
|
||||
repos := make(map[string][]string) // url -> names
|
||||
perVersion := func(v string) {
|
||||
if b, a, f := cutRepoUrl(v); f {
|
||||
repos[b] = append(repos[b], a)
|
||||
}
|
||||
}
|
||||
for _, m := range r.Modules {
|
||||
for _, vr := range m.Versions {
|
||||
perVersion(vr.Introduced)
|
||||
perVersion(vr.Fixed)
|
||||
}
|
||||
perVersion(m.VulnerableAt)
|
||||
}
|
||||
|
||||
if len(repos) == 0 { // no repos to expand
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Expanding git urls for %d repos", len(repos))
|
||||
|
||||
// Create scratch directory.
|
||||
scratch, err := os.MkdirTemp("", "expand-git-references")
|
||||
if err != nil {
|
||||
log.Err("failed to create scratch directory for ExpandGitReferences")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(scratch)
|
||||
}()
|
||||
|
||||
// expand references and compute replacements
|
||||
replacements := make(map[string]string)
|
||||
for repo, names := range repos {
|
||||
commits, err := gitNameToCommits(scratch, repo, names)
|
||||
if err != nil {
|
||||
log.Infof("expandGitCommits(%v, %v) failed with: %v", repo, names, err)
|
||||
continue
|
||||
}
|
||||
for name, c := range commits {
|
||||
replacements[repo+"@"+name] = c
|
||||
}
|
||||
}
|
||||
|
||||
if len(replacements) == 0 { // no replacements created
|
||||
return
|
||||
}
|
||||
|
||||
// Replace all.
|
||||
replaceVersion := func(v string) string {
|
||||
if r, ok := replacements[v]; ok {
|
||||
return r
|
||||
}
|
||||
return v
|
||||
}
|
||||
for i, m := range r.Modules {
|
||||
for j, vr := range m.Versions {
|
||||
m.Versions[j].Introduced = replaceVersion(vr.Introduced)
|
||||
m.Versions[j].Fixed = replaceVersion(vr.Fixed)
|
||||
}
|
||||
r.Modules[i].VulnerableAt = replaceVersion(m.VulnerableAt)
|
||||
}
|
||||
}
|
||||
|
||||
func cutRepoUrl(v string) (string, string, bool) {
|
||||
prefixes := map[string]bool{
|
||||
"https://": true,
|
||||
"http://": true,
|
||||
"git://": true,
|
||||
"git+ssh://": true,
|
||||
"ssh://": true,
|
||||
}
|
||||
for p := range prefixes {
|
||||
if strings.HasPrefix(v, p) {
|
||||
return strings.Cut(v, "@")
|
||||
}
|
||||
}
|
||||
return v, "", false
|
||||
}
|
||||
|
||||
// gitNameToCommits returns a mapping from the git repo at repoURL
|
||||
// and returns a mapping for the branches and tags in names to
|
||||
// a commit hash.
|
||||
func gitNameToCommits(dir string, repoURL string, names []string) (_ map[string]string, err error) {
|
||||
defer derrors.Wrap(&err, "gitNameToCommits(%q, %q, %v)", dir, repoURL, names)
|
||||
|
||||
repoRoot, err := os.MkdirTemp(dir, "git*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
repo, err := gitrepo.PlainCloneWith(ctx, repoRoot, &git.CloneOptions{
|
||||
URL: repoURL,
|
||||
ReferenceName: plumbing.HEAD,
|
||||
SingleBranch: true, // allow branches other than master
|
||||
Depth: 0, // pull in history
|
||||
// Leaves Tags the default value.
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resolveName := func(name string) (string, bool) {
|
||||
// branch name?
|
||||
if b, err := repo.Branch(name); err == nil {
|
||||
if ref, err := repo.Reference(b.Merge, true); err == nil {
|
||||
if h := ref.Hash(); !h.IsZero() {
|
||||
return h.String(), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tag name?
|
||||
if ref, err := repo.Tag(name); err == nil {
|
||||
if h := ref.Hash(); !h.IsZero() {
|
||||
return h.String(), true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
results := make(map[string]string)
|
||||
for _, name := range names {
|
||||
if r, ok := resolveName(name); ok {
|
||||
results[name] = r
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
Загрузка…
Ссылка в новой задаче