blob: d63340b5a0b741a3017235bed4a292511dc8d705 [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 cveschema contains the schema for a CVE, as derived from
// https://github.com/CVEProject/automation-working-group/tree/master/cve_json_schema.
package cveschema
import (
"bytes"
"encoding/json"
"errors"
"golang.org/x/vulndb/internal/derrors"
)
const (
// StateReserved is the initial state for a CVE Record; when the associated
// CVE ID is Reserved by a CNA.
StateReserved = "RESERVED"
// StatePublic is when a CNA populates the data associated with a CVE ID
// as a CVE Record, the state of the CVE Record is PUBLIC. The
// associated data must contain an identification number (CVE ID), a prose
// description, and at least one public reference.
StatePublic = "PUBLIC"
// StateRejected is when the CVE ID and associated CVE Record should no
// longer be used, the CVE Record is placed in the REJECT state. A Rejected
// CVE Record remains on the CVE List so that users can know when it is
// invalid.
StateRejected = "REJECT"
)
// CVE represents a "Common Vulnerabilities and Exposures" record, which is
// associated with a CVE ID and provided by a CNA.
//
// A CVE corresponds to a flaw in a software, firmware, hardware, or service
// component resulting from a weakness that can be exploited, causing a negative
// impact to the confidentiality, integrity, or availability of an impacted
// component or components.
type CVE struct {
// Metadata is metadata about the CVE ID such as the CVE ID, who
// requested it, who assigned it, when it was requested, when it was assigned,
// the current state (PUBLIC, REJECT, etc.) and so on.
Metadata `json:"CVE_data_meta"`
// DataType identifies what kind of data is held in this JSON file. This is
// mandatory and designed to prevent problems with attempting to detect
// what kind of file this is. Valid values for this string are CVE, CNA,
// CVEMENTOR.
DataType string `json:"data_type"`
// DataFormat identifies what data format is used in this JSON file. This
// is mandatory and designed to prevent problems with attempting to detect
// what format of data is used. Valid values for this string are MITRE, it can
// also be user defined (e.g. for internal use).
DataFormat string `json:"data_format"`
// DataVersion identifies which version of the data format is in use. This
// is mandatory and designed to prevent problems with attempting to detect
// what format of data is used.
DataVersion string `json:"data_version"`
// Affects is the root level container for affected vendors and in turn
// their affected technologies, products, hardware, etc. It only goes in
// the root level.
Affects Affects `json:"affects"`
// Description is a description of the issue. It can exist in the root
// level or within virtually any other container, the intent being that for
// example different products, and configurations may result in different
// impacts and thus descriptions of the issue.
Description Description `json:"description"`
// ProblemType is problem type information (e.g. CWE identifier).
ProblemType ProblemType `json:"problemtype"`
// References is reference data in the form of URLs or file objects
// (uuencoded and embedded within the JSON file, exact format to be
// decided, e.g. we may require a compressed format so the objects require
// unpacking before they are "dangerous").
References References `json:"references"`
// Credit is the credit information (different than CVE_timeline in that
// these are specific things being credited to specific
// people/organizations/etc.).
Credit Credit
// RawCredit is for unmarshaling only. Do not use.
RawCredit json.RawMessage `json:"credit"`
}
// Credit is the credit information (different than CVE_timeline in that these
// are specific things being credited to specific people/organizations/etc.).
type Credit struct {
Data CreditData `json:"credit_data"`
}
type CreditData struct {
Description Description `json:"description"`
}
// Metadata is meta data about the CVE ID such as the CVE ID, who requested
// it, who assigned it, when it was requested, when it was assigned, the
// current state (PUBLIC, REJECT, etc.) and so on.
type Metadata struct {
Assigner string `json:"ASSIGNER"`
ID string `json:"ID"`
State string `json:"STATE"`
}
// Affects is the root level container for affected vendors and in turn their
// affected technologies, products, hardware, etc. It only goes in the root
// level.
type Affects struct {
Vendor Vendor `json:"vendor"`
}
// Description is a description of the issue. It can exist in the root level or
// within virtually any other container, the intent being that for example
// different products, and configurations may result in different impacts and
// thus descriptions of the issue.
//
// The description could include:
//
// An explanation of an attack type using the vulnerability;
// The impact of the vulnerability;
// The software components within a software product that are affected by the
// vulnerability; and
// Any attack vectors that can make use of the vulnerability.
//
// Descriptions often follow this template:
//
// [PROBLEM TYPE] in [PRODUCT/VERSION] causes [IMPACT] when [ATTACK]
//
// where impact and attack are arbitrary terms that should be relevant to the
// nature of the vulnerability.
type Description struct {
Data []LangString `json:"description_data"`
}
// ProblemType is problem type information (e.g. CWE identifier).
//
// It can include an arbitrary summary of the problem, though Common Weakness
// Enumerations (CWEs) are a standard to use in this field.
type ProblemType struct {
Data []ProblemTypeDataItem `json:"problemtype_data"`
}
// A ProblemTypeDataItem is an entry in ProblemType.Data.
type ProblemTypeDataItem struct {
Description []LangString `json:"description"`
}
// LangString is a JSON data type containing the language that a description is
// written in and the text string.
type LangString struct {
Lang string `json:"lang"`
Value string `json:"value"`
}
// References is reference data in the form of URLs or file objects (uuencoded
// and embedded within the JSON file, exact format to be decided, e.g. we may
// require a compressed format so the objects require unpacking before they are
// "dangerous").
type References struct {
Data []Reference `json:"reference_data"`
}
// A reference is a URL pointing to a world-wide-web-based resource. For
// CSV and flat-file formats, they should be separated by a space. References
// should point to content that is relevant to the vulnerability and include at
// least all the details included in the CVE entry. Ideally, references should
// point to content that includes the CVE ID itself whenever possible. References
// must also be publicly available, as described in Section 2.1.1 of the CVE
// Numbering Authorities (CNA) Rules.
type Reference struct {
URL string `json:"url"`
}
// Vendor is the container for affected vendors, it only goes in the affects
// container.
type Vendor struct {
// Data is an array of version values (vulnerable and not); we use an
// array so that different entities can make statements about the same
// vendor and they are separate (if we used a JSON object we'd essentially
// be keying on the vendor name and they would have to overlap). Also this
// allows things like data_version or description to be applied directly to
// the vendor entry.
Data []VendorDataItem `json:"vendor_data"`
}
// A VendorDataItem represents a single vendor name and product.
type VendorDataItem struct {
Product Product `json:"product"`
VendorName string `json:"vendor_name"`
}
// Product is the container for affected technologies, products, hardware, etc.
//
// As a general guideline, the product should include the vendor, developer, or
// project name as well as the name of the actual software or hardware in which
// the vulnerability exists.
type Product struct {
// Data is an array of version values (vulnerable and not); we use
// an array so that we can make multiple statements about the same product and
// they are separate (if we used a JSON object we'd essentially be keying on
// the product name and they would have to overlap). Also this allows things
// like data_version or description to be applied directly to the product
// entry.
Data []ProductDataItem `json:"product_data"`
}
// ProductDataItem represents a single product name and version that belongs to
// a product container.
type ProductDataItem struct {
ProductName string `json:"product_name"`
Version VersionData `json:"version"`
}
// VersionData is an array of version values (vulnerable and not); we use an
// array so that we can make multiple statements about the same version and they
// are separate (if we used a JSON object we'd essentially be keying on the
// version name/number and they would have to overlap). Also this allows things
// like data_version or description to be applied directly to the product entry.
// This also allows more complex statements such as "Product X between versions
// 10.2 and 10.8" to be put in a machine-readable format. As well since multiple
// statements can be used multiple branches of the same product can be defined
// here.
type VersionData struct {
Data []VersionDataItem `json:"version_data"`
}
// A VersionDataItem represents a version, the date of release, or whatever
// indicator that is used by vendors, developers, or projects to differentiate
// between releases. The version can be described with specific version
// numbers, ranges of versions, or “all versions before/after” a version number or
// date.
type VersionDataItem struct {
VersionValue string `json:"version_value"`
VersionAffected string `json:"version_affected"`
}
var nullBytes = []byte("null")
// UnmarshalJSON implements json.Unmarshaler.
func (c *CVE) UnmarshalJSON(data []byte) (err error) {
defer derrors.Wrap(&err, "cveschema.CVE.UnmarshalJSON")
if bytes.Equal(data, nullBytes) {
return nil
}
// To avoid infinite recursion, disable the UnmarshalJSON method on CVE
// by defining a new type.
type nomethod CVE
if err := json.Unmarshal(data, (*nomethod)(c)); err != nil {
return err
}
c.Credit, err = decodeCredit(c.RawCredit)
if err != nil {
return err
}
c.RawCredit = nil
return nil
}
// The Credit field can have one of several formats.
func decodeCredit(raw []byte) (Credit, error) {
if len(raw) == 0 {
return Credit{}, nil
}
var c Credit
if err := json.Unmarshal(raw, &c); err == nil {
return c, nil
}
var lstrings []LangString
if err := json.Unmarshal(raw, &lstrings); err == nil {
return Credit{Data: CreditData{Description: Description{Data: lstrings}}}, nil
}
var strings []string
if err := json.Unmarshal(raw, &strings); err == nil {
var ls []LangString
for _, s := range strings {
ls = append(ls, LangString{Lang: "eng", Value: s})
}
return Credit{Data: CreditData{Description: Description{Data: ls}}}, nil
}
var str string
if err := json.Unmarshal(raw, &str); err == nil {
return Credit{
Data: CreditData{
Description: Description{
Data: []LangString{{Lang: "eng", Value: str}},
},
},
}, nil
}
return Credit{}, errors.New("could not parse credit field")
}