blob: b92ee145bacca9d3e2b0e6b569b58661a5fd11c0 [file] [log] [blame]
// 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 report
import (
"errors"
"sort"
"strings"
"golang.org/x/vulndb/internal/cveschema"
"golang.org/x/vulndb/internal/derrors"
"golang.org/x/vulndb/internal/stdlib"
)
// ToCVE creates a CVE from a reports/GO-YYYY-NNNN.yaml file.
func ToCVE(reportPath string) (_ *cveschema.CVE, err error) {
defer derrors.Wrap(&err, "report.ToCVE(%q)", reportPath)
r, err := Read(reportPath)
if err != nil {
return nil, err
}
if len(r.CVEs) > 0 {
return nil, errors.New("report has CVE ID is wrong section (should be in cve_metadata for self-issued CVEs)")
}
if r.CVEMetadata == nil {
return nil, errors.New("report missing cve_metadata section")
}
if r.CVEMetadata.ID == "" {
return nil, errors.New("report missing CVE ID")
}
c := &cveschema.CVE{
DataType: "CVE",
DataFormat: "MITRE",
DataVersion: "4.0",
Metadata: cveschema.Metadata{
ID: r.CVEMetadata.ID,
Assigner: "security@golang.org",
State: cveschema.StatePublic,
},
Description: cveschema.Description{
Data: []cveschema.LangString{
{
Lang: "eng",
Value: strings.TrimSuffix(r.CVEMetadata.Description, "\n"),
},
},
},
ProblemType: cveschema.ProblemType{
Data: []cveschema.ProblemTypeDataItem{
{
Description: []cveschema.LangString{
{
Lang: "eng",
Value: r.CVEMetadata.CWE,
},
},
},
},
},
}
for _, p := range r.Packages {
c.Affects.Vendor.Data = append(c.Affects.Vendor.Data, cveschema.VendorDataItem{
VendorName: "n/a", // ???
Product: cveschema.Product{
Data: []cveschema.ProductDataItem{
{
ProductName: p.Package,
Version: versionToVersion(p.Versions),
},
},
},
})
}
if r.Links.PR != "" {
c.References.Data = append(c.References.Data, cveschema.Reference{URL: r.Links.PR})
}
if r.Links.Commit != "" {
c.References.Data = append(c.References.Data, cveschema.Reference{URL: r.Links.Commit})
}
for _, url := range r.Links.Context {
c.References.Data = append(c.References.Data, cveschema.Reference{URL: url})
}
return c, nil
}
func versionToVersion(versions []VersionRange) cveschema.VersionData {
vd := cveschema.VersionData{}
for _, vr := range versions {
if vr.Introduced != "" {
vd.Data = append(vd.Data, cveschema.VersionDataItem{
VersionValue: string(vr.Introduced),
VersionAffected: ">=",
})
}
if vr.Fixed != "" {
vd.Data = append(vd.Data, cveschema.VersionDataItem{
VersionValue: string(vr.Fixed),
VersionAffected: "<",
})
}
}
return vd
}
// CVEToReport creates a Report struct from a given CVE and modulePath.
func CVEToReport(c *cveschema.CVE, modulePath string) *Report {
var description string
for _, d := range c.Description.Data {
description += d.Value + "\n"
}
var (
pr, commit string
context []string
)
for _, r := range c.References.Data {
if strings.Contains(r.URL, "go-review.googlesource.com") {
pr = r.URL
} else if strings.Contains(r.URL, "commit") {
commit = r.URL
} else if strings.Contains(r.URL, "pull") {
pr = r.URL
} else {
context = append(context, r.URL)
}
}
sort.Strings(context)
var credits []string
for _, v := range c.Credit.Data.Description.Data {
credits = append(credits, v.Value)
}
credit := strings.Join(credits, "\t")
var pkgPath string
if data := c.Affects.Vendor.Data; len(data) > 0 {
if data2 := data[0].Product.Data; len(data2) > 0 {
pkgPath = data2[0].ProductName
}
}
r := &Report{
Packages: []Package{{
Module: modulePath,
Package: pkgPath,
}},
Description: description,
CVEs: []string{c.Metadata.ID},
Credit: credit,
Links: Links{
Commit: commit,
PR: pr,
Context: context,
},
}
if !strings.Contains(modulePath, ".") {
r.Packages[0].Module = stdlib.ModulePath
r.Packages[0].Package = modulePath
}
if stdlib.Contains(r.Packages[0].Module) && r.Packages[0].Package == "" {
r.Packages[0].Package = modulePath
}
r.Fix()
return r
}