| // 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 ( |
| "time" |
| |
| "golang.org/x/mod/semver" |
| "golang.org/x/vulndb/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 Severity int |
| |
| const ( |
| SevNone Severity = iota |
| SevLow |
| SevMedium |
| SevHigh |
| SevCritical |
| ) |
| |
| var strToSev = map[string]Severity{ |
| // "": SevNone, |
| "low": SevLow, |
| "medium": SevMedium, |
| "high": SevHigh, |
| "critical": SevCritical, |
| } |
| |
| type Type int |
| |
| const ( |
| TypeUnspecified Type = iota |
| TypeGit |
| TypeSemver |
| ) |
| |
| type Ecosystem string |
| |
| const GoEcosystem Ecosystem = "go" |
| |
| type Package struct { |
| Name string |
| Ecosystem Ecosystem |
| } |
| |
| type AffectsRange struct { |
| Type Type |
| Introduced string |
| Fixed string |
| } |
| |
| func (ar AffectsRange) containsSemver(v string) bool { |
| if ar.Type != TypeSemver { |
| return false |
| } |
| |
| return (ar.Introduced == "" || semver.Compare(v, ar.Introduced) >= 0) && |
| (ar.Fixed == "" || semver.Compare(v, ar.Fixed) < 0) |
| } |
| |
| type Affects struct { |
| Ranges []AffectsRange |
| } |
| |
| func generateAffects(versions []report.VersionRange) Affects { |
| a := Affects{} |
| for _, v := range versions { |
| a.Ranges = append(a.Ranges, AffectsRange{ |
| Type: TypeSemver, |
| Introduced: v.Introduced, |
| Fixed: v.Fixed, |
| }) |
| } |
| return a |
| } |
| |
| func (a Affects) AffectsSemver(v string) bool { |
| if len(a.Ranges) == 0 { |
| // No ranges implies all versions are affected |
| return true |
| } |
| var semverRangePresent bool |
| for _, r := range a.Ranges { |
| 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 GoSpecific struct { |
| Symbols []string `json:",omitempty"` |
| GOOS []string `json:",omitempty"` |
| GOARCH []string `json:",omitempty"` |
| URL string |
| } |
| |
| // Entry represents a OSV style JSON vulnerability database |
| // entry |
| type Entry struct { |
| ID string |
| Package Package |
| Summary string |
| Details string |
| Severity Severity |
| Affects Affects |
| ReferenceURLs []string `json:"reference_urls,omitempty"` |
| Aliases []string `json:",omitempty"` |
| EcosystemSpecific GoSpecific `json:"ecosystem_specific,omitempty"` |
| LastModified time.Time `json:"last_modified"` |
| } |
| |
| func Generate(id string, url string, r report.Report) []Entry { |
| importPath := r.Module |
| if r.Package != "" { |
| importPath = r.Package |
| } |
| entry := Entry{ |
| ID: id, |
| Package: Package{ |
| Name: importPath, |
| Ecosystem: GoEcosystem, |
| }, |
| Summary: "", // TODO: think if we want to populate this in reports |
| Details: r.Description, |
| Affects: generateAffects(r.Versions), |
| LastModified: time.Now(), |
| EcosystemSpecific: GoSpecific{ |
| Symbols: r.Symbols, |
| GOOS: r.OS, |
| GOARCH: r.Arch, |
| URL: url, |
| }, |
| } |
| |
| if r.Severity != "" { |
| entry.Severity = strToSev[r.Severity] |
| } else { |
| // Default to medium or none? |
| entry.Severity = SevMedium |
| } |
| |
| if r.Links.PR != "" { |
| entry.ReferenceURLs = append(entry.ReferenceURLs, r.Links.PR) |
| } |
| if r.Links.Commit != "" { |
| entry.ReferenceURLs = append(entry.ReferenceURLs, r.Links.Commit) |
| } |
| if r.Links.Context != nil { |
| entry.ReferenceURLs = append(entry.ReferenceURLs, r.Links.Context...) |
| } |
| |
| if r.CVE != "" { |
| entry.Aliases = []string{r.CVE} |
| } |
| |
| entries := []Entry{entry} |
| |
| // It would be better if this was just a recursive thing probably |
| for _, additional := range r.AdditionalPackages { |
| entryCopy := entry |
| additionalImportPath := additional.Module |
| if additional.Package != "" { |
| additionalImportPath = additional.Package |
| } |
| entryCopy.Package.Name = additionalImportPath |
| entryCopy.EcosystemSpecific.Symbols = additional.Symbols |
| entryCopy.Affects = generateAffects(additional.Versions) |
| |
| entries = append(entries, entryCopy) |
| } |
| |
| return entries |
| } |