github.com/khulnasoft-lab/tunnel-db@v0.0.0-20231117205118-74e1113bd007/pkg/vulnsrc/oracle-oval/oracle-oval.go (about) 1 package oracleoval 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "log" 8 "path/filepath" 9 "strings" 10 11 bolt "go.etcd.io/bbolt" 12 "golang.org/x/xerrors" 13 14 version "github.com/khulnasoft-lab/go-rpm-version" 15 "github.com/khulnasoft-lab/tunnel-db/pkg/db" 16 "github.com/khulnasoft-lab/tunnel-db/pkg/types" 17 "github.com/khulnasoft-lab/tunnel-db/pkg/utils" 18 ustrings "github.com/khulnasoft-lab/tunnel-db/pkg/utils/strings" 19 "github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability" 20 ) 21 22 var ( 23 // cat /etc/os-release ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8" 24 platformFormat = "Oracle Linux %s" 25 targetPlatforms = []string{"Oracle Linux 5", "Oracle Linux 6", "Oracle Linux 7", "Oracle Linux 8", "Oracle Linux 9"} 26 oracleDir = filepath.Join("oval", "oracle") 27 28 source = types.DataSource{ 29 ID: vulnerability.OracleOVAL, 30 Name: "Oracle Linux OVAL definitions", 31 URL: "https://linux.oracle.com/security/oval/", 32 } 33 ) 34 35 type PutInput struct { 36 VulnID string // CVE-ID or ELSA-ID 37 Vuln types.VulnerabilityDetail // vulnerability detail such as CVSS and description 38 Advisories map[AffectedPackage]types.Advisory // pkg => advisory 39 OVAL OracleOVAL // for extensibility, not used in tunnel-db 40 } 41 42 type DB interface { 43 db.Operation 44 Put(*bolt.Tx, PutInput) error 45 Get(release, pkgName string) ([]types.Advisory, error) 46 } 47 48 type VulnSrc struct { 49 DB // Those who want to customize Tunnel DB can override put/get methods. 50 } 51 52 type Oracle struct { 53 db.Operation 54 } 55 56 func NewVulnSrc() *VulnSrc { 57 return &VulnSrc{ 58 DB: &Oracle{Operation: db.Config{}}, 59 } 60 } 61 62 func (vs *VulnSrc) Name() types.SourceID { 63 return source.ID 64 } 65 66 func (vs *VulnSrc) Update(dir string) error { 67 rootDir := filepath.Join(dir, "vuln-list", oracleDir) 68 ovals, err := vs.parse(rootDir) 69 if err != nil { 70 return err 71 } 72 if err = vs.put(ovals); err != nil { 73 return xerrors.Errorf("error in Oracle Linux OVAL save: %w", err) 74 } 75 76 return nil 77 } 78 79 // Parse parses all the advisories from Alma Linux. 80 // It is exported for those who want to customize tunnel-db. 81 func (vs *VulnSrc) parse(rootDir string) ([]OracleOVAL, error) { 82 var ovals []OracleOVAL 83 err := utils.FileWalk(rootDir, func(r io.Reader, path string) error { 84 var oval OracleOVAL 85 if err := json.NewDecoder(r).Decode(&oval); err != nil { 86 return xerrors.Errorf("failed to decode Oracle Linux OVAL JSON: %w", err) 87 } 88 ovals = append(ovals, oval) 89 return nil 90 }) 91 if err != nil { 92 return nil, xerrors.Errorf("error in Oracle Linux OVAL walk: %w", err) 93 } 94 95 return ovals, nil 96 } 97 98 func (vs *VulnSrc) put(ovals []OracleOVAL) error { 99 log.Println("Saving Oracle Linux OVAL") 100 101 err := vs.BatchUpdate(func(tx *bolt.Tx) error { 102 return vs.commit(tx, ovals) 103 }) 104 if err != nil { 105 return xerrors.Errorf("error in batch update: %w", err) 106 } 107 108 return nil 109 110 } 111 112 func (vs *VulnSrc) commit(tx *bolt.Tx, ovals []OracleOVAL) error { 113 for _, oval := range ovals { 114 elsaID := strings.Split(oval.Title, ":")[0] 115 116 var vulnIDs []string 117 for _, cve := range oval.Cves { 118 vulnIDs = append(vulnIDs, cve.ID) 119 } 120 if len(vulnIDs) == 0 { 121 vulnIDs = append(vulnIDs, elsaID) 122 } 123 124 advisories := map[AffectedPackage]types.Advisory{} 125 affectedPkgs := walkOracle(oval.Criteria, "", []AffectedPackage{}) 126 for _, affectedPkg := range affectedPkgs { 127 if affectedPkg.Package.Name == "" { 128 continue 129 } 130 131 platformName := affectedPkg.PlatformName() 132 if !ustrings.InSlice(platformName, targetPlatforms) { 133 continue 134 } 135 136 if err := vs.PutDataSource(tx, platformName, source); err != nil { 137 return xerrors.Errorf("failed to put data source: %w", err) 138 } 139 140 advisories[affectedPkg] = types.Advisory{ 141 FixedVersion: affectedPkg.Package.FixedVersion, 142 } 143 } 144 145 var references []string 146 for _, ref := range oval.References { 147 references = append(references, ref.URI) 148 } 149 150 for _, vulnID := range vulnIDs { 151 vuln := types.VulnerabilityDetail{ 152 Description: oval.Description, 153 References: referencesFromContains(references, []string{elsaID, vulnID}), 154 Title: oval.Title, 155 Severity: severityFromThreat(oval.Severity), 156 } 157 158 err := vs.Put(tx, PutInput{ 159 VulnID: vulnID, 160 Vuln: vuln, 161 Advisories: advisories, 162 OVAL: oval, 163 }) 164 if err != nil { 165 return xerrors.Errorf("db put error: %w", err) 166 } 167 } 168 } 169 170 return nil 171 } 172 173 func (o *Oracle) Put(tx *bolt.Tx, input PutInput) error { 174 if err := o.PutVulnerabilityDetail(tx, input.VulnID, source.ID, input.Vuln); err != nil { 175 return xerrors.Errorf("failed to save Oracle Linux OVAL vulnerability: %w", err) 176 } 177 178 // for optimization 179 if err := o.PutVulnerabilityID(tx, input.VulnID); err != nil { 180 return xerrors.Errorf("failed to save %s: %w", input.VulnID, err) 181 } 182 183 for pkg, advisory := range input.Advisories { 184 platformName := pkg.PlatformName() 185 if err := o.PutAdvisoryDetail(tx, input.VulnID, pkg.Package.Name, []string{platformName}, advisory); err != nil { 186 return xerrors.Errorf("failed to save Oracle Linux advisory: %w", err) 187 } 188 } 189 return nil 190 } 191 192 func (o *Oracle) Get(release string, pkgName string) ([]types.Advisory, error) { 193 bucket := fmt.Sprintf(platformFormat, release) 194 advisories, err := o.GetAdvisories(bucket, pkgName) 195 if err != nil { 196 return nil, xerrors.Errorf("failed to get Oracle Linux advisories: %w", err) 197 } 198 return advisories, nil 199 } 200 201 func walkOracle(cri Criteria, osVer string, pkgs []AffectedPackage) []AffectedPackage { 202 for _, c := range cri.Criterions { 203 if strings.HasPrefix(c.Comment, "Oracle Linux ") && 204 strings.HasSuffix(c.Comment, " is installed") { 205 osVer = strings.TrimSuffix(strings.TrimPrefix(c.Comment, "Oracle Linux "), " is installed") 206 } 207 ss := strings.Split(c.Comment, " is earlier than ") 208 if len(ss) != 2 { 209 continue 210 } 211 212 pkgs = append(pkgs, AffectedPackage{ 213 OSVer: osVer, 214 Package: Package{ 215 Name: ss[0], 216 FixedVersion: version.NewVersion(ss[1]).String(), 217 }, 218 }) 219 } 220 221 for _, c := range cri.Criterias { 222 pkgs = walkOracle(c, osVer, pkgs) 223 } 224 return pkgs 225 } 226 227 func referencesFromContains(sources []string, matches []string) []string { 228 var references []string 229 for _, s := range sources { 230 for _, m := range matches { 231 if strings.Contains(s, m) { 232 references = append(references, s) 233 } 234 } 235 } 236 return ustrings.Unique(references) 237 } 238 239 func severityFromThreat(sev string) types.Severity { 240 switch sev { 241 case "LOW": 242 return types.SeverityLow 243 case "MODERATE": 244 return types.SeverityMedium 245 case "IMPORTANT": 246 return types.SeverityHigh 247 case "CRITICAL": 248 return types.SeverityCritical 249 } 250 return types.SeverityUnknown 251 }