github.com/khulnasoft-lab/tunnel-db@v0.0.0-20231117205118-74e1113bd007/pkg/vulnsrc/redhat/redhat.go (about) 1 package redhat 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "log" 8 "path/filepath" 9 "strconv" 10 "strings" 11 12 bolt "go.etcd.io/bbolt" 13 "golang.org/x/text/cases" 14 "golang.org/x/text/language" 15 "golang.org/x/xerrors" 16 17 "github.com/khulnasoft-lab/tunnel-db/pkg/db" 18 "github.com/khulnasoft-lab/tunnel-db/pkg/types" 19 "github.com/khulnasoft-lab/tunnel-db/pkg/utils" 20 "github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability" 21 ) 22 23 const ( 24 vulnListDir = "vuln-list-redhat" 25 apiDir = "api" 26 27 resourceURL = "https://access.redhat.com/security/cve/%s" 28 ) 29 30 type VulnSrc struct { 31 dbc db.Operation 32 } 33 34 func NewVulnSrc() VulnSrc { 35 return VulnSrc{ 36 dbc: db.Config{}, 37 } 38 } 39 40 func (vs VulnSrc) Name() types.SourceID { 41 return vulnerability.RedHat 42 } 43 44 func (vs VulnSrc) Update(dir string) error { 45 rootDir := filepath.Join(dir, vulnListDir, apiDir) 46 47 var cves []RedhatCVE 48 err := utils.FileWalk(rootDir, func(r io.Reader, _ string) error { 49 content, err := io.ReadAll(r) 50 if err != nil { 51 return err 52 } 53 cve := RedhatCVE{} 54 if err = json.Unmarshal(content, &cve); err != nil { 55 return xerrors.Errorf("failed to decode RedHat JSON: %w", err) 56 } 57 switch cve.TempAffectedRelease.(type) { 58 case []interface{}: 59 var ar RedhatCVEAffectedReleaseArray 60 if err = json.Unmarshal(content, &ar); err != nil { 61 return xerrors.Errorf("unknown affected_release type: %w", err) 62 } 63 cve.AffectedRelease = ar.AffectedRelease 64 case map[string]interface{}: 65 var ar RedhatCVEAffectedReleaseObject 66 if err = json.Unmarshal(content, &ar); err != nil { 67 return xerrors.Errorf("unknown affected_release type: %w", err) 68 } 69 cve.AffectedRelease = []RedhatAffectedRelease{ar.AffectedRelease} 70 case nil: 71 default: 72 return xerrors.New("unknown affected_release type") 73 } 74 75 switch cve.TempPackageState.(type) { 76 case []interface{}: 77 var ps RedhatCVEPackageStateArray 78 if err = json.Unmarshal(content, &ps); err != nil { 79 return xerrors.Errorf("unknown package_state type: %w", err) 80 } 81 cve.PackageState = ps.PackageState 82 case map[string]interface{}: 83 var ps RedhatCVEPackageStateObject 84 if err = json.Unmarshal(content, &ps); err != nil { 85 return xerrors.Errorf("unknown package_state type: %w", err) 86 } 87 cve.PackageState = []RedhatPackageState{ps.PackageState} 88 case nil: 89 default: 90 return xerrors.New("unknown package_state type") 91 } 92 cves = append(cves, cve) 93 return nil 94 }) 95 if err != nil { 96 return xerrors.Errorf("error in Red Hat walk: %w", err) 97 } 98 99 if err = vs.save(cves); err != nil { 100 return xerrors.Errorf("error in Red Hat save: %w", err) 101 } 102 103 return nil 104 } 105 106 func (vs VulnSrc) save(cves []RedhatCVE) error { 107 log.Println("Saving Red Hat DB") 108 err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error { 109 return vs.commit(tx, cves) 110 }) 111 if err != nil { 112 return xerrors.Errorf("failed batch update: %w", err) 113 } 114 return nil 115 } 116 117 func (vs VulnSrc) commit(tx *bolt.Tx, cves []RedhatCVE) error { 118 for _, cve := range cves { 119 if err := vs.putVulnerabilityDetail(tx, cve); err != nil { 120 return err 121 } 122 } 123 124 return nil 125 } 126 127 func (vs VulnSrc) putVulnerabilityDetail(tx *bolt.Tx, cve RedhatCVE) error { 128 cvssScore, _ := strconv.ParseFloat(cve.Cvss.CvssBaseScore, 64) 129 cvss3Score, _ := strconv.ParseFloat(cve.Cvss3.Cvss3BaseScore, 64) 130 title := strings.TrimPrefix(strings.TrimSpace(cve.Bugzilla.Description), cve.Name) 131 references := append(cve.References, fmt.Sprintf(resourceURL, cve.Name)) 132 133 vuln := types.VulnerabilityDetail{ 134 CvssScore: cvssScore, 135 CvssVector: cve.Cvss.CvssScoringVector, 136 CvssScoreV3: cvss3Score, 137 CvssVectorV3: cve.Cvss3.Cvss3ScoringVector, 138 Severity: severityFromThreat(cve.ThreatSeverity), 139 References: references, 140 Title: strings.TrimSpace(title), 141 Description: strings.TrimSpace(strings.Join(cve.Details, "")), 142 } 143 if err := vs.dbc.PutVulnerabilityDetail(tx, cve.Name, vulnerability.RedHat, vuln); err != nil { 144 return xerrors.Errorf("failed to save Red Hat vulnerability: %w", err) 145 } 146 147 // for optimization 148 if err := vs.dbc.PutVulnerabilityID(tx, cve.Name); err != nil { 149 return xerrors.Errorf("failed to save the vulnerability ID: %w", err) 150 } 151 return nil 152 } 153 154 func severityFromThreat(sev string) types.Severity { 155 severity := cases.Title(language.English).String(sev) 156 switch severity { 157 case "Low": 158 return types.SeverityLow 159 case "Moderate": 160 return types.SeverityMedium 161 case "Important": 162 return types.SeverityHigh 163 case "Critical": 164 return types.SeverityCritical 165 } 166 return types.SeverityUnknown 167 }