providers/redhat/schema/convert.go (172 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 and // limitations under the License. package schema import ( "fmt" "strconv" "strings" nvd "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" ) const ( cveVersion = "4.0" ) func (cve *CVE) Convert() (*nvd.NVDCVEFeedJSON10DefCVEItem, error) { publishedDate, err := convertTime(cve.PublicDate) if err != nil { return nil, fmt.Errorf("unable to convert published date: %v", err) } configurations, err := cve.newConfigurations() if err != nil { return nil, fmt.Errorf("unable to construct configurations: %v", err) } impact, err := cve.newImpact() if err != nil { return nil, fmt.Errorf("unable to construct impact: %v", err) } item := nvd.NVDCVEFeedJSON10DefCVEItem{ CVE: &nvd.CVEJSON40{ CVEDataMeta: &nvd.CVEJSON40CVEDataMeta{ ID: cve.ID(), ASSIGNER: "redhat", }, DataFormat: "MITRE", DataType: "CVE", DataVersion: cveVersion, Description: &nvd.CVEJSON40Description{ DescriptionData: []*nvd.CVEJSON40LangString{ { Lang: "en", Value: strings.Join(cve.Details, "\n"), }, }, }, Problemtype: cve.newProblemType(), References: cve.newReferences(), }, Configurations: configurations, Impact: impact, PublishedDate: publishedDate, } return &item, nil } func (cve *CVE) ID() string { return cve.Name } func (cve *CVE) newProblemType() *nvd.CVEJSON40Problemtype { cwes := findCWEs(cve.CWE) if len(cwes) == 0 { return nil } data := make([]*nvd.CVEJSON40ProblemtypeProblemtypeData, len(cwes)) for i, cwe := range cwes { data[i] = &nvd.CVEJSON40ProblemtypeProblemtypeData{ Description: []*nvd.CVEJSON40LangString{ {Lang: "en", Value: cwe}, }, } } return &nvd.CVEJSON40Problemtype{ProblemtypeData: data} } func (cve *CVE) newReferences() *nvd.CVEJSON40References { if len(cve.References) == 0 { return nil } referenceData := make([]*nvd.CVEJSON40Reference, len(cve.References)) for i, ref := range cve.References { referenceData[i] = &nvd.CVEJSON40Reference{URL: ref} } return &nvd.CVEJSON40References{ReferenceData: referenceData} } func (cve *CVE) newImpact() (*nvd.NVDCVEFeedJSON10DefImpact, error) { if cve.CVSS == nil && cve.CVSS3 == nil { return nil, fmt.Errorf("cvss v2 nor cvss v3 is set in the cve") } impact := nvd.NVDCVEFeedJSON10DefImpact{} if cve.CVSS != nil { score, err := strconv.ParseFloat(cve.CVSS.BaseScore, 64) if err != nil { return nil, fmt.Errorf("unable to parse cvss v2 base score: %v", err) } impact.BaseMetricV2 = &nvd.NVDCVEFeedJSON10DefImpactBaseMetricV2{ CVSSV2: &nvd.CVSSV20{ BaseScore: score, VectorString: cve.CVSS.Vector, }, } } if cve.CVSS3 != nil { score, err := strconv.ParseFloat(cve.CVSS3.BaseScore, 64) if err != nil { return nil, fmt.Errorf("unable to parse cvss v3 base score: %v", err) } impact.BaseMetricV3 = &nvd.NVDCVEFeedJSON10DefImpactBaseMetricV3{ CVSSV3: &nvd.CVSSV30{ BaseScore: score, VectorString: cve.CVSS3.Vector, }, } } return &impact, nil } // CPEs configuration, AKA the tricky part func (cve *CVE) newConfigurations() (*nvd.NVDCVEFeedJSON10DefConfigurations, error) { nodes := make([]*nvd.NVDCVEFeedJSON10DefNode, len(cve.AffectedRelease)+len(cve.PackageState)) var err error for i, ar := range cve.AffectedRelease { if nodes[i], err = ar.createNode(); err != nil { return nil, fmt.Errorf("can't create node for affected release %d: %v", i, err) } } offset := len(cve.AffectedRelease) for i, ps := range cve.PackageState { if nodes[i+offset], err = ps.createNode(); err != nil { return nil, fmt.Errorf("can't create node for package state %d: %v", i, err) } } conf := nvd.NVDCVEFeedJSON10DefConfigurations{ CVEDataVersion: cveVersion, Nodes: nodes, } return &conf, nil } func (ar *AffectedRelease) createNode() (*nvd.NVDCVEFeedJSON10DefNode, error) { node := nvd.NVDCVEFeedJSON10DefNode{ Operator: "AND", CPEMatch: []*nvd.NVDCVEFeedJSON10DefCPEMatch{ { Cpe22Uri: ar.CPE, Vulnerable: false, }, }, } if ar.Package != "" { pkgAttrs, err := package2wfn(ar.Package) if err != nil { return nil, fmt.Errorf("can't create wfn from package: %v", err) } node.CPEMatch = append(node.CPEMatch, &nvd.NVDCVEFeedJSON10DefCPEMatch{ Cpe22Uri: pkgAttrs.BindToURI(), Cpe23Uri: pkgAttrs.BindToFmtString(), Vulnerable: false, }) } return &node, nil } func (ps *PackageState) createNode() (*nvd.NVDCVEFeedJSON10DefNode, error) { pkgAttrs, err := packageName2wfn(ps.PackageName) if err != nil { return nil, fmt.Errorf("can't create wfn from package name: %v", err) } node := nvd.NVDCVEFeedJSON10DefNode{ Operator: "AND", CPEMatch: []*nvd.NVDCVEFeedJSON10DefCPEMatch{ // package { Cpe22Uri: pkgAttrs.BindToURI(), Cpe23Uri: pkgAttrs.BindToFmtString(), Vulnerable: !IsFixed(ps.FixState), }, // distribution { Cpe22Uri: ps.CPE, Vulnerable: false, }, }, } return &node, nil }