blob: e878425715f00a4cb850f0b14efe6314bb99a377 [file] [log] [blame]
// 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"
"time"
"golang.org/x/vulndb/internal/cveclient"
"golang.org/x/vulndb/internal/cveschema"
"golang.org/x/vulndb/internal/cveschema5"
"golang.org/x/vulndb/internal/ghsa"
"golang.org/x/vulndb/internal/proxy"
)
// Source represents a vulnerability format (e.g., GHSA, CVE)
// that can be converted to our Report format.
type Source interface {
// SourceID returns the ID of the source.
// For example, the GHSA or CVE id.
SourceID() string
ToReport(modulePath string) *Report
}
func New(src Source, pc *proxy.Client, opts ...NewOption) *Report {
cfg := newCfg(opts)
r := src.ToReport(cfg.ModulePath)
r.ID = cfg.GoID
r.AddAliases(cfg.Aliases)
r.SourceMeta = &SourceMeta{
ID: src.SourceID(),
}
if !cfg.Created.IsZero() {
r.SourceMeta.Created = &cfg.Created
}
r.Fix(pc)
return r
}
type Fetcher interface {
Fetch(ctx context.Context, id string) (Source, error)
}
type NewOption func(*cfg)
func WithModulePath(path string) NewOption {
return func(h *cfg) {
h.ModulePath = path
}
}
func WithAliases(aliases []string) NewOption {
return func(h *cfg) {
h.Aliases = aliases
}
}
func WithCreated(created time.Time) NewOption {
return func(h *cfg) {
h.Created = created
}
}
func WithGoID(id string) NewOption {
return func(h *cfg) {
h.GoID = id
}
}
type cfg struct {
ModulePath string
Aliases []string
Created time.Time
GoID string
}
const pendingID = "GO-ID-PENDING"
func newCfg(opts []NewOption) *cfg {
h := &cfg{
GoID: pendingID,
}
for _, opt := range opts {
opt(h)
}
return h
}
type cve5 struct {
*cveschema5.CVERecord
}
var _ Source = &cve5{}
func ToCVE5(c *cveschema5.CVERecord) Source {
return &cve5{CVERecord: c}
}
func (c *cve5) ToReport(modulePath string) *Report {
return cve5ToReport(c.CVERecord, modulePath)
}
func (c *cve5) SourceID() string {
return c.Metadata.ID
}
type cve5Fetcher struct{}
func CVE5Fetcher() Fetcher {
return &cve5Fetcher{}
}
func (_ *cve5Fetcher) Fetch(ctx context.Context, id string) (Source, error) {
cve, err := cveclient.Fetch(id)
if err != nil {
return nil, err
}
return &cve5{CVERecord: cve}, nil
}
// cve4 is a wrapper for a CVE in CVE JSON 4.0 (legacy) format.
//
// Note: Fetch is not implemented for CVE4, as it is a legacy format
// which will be phased out soon.
type cve4 struct {
*cveschema.CVE
}
var _ Source = &cve4{}
func ToCVE4(c *cveschema.CVE) Source {
return &cve4{CVE: c}
}
func (c *cve4) ToReport(modulePath string) *Report {
return cveToReport(c.CVE, modulePath)
}
func (c *cve4) SourceID() string {
return c.ID
}
// legacyGHSA is a wrapper for a GHSA in the format retrievable
// via the Github GraphQL API.
//
// We are planning to phase this out in favor of the Github OSV format,
// but some of our processes still rely on this format.
type legacyGHSA struct {
*ghsa.SecurityAdvisory
}
var _ Source = &legacyGHSA{}
func ToLegacyGHSA(g *ghsa.SecurityAdvisory) Source {
return &legacyGHSA{
SecurityAdvisory: g,
}
}
func (g *legacyGHSA) ToReport(modulePath string) *Report {
return ghsaToReport(g.SecurityAdvisory, modulePath)
}
func (g *legacyGHSA) SourceID() string {
return g.ID
}
type legacyGHSAFetcher struct {
*ghsa.Client
}
func LegacyGHSAFetcher(c *ghsa.Client) Fetcher {
return &legacyGHSAFetcher{
Client: c,
}
}
func (g *legacyGHSAFetcher) Fetch(ctx context.Context, id string) (Source, error) {
fetched, err := g.FetchGHSA(ctx, id)
if err != nil {
return nil, err
}
return &legacyGHSA{
SecurityAdvisory: fetched,
}, err
}
// original represents an original report created from scratch by the Go Security Team.
//
// This is used for standard library & toolchain reports, or in cases where the
// source report cannot be retrieved automatically.
type original struct{}
var _ Source = &original{}
func Original() Source {
return &original{}
}
func (original) ToReport(modulePath string) *Report {
return &Report{
Modules: []*Module{
{
Module: modulePath,
},
},
}
}
func (original) SourceID() string {
return sourceGoTeam
}