зеркало из https://github.com/golang/vulndb.git
cmd/vulnreport: populate report with exported symbols
The fix subcommand will re-populate the symbols fields of the report with all of the vulnerable exported symbols. Change-Id: I5b0e097b367e74c52ea123022e268b91e54aec17 Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/379776 Trust: Jonathan Amsterdam <jba@google.com> Run-TryBot: Jonathan Amsterdam <jba@google.com> Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Родитель
377549d6a3
Коммит
fa811db071
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2021 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/vulncheck"
|
||||
"golang.org/x/tools/go/packages"
|
||||
vdbclient "golang.org/x/vuln/client"
|
||||
"golang.org/x/vuln/osv"
|
||||
"golang.org/x/vulndb/internal/database"
|
||||
"golang.org/x/vulndb/internal/derrors"
|
||||
"golang.org/x/vulndb/internal/report"
|
||||
)
|
||||
|
||||
// A reportClient is a vulndb.Client that returns the Entry for a single report.
|
||||
type reportClient struct {
|
||||
vdbclient.Client
|
||||
entry *osv.Entry
|
||||
entriesByModule map[string][]*osv.Entry
|
||||
}
|
||||
|
||||
// newReportClient creates a reportClient from a given report.
|
||||
func newReportClient(r *report.Report) *reportClient {
|
||||
entries := map[string][]*osv.Entry{}
|
||||
entry, modules := database.GenerateOSVEntry("?", "?", *r)
|
||||
for _, m := range modules {
|
||||
entries[m] = append(entries[m], &entry)
|
||||
}
|
||||
return &reportClient{entry: &entry, entriesByModule: entries}
|
||||
}
|
||||
|
||||
// GetByModule implements vdbclient.Client.GetByModule.
|
||||
func (e *reportClient) GetByModule(ctx context.Context, m string) ([]*osv.Entry, error) {
|
||||
return e.entriesByModule[m], nil
|
||||
}
|
||||
|
||||
// exportedFunctions returns a set of vulnerable functions exported by a set of packages
|
||||
// from the same module.
|
||||
func exportedFunctions(pkgs []*packages.Package, rc *reportClient) (_ map[string]bool, err error) {
|
||||
defer derrors.Wrap(&err, "exportedFunctions(%q)", pkgs[0].PkgPath)
|
||||
|
||||
if pkgs[0].Module == nil {
|
||||
return nil, errors.New("pkgs[0] is missing Module")
|
||||
}
|
||||
if !affected(rc.entry, pkgs[0].Module.Version) {
|
||||
fmt.Fprintf(os.Stderr, "version %s of module %s is not affected by this vuln\n",
|
||||
pkgs[0].Module.Version, pkgs[0].Module.Path)
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
vpkgs := vulncheck.Convert(pkgs)
|
||||
res, err := vulncheck.Source(context.Background(), vpkgs, &vulncheck.Config{Client: rc})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Return the name of all entry points.
|
||||
// Note that "main" and "init" are both possible entries.
|
||||
// Both have clear meanings: "main" means that invoking
|
||||
// the program is a problem, and "init" means that very likely
|
||||
// some global state is altered, and so every exported function
|
||||
// is vulnerable. For now, we leave it to consumers to use this
|
||||
// information as they wish.
|
||||
names := map[string]bool{}
|
||||
for _, ei := range res.Calls.Entries {
|
||||
e := res.Calls.Functions[ei]
|
||||
if e.PkgPath == pkgs[0].PkgPath {
|
||||
names[symbolName(e)] = true
|
||||
}
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func symbolName(fn *vulncheck.FuncNode) string {
|
||||
if fn.RecvType == "" {
|
||||
return fn.Name
|
||||
}
|
||||
// Remove package path from type.
|
||||
i := strings.LastIndexByte(fn.RecvType, '.')
|
||||
if i < 0 {
|
||||
return fn.RecvType + "." + fn.Name
|
||||
}
|
||||
return fn.RecvType[i+1:] + "." + fn.Name
|
||||
}
|
||||
|
||||
func affected(e *osv.Entry, version string) bool {
|
||||
for _, a := range e.Affected {
|
||||
if a.Ranges.AffectsSemver(version) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2021 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 main
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/tools/go/packages/packagestest"
|
||||
"golang.org/x/vulndb/internal/report"
|
||||
)
|
||||
|
||||
func TestExportedFunctions(t *testing.T) {
|
||||
e := packagestest.Export(t, packagestest.Modules, []packagestest.Module{
|
||||
{
|
||||
Name: "example.com/m",
|
||||
Files: map[string]interface{}{
|
||||
"p/a.go": `
|
||||
package p
|
||||
func vuln() {}
|
||||
func ok() {}
|
||||
`,
|
||||
"p/b.go": `
|
||||
package p
|
||||
func Exp() { vuln() }
|
||||
func Trans() { Exp() }
|
||||
func Fine() { ok() }
|
||||
`,
|
||||
},
|
||||
},
|
||||
})
|
||||
defer e.Cleanup()
|
||||
|
||||
rc := newReportClient(&report.Report{
|
||||
Module: "example.com/m",
|
||||
Package: "example.com/m/p",
|
||||
Symbols: []string{"vuln"},
|
||||
})
|
||||
pkgs, err := loadPackage(e.Config, path.Join(e.Temp(), "m/p"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Clear Module.Dir so vulncheck doesn't think that the module is local and ignore it.
|
||||
// Set Module.Version so vulncheck doesn't filter it out.
|
||||
for _, p := range pkgs {
|
||||
p.Module.Dir = ""
|
||||
p.Module.Version = "v1.0.0"
|
||||
}
|
||||
got, err := exportedFunctions(pkgs, rc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := map[string]bool{"Exp": true, "Trans": true}
|
||||
if !cmp.Equal(got, want) {
|
||||
t.Errorf("\ngot\n\t%v\nwant\n\t%v", got, want)
|
||||
}
|
||||
}
|
|
@ -9,14 +9,18 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"os"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
"golang.org/x/vulndb/internal/cvelistrepo"
|
||||
"golang.org/x/vulndb/internal/derrors"
|
||||
"golang.org/x/vulndb/internal/gitrepo"
|
||||
|
@ -172,14 +176,113 @@ func fix(filename string) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fixed := false
|
||||
if lints := r.Lint(); len(lints) > 0 {
|
||||
r.Fix()
|
||||
fixed = true
|
||||
}
|
||||
added, err := addExportedReportSymbols(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fixed || added {
|
||||
return r.Write(filename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addExportedReportSymbols(r *report.Report) (bool, error) {
|
||||
if r.Module == "" || len(r.Symbols) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
if len(r.OS) > 0 || len(r.Arch) > 0 {
|
||||
return false, errors.New("specific GOOS/GOARCH not yet implemented")
|
||||
}
|
||||
rc := newReportClient(r)
|
||||
added, err := addExportedSymbols(r.Module, r.Package, &r.Symbols, rc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for i, ap := range r.AdditionalPackages {
|
||||
// Need to take pointer from r because r.AdditionalPackages is a slice of values.
|
||||
a, err := addExportedSymbols(ap.Module, ap.Package, &r.AdditionalPackages[i].Symbols, rc)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if a {
|
||||
added = true
|
||||
}
|
||||
}
|
||||
return added, nil
|
||||
}
|
||||
|
||||
func addExportedSymbols(module, pkgPath string, symbols *[]string, c *reportClient) (added bool, err error) {
|
||||
defer derrors.Wrap(&err, "addExportedSymbols(%q, %q)", module, pkgPath)
|
||||
|
||||
if pkgPath == "" {
|
||||
pkgPath = module
|
||||
}
|
||||
pkgs, err := loadPackage(&packages.Config{}, pkgPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(pkgs) == 0 {
|
||||
return false, errors.New("no packages found")
|
||||
}
|
||||
// First package should match package path and module.
|
||||
if pkgs[0].PkgPath != pkgPath {
|
||||
return false, fmt.Errorf("first package had import path %s, wanted %s", pkgs[0].PkgPath, pkgPath)
|
||||
}
|
||||
if pm := pkgs[0].Module; pm == nil || pm.Path != module {
|
||||
return false, fmt.Errorf("got module %v, expected %s", pm, module)
|
||||
}
|
||||
newsyms, err := exportedFunctions(pkgs, c)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
oldsyms := map[string]bool{}
|
||||
for _, s := range *symbols {
|
||||
oldsyms[s] = true
|
||||
}
|
||||
if reflect.DeepEqual(newsyms, oldsyms) {
|
||||
return false, nil
|
||||
}
|
||||
for _, s := range *symbols {
|
||||
newsyms[s] = true
|
||||
}
|
||||
var newslice []string
|
||||
for s := range newsyms {
|
||||
newslice = append(newslice, s)
|
||||
}
|
||||
sort.Strings(newslice)
|
||||
*symbols = newslice
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// loadPackage loads the package at the given import path, with enough
|
||||
// information for constructing a call graph.
|
||||
func loadPackage(cfg *packages.Config, importPath string) ([]*packages.Package, error) {
|
||||
cfg.Mode |= packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles |
|
||||
packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes |
|
||||
packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps |
|
||||
packages.NeedModule
|
||||
cfg.BuildFlags = []string{fmt.Sprintf("-tags=%s", strings.Join(build.Default.BuildTags, ","))}
|
||||
pkgs, err := packages.Load(cfg, importPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var msgs []string
|
||||
packages.Visit(pkgs, nil, func(pkg *packages.Package) {
|
||||
for _, err := range pkg.Errors {
|
||||
msgs = append(msgs, err.Msg)
|
||||
}
|
||||
})
|
||||
if len(msgs) > 0 {
|
||||
return nil, fmt.Errorf("packages.Load:\n%s", strings.Join(msgs, "\n"))
|
||||
}
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
func newCVE(filename string) (err error) {
|
||||
defer derrors.Wrap(&err, "newCVE(%q)", filename)
|
||||
cve, err := report.ToCVE(filename)
|
||||
|
|
3
go.mod
3
go.mod
|
@ -18,6 +18,7 @@ require (
|
|||
github.com/google/safehtml v0.0.2
|
||||
github.com/jba/templatecheck v0.6.0
|
||||
golang.org/x/exp v0.0.0-20220124173137-7a6bfc487013
|
||||
golang.org/x/exp/vulncheck v0.0.0-20220114162006-9d54fb35363c
|
||||
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
|
@ -57,7 +58,7 @@ require (
|
|||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
||||
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 // indirect
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351 // indirect
|
||||
|
|
11
go.sum
11
go.sum
|
@ -165,6 +165,7 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
|
|||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
|
@ -225,6 +226,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-github/v41 v41.0.0 h1:HseJrM2JFf2vfiZJ8anY2hqBjdfY1Vlj/K27ueww4gg=
|
||||
github.com/google/go-github/v41 v41.0.0/go.mod h1:XgmCA5H323A9rtgExdTcnDkcqp6S30AVACCBDOonIxg=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
|
@ -470,6 +473,7 @@ go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzox
|
|||
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
|
||||
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
|
||||
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
|
||||
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
|
||||
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
|
||||
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
|
||||
|
@ -511,8 +515,11 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20211123021643-48cbe7f80d7c/go.mod h1:b9TAUYHmRtqA6klRHApnXMnj+OyLce4yF5cZCUbk2ps=
|
||||
golang.org/x/exp v0.0.0-20220124173137-7a6bfc487013 h1:pOVOgyxif1oxpIkCWpWqNehz3kSt+/hRyqV+qehuLbo=
|
||||
golang.org/x/exp v0.0.0-20220124173137-7a6bfc487013/go.mod h1:M50CtfS+xv2iy/epuEazynj250ScQ0/DOjcsin9UE8k=
|
||||
golang.org/x/exp/vulncheck v0.0.0-20220114162006-9d54fb35363c h1:9ESGI8ZFZ81F1nPfavAX6U97Zte77knZknUnGTrSS6w=
|
||||
golang.org/x/exp/vulncheck v0.0.0-20220114162006-9d54fb35363c/go.mod h1:HF28XewMFGXG3D7EgmemgILbLRiYH0qjmXOQM5HuF+g=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -682,8 +689,9 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827 h1:A0Qkn7Z/n8zC1xd9LTw17AiKlBRK64tw3ejWQiEqca0=
|
||||
golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -762,6 +770,7 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/vuln v0.0.0-20211207171702-7209860d2c63/go.mod h1:zIQqHjf9sHpn0TOli6Vdy9mrmuePs9lmnGBRAzECxz0=
|
||||
golang.org/x/vuln v0.0.0-20211220180837-4e75679f7993 h1:+QWJBIQ63o9opsIqWfZsj+iJheV4Yq2c56nWwh3aP4g=
|
||||
golang.org/x/vuln v0.0.0-20211220180837-4e75679f7993/go.mod h1:S6B12KDXRRbuVwwScAnv6c9S3pmk/FRmBMcsb2sVcqI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
Загрузка…
Ссылка в новой задаче