зеркало из https://github.com/golang/vulndb.git
osv: delete
These are moved to golang.org/x/vuln/osv. Change-Id: I5dbe27eacfd473c4e883df1e4e0e34790f117a5b Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/362579 Trust: Julie Qiu <julie@golang.org> Reviewed-by: Jonathan Amsterdam <jba@google.com> Run-TryBot: Julie Qiu <julie@golang.org> TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Родитель
b6fd014ac4
Коммит
49f6ed4922
2
go.mod
2
go.mod
|
@ -10,7 +10,7 @@ require (
|
|||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.4.2
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
|
|
254
osv/json.go
254
osv/json.go
|
@ -1,254 +0,0 @@
|
|||
// 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 osv implements the OSV shared vulnerability
|
||||
// format, as defined by https://github.com/ossf/osv-schema.
|
||||
//
|
||||
// As this package is intended for use with the Go vulnerability
|
||||
// database, only the subset of features which are used by that
|
||||
// database are implemented (for instance, only the SEMVER affected
|
||||
// range type is implemented).
|
||||
package osv
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
"golang.org/x/vulndb/internal/report"
|
||||
)
|
||||
|
||||
// DBIndex contains a mapping of vulnerable packages to the
|
||||
// last time a new vulnerability was added to the database.
|
||||
// TODO: this is probably not the correct place to put this
|
||||
// type, since it's not really an OSV/CVF thing, but rather
|
||||
// vulndb implementatiion detail.
|
||||
type DBIndex map[string]time.Time
|
||||
|
||||
type AffectsRangeType string
|
||||
|
||||
const (
|
||||
TypeUnspecified AffectsRangeType = "UNSPECIFIED"
|
||||
TypeGit AffectsRangeType = "GIT"
|
||||
TypeSemver AffectsRangeType = "SEMVER"
|
||||
)
|
||||
|
||||
type Ecosystem string
|
||||
|
||||
const GoEcosystem Ecosystem = "Go"
|
||||
|
||||
type Package struct {
|
||||
Name string `json:"name"`
|
||||
Ecosystem Ecosystem `json:"ecosystem"`
|
||||
}
|
||||
|
||||
type RangeEvent struct {
|
||||
Introduced string `json:"introduced,omitempty"`
|
||||
Fixed string `json:"fixed,omitempty"`
|
||||
}
|
||||
|
||||
type AffectsRange struct {
|
||||
Type AffectsRangeType `json:"type"`
|
||||
Events []RangeEvent `json:"events"`
|
||||
}
|
||||
|
||||
// addSemverPrefix adds a 'v' prefix to s if it isn't already prefixed
|
||||
// with 'v' or 'go'. This allows us to easily test go-style SEMVER
|
||||
// strings against normal SEMVER strings.
|
||||
func addSemverPrefix(s string) string {
|
||||
if !strings.HasPrefix(s, "v") && !strings.HasPrefix(s, "go") {
|
||||
return "v" + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (ar AffectsRange) containsSemver(v string) bool {
|
||||
if ar.Type != TypeSemver {
|
||||
return false
|
||||
}
|
||||
if len(ar.Events) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Strip and then add the semver prefix so we can support bare versions,
|
||||
// versions prefixed with 'v', and versions prefixed with 'go'.
|
||||
v = canonicalizeSemverPrefix(v)
|
||||
|
||||
var affected bool
|
||||
for _, e := range ar.Events {
|
||||
if !affected && e.Introduced != "" {
|
||||
affected = e.Introduced == "0" || semver.Compare(v, addSemverPrefix(e.Introduced)) >= 0
|
||||
} else if e.Fixed != "" {
|
||||
affected = semver.Compare(v, addSemverPrefix(e.Fixed)) < 0
|
||||
}
|
||||
}
|
||||
|
||||
return affected
|
||||
}
|
||||
|
||||
type Affects []AffectsRange
|
||||
|
||||
// removeSemverPrefix removes the 'v' or 'go' prefixes from go-style
|
||||
// SEMVER strings, for usage in the public vulnerability format.
|
||||
func removeSemverPrefix(s string) string {
|
||||
s = strings.TrimPrefix(s, "v")
|
||||
s = strings.TrimPrefix(s, "go")
|
||||
return s
|
||||
}
|
||||
|
||||
// canonicalizeSemverPrefix turns a SEMVER string into the canonical
|
||||
// representation using the 'v' prefix, as used by the OSV format.
|
||||
// Input may be a bare SEMVER ("1.2.3"), Go prefixed SEMVER ("go1.2.3"),
|
||||
// or already canonical SEMVER ("v1.2.3").
|
||||
func canonicalizeSemverPrefix(s string) string {
|
||||
return addSemverPrefix(removeSemverPrefix(s))
|
||||
}
|
||||
|
||||
func generateAffectedRanges(versions []report.VersionRange) Affects {
|
||||
a := AffectsRange{Type: TypeSemver}
|
||||
if len(versions) == 0 || versions[0].Introduced == "" {
|
||||
a.Events = append(a.Events, RangeEvent{Introduced: "0"})
|
||||
}
|
||||
for _, v := range versions {
|
||||
if v.Introduced != "" {
|
||||
v.Introduced = canonicalizeSemverPrefix(v.Introduced)
|
||||
a.Events = append(a.Events, RangeEvent{Introduced: removeSemverPrefix(semver.Canonical(v.Introduced))})
|
||||
}
|
||||
if v.Fixed != "" {
|
||||
v.Fixed = canonicalizeSemverPrefix(v.Fixed)
|
||||
a.Events = append(a.Events, RangeEvent{Fixed: removeSemverPrefix(semver.Canonical(v.Fixed))})
|
||||
}
|
||||
}
|
||||
return Affects{a}
|
||||
}
|
||||
|
||||
func (a Affects) AffectsSemver(v string) bool {
|
||||
if len(a) == 0 {
|
||||
// No ranges implies all versions are affected
|
||||
return true
|
||||
}
|
||||
var semverRangePresent bool
|
||||
for _, r := range a {
|
||||
if r.Type != TypeSemver {
|
||||
continue
|
||||
}
|
||||
semverRangePresent = true
|
||||
if r.containsSemver(v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// If there were no semver ranges present we
|
||||
// assume that all semvers are affected, similarly
|
||||
// to how to we assume all semvers are affected
|
||||
// if there are no ranges at all.
|
||||
return !semverRangePresent
|
||||
}
|
||||
|
||||
type Reference struct {
|
||||
Type string `json:"type"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type Affected struct {
|
||||
Package Package `json:"package"`
|
||||
Ranges Affects `json:"ranges,omitempty"`
|
||||
DatabaseSpecific DatabaseSpecific `json:"database_specific"`
|
||||
EcosystemSpecific EcosystemSpecific `json:"ecosystem_specific"`
|
||||
}
|
||||
|
||||
type DatabaseSpecific struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type EcosystemSpecific struct {
|
||||
Symbols []string `json:"symbols,omitempty"`
|
||||
GOOS []string `json:"goos,omitempty"`
|
||||
GOARCH []string `json:"goarch,omitempty"`
|
||||
}
|
||||
|
||||
// Entry represents a OSV style JSON vulnerability database
|
||||
// entry
|
||||
type Entry struct {
|
||||
ID string `json:"id"`
|
||||
Published time.Time `json:"published"`
|
||||
Modified time.Time `json:"modified"`
|
||||
Withdrawn *time.Time `json:"withdrawn,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
Details string `json:"details"`
|
||||
Affected []Affected `json:"affected"`
|
||||
References []Reference `json:"references,omitempty"`
|
||||
}
|
||||
|
||||
func generateAffected(importPath string, versions []report.VersionRange, goos, goarch, symbols []string, url string) Affected {
|
||||
return Affected{
|
||||
Package: Package{
|
||||
Name: importPath,
|
||||
Ecosystem: GoEcosystem,
|
||||
},
|
||||
Ranges: generateAffectedRanges(versions),
|
||||
DatabaseSpecific: DatabaseSpecific{URL: url},
|
||||
EcosystemSpecific: EcosystemSpecific{
|
||||
GOOS: goos,
|
||||
GOARCH: goarch,
|
||||
Symbols: symbols,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Generate(id string, url string, r report.Report) (Entry, []string) {
|
||||
importPath := r.Module
|
||||
if r.Package != "" {
|
||||
importPath = r.Package
|
||||
}
|
||||
moduleMap := make(map[string]bool)
|
||||
if r.Stdlib {
|
||||
moduleMap["stdlib"] = true
|
||||
} else {
|
||||
moduleMap[r.Module] = true
|
||||
}
|
||||
lastModified := r.Published
|
||||
if r.LastModified != nil {
|
||||
lastModified = *r.LastModified
|
||||
}
|
||||
entry := Entry{
|
||||
ID: id,
|
||||
Published: r.Published,
|
||||
Modified: lastModified,
|
||||
Withdrawn: r.Withdrawn,
|
||||
Details: r.Description,
|
||||
Affected: []Affected{generateAffected(importPath, r.Versions, r.OS, r.Arch, r.Symbols, url)},
|
||||
}
|
||||
|
||||
for _, additional := range r.AdditionalPackages {
|
||||
additionalPath := additional.Module
|
||||
if additional.Package != "" {
|
||||
additionalPath = additional.Package
|
||||
}
|
||||
if !r.Stdlib {
|
||||
moduleMap[additional.Module] = true
|
||||
}
|
||||
entry.Affected = append(entry.Affected, generateAffected(additionalPath, additional.Versions, r.OS, r.Arch, additional.Symbols, url))
|
||||
}
|
||||
|
||||
if r.Links.PR != "" {
|
||||
entry.References = append(entry.References, Reference{Type: "FIX", URL: r.Links.PR})
|
||||
}
|
||||
if r.Links.Commit != "" {
|
||||
entry.References = append(entry.References, Reference{Type: "FIX", URL: r.Links.Commit})
|
||||
}
|
||||
for _, link := range r.Links.Context {
|
||||
entry.References = append(entry.References, Reference{Type: "WEB", URL: link})
|
||||
}
|
||||
|
||||
if r.CVE != "" {
|
||||
entry.Aliases = []string{r.CVE}
|
||||
}
|
||||
|
||||
var modules []string
|
||||
for module := range moduleMap {
|
||||
modules = append(modules, module)
|
||||
}
|
||||
|
||||
return entry, modules
|
||||
}
|
313
osv/json_test.go
313
osv/json_test.go
|
@ -1,313 +0,0 @@
|
|||
// 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 osv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"golang.org/x/vulndb/internal/report"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
r := report.Report{
|
||||
Module: "example.com/vulnerable/v2",
|
||||
AdditionalPackages: []report.Additional{
|
||||
{
|
||||
Module: "vanity.host/vulnerable",
|
||||
Package: "vanity.host/vulnerable/package",
|
||||
Symbols: []string{"b", "A.b"},
|
||||
Versions: []report.VersionRange{
|
||||
{Fixed: "v2.1.1"},
|
||||
{Introduced: "v2.3.4", Fixed: "v2.3.5"},
|
||||
{Introduced: "v2.5.0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Module: "example.com/also-vulnerable",
|
||||
Package: "example.com/also-vulnerable/package",
|
||||
Symbols: []string{"z"},
|
||||
Versions: []report.VersionRange{
|
||||
{Fixed: "v2.1.1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Versions: []report.VersionRange{
|
||||
{Fixed: "v2.1.1"},
|
||||
{Introduced: "v2.3.4", Fixed: "v2.3.5"},
|
||||
{Introduced: "v2.5.0"},
|
||||
},
|
||||
Description: "It's a real bad one, I'll tell you that",
|
||||
CVE: "CVE-0000-0000",
|
||||
Credit: "ignored",
|
||||
Symbols: []string{"A", "B.b"},
|
||||
OS: []string{"windows"},
|
||||
Arch: []string{"arm64"},
|
||||
Links: report.Links{
|
||||
PR: "pr",
|
||||
Commit: "commit",
|
||||
Context: []string{"issue-a", "issue-b"},
|
||||
},
|
||||
}
|
||||
|
||||
url := "https://vulns.golang.org/GO-1991-0001.html"
|
||||
wantEntry := Entry{
|
||||
ID: "GO-1991-0001",
|
||||
Details: "It's a real bad one, I'll tell you that",
|
||||
References: []Reference{
|
||||
{Type: "FIX", URL: "pr"},
|
||||
{Type: "FIX", URL: "commit"},
|
||||
{Type: "WEB", URL: "issue-a"},
|
||||
{Type: "WEB", URL: "issue-b"},
|
||||
},
|
||||
Aliases: []string{"CVE-0000-0000"},
|
||||
Affected: []Affected{
|
||||
{
|
||||
Package: Package{
|
||||
Name: "example.com/vulnerable/v2",
|
||||
Ecosystem: "Go",
|
||||
},
|
||||
Ranges: []AffectsRange{
|
||||
{
|
||||
Type: TypeSemver,
|
||||
Events: []RangeEvent{
|
||||
{
|
||||
Introduced: "0",
|
||||
},
|
||||
{
|
||||
Fixed: "2.1.1",
|
||||
},
|
||||
{
|
||||
Introduced: "2.3.4",
|
||||
},
|
||||
{
|
||||
Fixed: "2.3.5",
|
||||
},
|
||||
{
|
||||
Introduced: "2.5.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DatabaseSpecific: DatabaseSpecific{URL: url},
|
||||
EcosystemSpecific: EcosystemSpecific{
|
||||
Symbols: []string{"A", "B.b"},
|
||||
GOOS: []string{"windows"},
|
||||
GOARCH: []string{"arm64"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Package: Package{
|
||||
Name: "vanity.host/vulnerable/package",
|
||||
Ecosystem: "Go",
|
||||
},
|
||||
Ranges: []AffectsRange{
|
||||
{
|
||||
Type: TypeSemver,
|
||||
Events: []RangeEvent{
|
||||
{
|
||||
Introduced: "0",
|
||||
},
|
||||
{
|
||||
Fixed: "2.1.1",
|
||||
},
|
||||
{
|
||||
Introduced: "2.3.4",
|
||||
},
|
||||
{
|
||||
Fixed: "2.3.5",
|
||||
},
|
||||
{
|
||||
Introduced: "2.5.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DatabaseSpecific: DatabaseSpecific{URL: url},
|
||||
EcosystemSpecific: EcosystemSpecific{
|
||||
Symbols: []string{"b", "A.b"},
|
||||
GOOS: []string{"windows"},
|
||||
GOARCH: []string{"arm64"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Package: Package{
|
||||
Name: "example.com/also-vulnerable/package",
|
||||
Ecosystem: "Go",
|
||||
},
|
||||
Ranges: []AffectsRange{
|
||||
{
|
||||
Type: TypeSemver,
|
||||
Events: []RangeEvent{
|
||||
{
|
||||
Introduced: "0",
|
||||
},
|
||||
{
|
||||
Fixed: "2.1.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DatabaseSpecific: DatabaseSpecific{URL: url},
|
||||
EcosystemSpecific: EcosystemSpecific{
|
||||
Symbols: []string{"z"},
|
||||
GOOS: []string{"windows"},
|
||||
GOARCH: []string{"arm64"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
wantModules := []string{"example.com/vulnerable/v2", "vanity.host/vulnerable", "example.com/also-vulnerable"}
|
||||
sort.Strings(wantModules)
|
||||
|
||||
gotEntry, gotModules := Generate("GO-1991-0001", url, r)
|
||||
if diff := cmp.Diff(wantEntry, gotEntry, cmp.Comparer(func(a, b time.Time) bool { return a.Equal(b) })); diff != "" {
|
||||
t.Errorf("Generate returned unexpected entry (-want +got):\n%s", diff)
|
||||
}
|
||||
sort.Strings(gotModules)
|
||||
if !reflect.DeepEqual(gotModules, wantModules) {
|
||||
t.Errorf("Generate returned unexpected modules: got %v, want %v", gotModules, wantModules)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAffectsSemver(t *testing.T) {
|
||||
cases := []struct {
|
||||
affects Affects
|
||||
version string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
// empty Affects indicates everything is affected
|
||||
affects: Affects{},
|
||||
version: "v0.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// Affects containing an empty SEMVER range also indicates
|
||||
// everything is affected
|
||||
affects: []AffectsRange{{Type: TypeSemver}},
|
||||
version: "v0.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// Affects containing a SEMVER range with only an "introduced":"0"
|
||||
// also indicates everything is affected
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0"}}}},
|
||||
version: "v0.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// v1.0.0 < v2.0.0
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0"}, {Fixed: "2.0.0"}}}},
|
||||
version: "v1.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// v0.0.1 <= v1.0.0
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0.0.1"}}}},
|
||||
version: "v1.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// v1.0.0 <= v1.0.0
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}}}},
|
||||
version: "v1.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// v1.0.0 <= v1.0.0 < v2.0.0
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}}}},
|
||||
version: "v1.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// v0.0.1 <= v1.0.0 < v2.0.0
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0.0.1"}, {Fixed: "2.0.0"}}}},
|
||||
version: "v1.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// v2.0.0 < v3.0.0
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}}}},
|
||||
version: "v3.0.0",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
// Multiple ranges
|
||||
affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}, {Introduced: "3.0.0"}}}},
|
||||
version: "v3.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// Wrong type range
|
||||
affects: []AffectsRange{{Type: TypeUnspecified, Events: []RangeEvent{{Introduced: "3.0.0"}}}},
|
||||
version: "v3.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// Semver ranges don't match
|
||||
affects: []AffectsRange{
|
||||
{Type: TypeUnspecified, Events: []RangeEvent{{Introduced: "3.0.0"}}},
|
||||
{Type: TypeSemver, Events: []RangeEvent{{Introduced: "4.0.0"}}},
|
||||
},
|
||||
version: "v3.0.0",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
// Semver ranges do match
|
||||
affects: []AffectsRange{
|
||||
{Type: TypeUnspecified, Events: []RangeEvent{{Introduced: "3.0.0"}}},
|
||||
{Type: TypeSemver, Events: []RangeEvent{{Introduced: "3.0.0"}}},
|
||||
},
|
||||
version: "v3.0.0",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
// Semver ranges match (go prefix)
|
||||
affects: []AffectsRange{
|
||||
{Type: TypeSemver, Events: []RangeEvent{{Introduced: "3.0.0"}}},
|
||||
},
|
||||
version: "go3.0.1",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
got := c.affects.AffectsSemver(c.version)
|
||||
if c.want != got {
|
||||
t.Errorf("%#v.AffectsSemver(%s): want %t, got %t", c.affects, c.version, c.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSemverCanonicalize(t *testing.T) {
|
||||
in := []report.VersionRange{
|
||||
{
|
||||
Introduced: "go1.16",
|
||||
Fixed: "go1.17",
|
||||
},
|
||||
}
|
||||
expected := Affects{
|
||||
{
|
||||
Type: TypeSemver,
|
||||
Events: []RangeEvent{
|
||||
{
|
||||
Introduced: "1.16.0",
|
||||
},
|
||||
{
|
||||
Fixed: "1.17.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out := generateAffectedRanges(in)
|
||||
if !reflect.DeepEqual(out, expected) {
|
||||
t.Fatalf("unexpected output: got %#v, want %#v", out, expected)
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче