providers/vfeed/schema/providers.go (246 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions andT47981963 // limitations under the License. package schema import ( "errors" "fmt" "time" nvd "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" ) // TODO: move this file to its own package in the future, to implement a common // API for all providers (hence the "Providers" namespace). We will need to add // missing features to support all providers. const ( providersDataFormat = "MITRE" providersDataType = "CVE" providersDataLang = "en" providersDataVersion = "4.0" providersTimeLayout = "2006-01-02T15:04Z" ) // ProvidersItem captures the top-level CVE information for a vendor. type ProvidersItem struct { Vendor string ID string Description string CWEs []string References *ProvidersReferences Configuration *ProvidersConfiguration CVSS2 *ProvidersCVSS CVSS3 *ProvidersCVSS LastModifiedDate *time.Time PublishedDate *time.Time } // ProvidersCVSS is used to store CVSS2 and CVSS3 data. type ProvidersCVSS struct { BaseScore float64 TemporalScore float64 Vector string } // ProvidersReferences hold data related to the thread. type ProvidersReferences struct { referenceData []*nvd.CVEJSON40Reference } // ProvidersConfiguration captures what specific software versions are vulnerable. type ProvidersConfiguration struct { Nodes []*ProvidersNode } // ProvidersNode holds a set of matches, any positive match being able to // configure a CVE. If conditional matches are present as well, then at least // one conditional match should also be positive to configure a CVE. type ProvidersNode struct { matches []*nvd.NVDCVEFeedJSON10DefCPEMatch conditionalMatches []*nvd.NVDCVEFeedJSON10DefCPEMatch } // ProvidersMatch represents software versions that match a CPE. type ProvidersMatch struct { CPE22URI string CPE23URI string VersionStartExcluding string VersionStartIncluding string VersionEndExcluding string VersionEndIncluding string Vulnerable bool } // ProvidersConvertStrTime takes a time layout and a string representing time in that // layout and converts it to a Time type. If the string is empty nil is // returned. func ProvidersConvertStrTime(layout, strTime string) (*time.Time, error) { if strTime == "" { return nil, nil } t, err := time.Parse(layout, strTime) if err != nil { return nil, err } return &t, nil } func providersConvertTime(t *time.Time) string { if t == nil { return "" } return t.Format(providersTimeLayout) } // ProvidersNewItem creates a vendor item. func ProvidersNewItem(item *ProvidersItem) (*nvd.NVDCVEFeedJSON10DefCVEItem, error) { if err := item.validate(); err != nil { return nil, fmt.Errorf("validation error: %v", err) } return &nvd.NVDCVEFeedJSON10DefCVEItem{ CVE: &nvd.CVEJSON40{ CVEDataMeta: &nvd.CVEJSON40CVEDataMeta{ ID: item.ID, ASSIGNER: item.Vendor, }, DataFormat: providersDataFormat, DataType: providersDataType, DataVersion: providersDataVersion, Description: &nvd.CVEJSON40Description{ DescriptionData: []*nvd.CVEJSON40LangString{ { Lang: providersDataLang, Value: item.Description, }, }, }, Problemtype: item.problemType(), References: item.references(), }, Configurations: item.Configuration.convertToNVD(), Impact: &nvd.NVDCVEFeedJSON10DefImpact{ BaseMetricV2: &nvd.NVDCVEFeedJSON10DefImpactBaseMetricV2{ CVSSV2: item.cvssV20(), }, BaseMetricV3: &nvd.NVDCVEFeedJSON10DefImpactBaseMetricV3{ CVSSV3: item.cvssV30(), }, }, LastModifiedDate: providersConvertTime(item.LastModifiedDate), PublishedDate: providersConvertTime(item.PublishedDate), }, nil } // validate will return an error if any mandatory fields are missing. func (item *ProvidersItem) validate() error { if item.ID == "" || item.Vendor == "" || item.Description == "" { return errors.New("id, vendor and description can't be empty") } if item.Configuration == nil { return errors.New("Configuration can't be nil") } return nil } func (item *ProvidersItem) cvssV20() *nvd.CVSSV20 { if item.CVSS2 == nil { return nil } return &nvd.CVSSV20{ BaseScore: item.CVSS2.BaseScore, TemporalScore: item.CVSS2.TemporalScore, VectorString: item.CVSS2.Vector, } } func (item *ProvidersItem) cvssV30() *nvd.CVSSV30 { if item.CVSS3 == nil { return nil } return &nvd.CVSSV30{ BaseScore: item.CVSS3.BaseScore, TemporalScore: item.CVSS3.TemporalScore, VectorString: item.CVSS3.Vector, } } func (item *ProvidersItem) references() *nvd.CVEJSON40References { if item.References == nil || item.References.referenceData == nil { return nil } return &nvd.CVEJSON40References{ ReferenceData: item.References.referenceData, } } func (item *ProvidersItem) problemType() *nvd.CVEJSON40Problemtype { if item.CWEs == nil { return nil } var weaknesses []*nvd.CVEJSON40LangString for _, cwe := range item.CWEs { weaknesses = append(weaknesses, &nvd.CVEJSON40LangString{ Lang: providersDataLang, Value: cwe, }) } return &nvd.CVEJSON40Problemtype{ ProblemtypeData: []*nvd.CVEJSON40ProblemtypeProblemtypeData{ { Description: weaknesses, }, }, } } // ProvidersNewReferences creates a ProvidersReferences. func ProvidersNewReferences() *ProvidersReferences { return &ProvidersReferences{} } // Add adds a new reference to the references. func (r *ProvidersReferences) Add(name, url string) { r.referenceData = append(r.referenceData, &nvd.CVEJSON40Reference{ Name: name, URL: url, }) } // ProvidersNewConfiguration creates a ProvidersConfiguration. func ProvidersNewConfiguration() *ProvidersConfiguration { return &ProvidersConfiguration{ Nodes: []*ProvidersNode{}, } } func (c *ProvidersConfiguration) convertToNVD() *nvd.NVDCVEFeedJSON10DefConfigurations { var nvdNodes []*nvd.NVDCVEFeedJSON10DefNode for _, node := range c.Nodes { nvdNode := &nvd.NVDCVEFeedJSON10DefNode{} if len(node.conditionalMatches) > 0 { nvdNode.Operator = "AND" nvdNode.Children = []*nvd.NVDCVEFeedJSON10DefNode{ &nvd.NVDCVEFeedJSON10DefNode{ Operator: "OR", CPEMatch: node.matches, }, &nvd.NVDCVEFeedJSON10DefNode{ Operator: "OR", CPEMatch: node.conditionalMatches, }, } } else { nvdNode.Operator = "OR" nvdNode.CPEMatch = node.matches } nvdNodes = append(nvdNodes, nvdNode) } return &nvd.NVDCVEFeedJSON10DefConfigurations{ CVEDataVersion: providersDataVersion, Nodes: nvdNodes, } } // NewNode creates a Node in the ProvidersConfiguration func (c *ProvidersConfiguration) NewNode() *ProvidersNode { node := &ProvidersNode{ matches: []*nvd.NVDCVEFeedJSON10DefCPEMatch{}, conditionalMatches: []*nvd.NVDCVEFeedJSON10DefCPEMatch{}, } c.Nodes = append(c.Nodes, node) return node } // AddMatch adds a ProvidersMatch to a ProvidersNode. func (node *ProvidersNode) AddMatch(m *ProvidersMatch) { node.matches = append(node.matches, m.convertToNVD()) } // AddConditionalMatch adds a ProvidersMatch to a ProvidersNode. func (node *ProvidersNode) AddConditionalMatch(m *ProvidersMatch) { node.conditionalMatches = append(node.conditionalMatches, m.convertToNVD()) } // ProvidersNewMatch creates a ProvidersMatch. func ProvidersNewMatch(cpe22uri, cpe23uri string, vulnerable bool) *ProvidersMatch { return &ProvidersMatch{ CPE22URI: cpe22uri, CPE23URI: cpe23uri, Vulnerable: vulnerable, } } func (m *ProvidersMatch) convertToNVD() *nvd.NVDCVEFeedJSON10DefCPEMatch { return &nvd.NVDCVEFeedJSON10DefCPEMatch{ Cpe22Uri: m.CPE22URI, Cpe23Uri: m.CPE23URI, VersionStartExcluding: m.VersionStartExcluding, VersionStartIncluding: m.VersionStartIncluding, VersionEndExcluding: m.VersionEndExcluding, VersionEndIncluding: m.VersionEndIncluding, Vulnerable: m.Vulnerable, } } // AddVersionStart adds the starting version to a Match, along with // whether that version is included or excluded from the Match. func (m *ProvidersMatch) AddVersionStart(version string, excluding bool) { if excluding { m.VersionStartExcluding = version } else { m.VersionStartIncluding = version } } // AddVersionEnd adds the ending version to a Match, along with // whether that version is included or excluded from the Match. func (m *ProvidersMatch) AddVersionEnd(version string, excluding bool) { if excluding { m.VersionEndExcluding = version } else { m.VersionEndIncluding = version } }