blob: b09899644af6e5a0b8426c2ee0dec72c1489fdb6 [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.
// File copied from github.com/google/osv-scanner@v1.8.0/pkg/models/vulnerability.go
package genericosv
import (
"encoding/json"
"time"
)
// Package identifies the affected code library or command provided by the
// package.
//
// See: https://ossf.github.io/osv-schema/#affectedpackage-field
type Package struct {
Ecosystem Ecosystem `json:"ecosystem" yaml:"ecosystem"`
Name string `json:"name" yaml:"name"`
Purl string `json:"purl,omitempty" yaml:"purl,omitempty"`
}
// Event describes a single version that either:
//
// - Introduces a vulnerability: {"introduced": string}
// - Fixes a vulnerability: {"fixed": string}
// - Describes the last known affected version: {"last_affected": string}
// - Sets an upper limit on the range being described: {"limit": string}
//
// Event instances form part of a “timeline” of status changes for the affected
// package described by the Affected struct.
//
// See: https://ossf.github.io/osv-schema/#affectedrangesevents-fields
type Event struct {
Introduced string `json:"introduced,omitempty" yaml:"introduced,omitempty"`
Fixed string `json:"fixed,omitempty" yaml:"fixed,omitempty"`
LastAffected string `json:"last_affected,omitempty" yaml:"last_affected,omitempty"`
Limit string `json:"limit,omitempty" yaml:"limit,omitempty"`
}
// Range describes the affected range of given version for a specific package.
//
// See: https://ossf.github.io/osv-schema/#affectedranges-field
type Range struct {
Type RangeType `json:"type" yaml:"type"`
Events []Event `json:"events" yaml:"events"`
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
DatabaseSpecific map[string]interface{} `json:"database_specific,omitempty" yaml:"database_specific,omitempty"`
}
// Severity is used to describe the severity of a vulnerability for an affected
// package using one or more quantitative scoring methods.
//
// See: https://ossf.github.io/osv-schema/#severity-field
type Severity struct {
Type SeverityType `json:"type" yaml:"type"`
Score string `json:"score" yaml:"score"`
}
// Affected describes an affected package version, meaning one instance that
// contains the vulnerability.
//
// See: https://ossf.github.io/osv-schema/#affected-fields
type Affected struct {
Package Package `json:"package,omitempty" yaml:"package,omitempty"`
Severity []Severity `json:"severity,omitempty" yaml:"severity,omitempty"`
Ranges []Range `json:"ranges,omitempty" yaml:"ranges,omitempty"`
Versions []string `json:"versions,omitempty" yaml:"versions,omitempty"`
DatabaseSpecific map[string]interface{} `json:"database_specific,omitempty" yaml:"database_specific,omitempty"`
EcosystemSpecific map[string]interface{} `json:"ecosystem_specific,omitempty" yaml:"ecosystem_specific,omitempty"`
}
// MarshalJSON implements the json.Marshaler interface.
//
// This method ensures Package is only present if it is not equal to the zero value.
// This is achieved by embedding the Affected struct with a pointer to Package used
// to populate the "package" key in the JSON object.
func (a Affected) MarshalJSON() ([]byte, error) {
type rawAffected Affected // alias Affected to avoid recursion during Marshal
type wrapper struct {
Package *Package `json:"package,omitempty"`
rawAffected
}
raw := wrapper{rawAffected: rawAffected(a)}
if a.Package == (Package{}) {
raw.Package = nil
} else {
raw.Package = &(a.Package)
}
return json.Marshal(raw)
}
// Reference links to additional information, advisories, issue tracker entries,
// and so on about the vulnerability itself.
//
// See: https://ossf.github.io/osv-schema/#references-field
type Reference struct {
Type ReferenceType `json:"type" yaml:"type"`
URL string `json:"url" yaml:"url"`
}
// Credit gives credit for the discovery, confirmation, patch, or other events
// in the life cycle of a vulnerability.
//
// See: https://ossf.github.io/osv-schema/#credits-fields
type Credit struct {
Name string `json:"name" yaml:"name"`
Type CreditType `json:"type,omitempty" yaml:"type,omitempty"`
Contact []string `json:"contact,omitempty" yaml:"contact,omitempty"`
}
// Vulnerability is the core Open Source Vulnerability (OSV) data type.
//
// The full documentation for the schema is available at
// https://ossf.github.io/osv-schema.
type Vulnerability struct {
SchemaVersion string `json:"schema_version,omitempty" yaml:"schema_version,omitempty"`
ID string `json:"id" yaml:"id"`
Modified time.Time `json:"modified" yaml:"modified"`
Published time.Time `json:"published,omitempty" yaml:"published,omitempty"`
Withdrawn time.Time `json:"withdrawn,omitempty" yaml:"withdrawn,omitempty"`
Aliases []string `json:"aliases,omitempty" yaml:"aliases,omitempty"`
Related []string `json:"related,omitempty" yaml:"related,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Details string `json:"details,omitempty" yaml:"details,omitempty"`
Affected []Affected `json:"affected,omitempty" yaml:"affected,omitempty"`
// TODO(tatianabradley): There is a bug in Severity unmarshal.
// We don't use this field, so it is ignored until we fix this.
Severity []Severity `json:"-,omitempty" yaml:"-,omitempty"`
References []Reference `json:"references,omitempty" yaml:"references,omitempty"`
Credits []Credit `json:"credits,omitempty" yaml:"credits,omitempty"`
DatabaseSpecific map[string]interface{} `json:"database_specific,omitempty" yaml:"database_specific,omitempty"`
}
// MarshalJSON implements the json.Marshaler interface.
//
// This method ensures times all times are formatted correctly according to the schema.
func (v Vulnerability) MarshalJSON() ([]byte, error) {
type rawVulnerability Vulnerability // alias Vulnerability to avoid recursion during Marshal
type wrapper struct {
Modified string `json:"modified"`
Published string `json:"published,omitempty"`
Withdrawn string `json:"withdrawn,omitempty"`
rawVulnerability
}
raw := wrapper{rawVulnerability: rawVulnerability(v)}
raw.Modified = v.Modified.UTC().Format(time.RFC3339)
if !v.Published.IsZero() {
raw.Published = v.Published.UTC().Format(time.RFC3339)
}
if !v.Withdrawn.IsZero() {
raw.Withdrawn = v.Withdrawn.UTC().Format(time.RFC3339)
}
return json.Marshal(raw)
}
// MarshalYAML implements the yaml.Marshaler interface.
//
// This method ensures times all times are formatted correctly.
func (v Vulnerability) MarshalYAML() (interface{}, error) {
type rawVulnerability Vulnerability // alias Vulnerability to avoid recursion during Marshal
raw := rawVulnerability(v)
if !v.Modified.IsZero() {
raw.Modified = v.Modified.UTC()
}
if !v.Published.IsZero() {
raw.Published = v.Published.UTC()
}
if !v.Withdrawn.IsZero() {
raw.Withdrawn = v.Withdrawn.UTC()
}
return raw, nil
}