Skip to content

Commit

Permalink
Merge pull request #61 from Acmarr/main
Browse files Browse the repository at this point in the history
NVSHAS-8594 add rocky linux support
  • Loading branch information
jayhuang-suse committed Aug 27, 2024
2 parents 5355c9c + ea978ef commit dcf05cc
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 0 deletions.
1 change: 1 addition & 0 deletions dbgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
_ "github.com/vul-dbgen/updater/fetchers/oracle"
_ "github.com/vul-dbgen/updater/fetchers/photon"
_ "github.com/vul-dbgen/updater/fetchers/rhel2"
_ "github.com/vul-dbgen/updater/fetchers/rocky"
_ "github.com/vul-dbgen/updater/fetchers/suse"
_ "github.com/vul-dbgen/updater/fetchers/ubuntu"
)
Expand Down
2 changes: 2 additions & 0 deletions memdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ const (
dbMariner
dbSuse
dbPhoton
dbRocky
dbMax
)

Expand Down Expand Up @@ -179,6 +180,7 @@ func (db *memDB) UpdateDb(version string) bool {
dbs.buffers[dbMariner] = dbBuffer{namespace: "mariner", indexFile: "mariner_index.tb", fullFile: "mariner_full.tb"}
dbs.buffers[dbSuse] = dbBuffer{namespace: "sles", indexFile: "suse_index.tb", fullFile: "suse_full.tb"}
dbs.buffers[dbPhoton] = dbBuffer{namespace: "photon", indexFile: "photon_index.tb", fullFile: "photon_full.tb"}
dbs.buffers[dbRocky] = dbBuffer{namespace: "rocky", indexFile: "rocky_index.tb", fullFile: "rocky_full.tb"}

dbs.rawSHA = make([][sha256.Size]byte, len(db.rawFiles))

Expand Down
239 changes: 239 additions & 0 deletions updater/fetchers/rocky/rocky.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package rocky

import (
"encoding/json"
"io/ioutil"
"net/http"
"strconv"
"strings"

"github.com/vul-dbgen/common"
"github.com/vul-dbgen/updater"

log "github.com/sirupsen/logrus"
)

var endpoint = "https://apollo.build.resf.org/api/v3/advisories/"

type RockyFetcher struct{}

type apiResponse struct {
Advisories []advisory `json:"advisories"`
}

type advisory struct {
Id int `json:"id"`
Created_at string `json:"created_at"`
Updated_at string `json:"updated_at"`
Published_at string `json:"published_at"`
Name string `json:"name"`
Synopsis string `json:"synopsis"`
Description string `json:"description"`
Kind string `json:"kind"`
Severity string `json:"severity"`
Topic string `json:"topic"`
Red_hat_advisory_id int `json:"red_hat_advisory_id"`
AffectedProducts []Product `json:"affected_products"`
Cves []cve `json:"cves"`
Packages []ApiPackage `json:"packages"`
Fixes []Fix `json:"fixes"`
}

type cve struct {
Id int `json:"id"`
Cve string `json:"cve"`
CvssVector string `json:"cvss3_scoring_vector"`
CvssScore string `json:"cvss3_base_score"`
Cwe string `json:"cwe"`
}

type ApiPackage struct {
Id int `json"id"`
Nevra string `json:"nevra"`
Checksum string `json:"checksum"`
ChecksumType string `json:"checksum_type"`
ModuleContext string `json:"module_context"`
ModuleName string `json:"module_name"`
ModuleStream string `json:"module_stream"`
ModuleVersion string `json:"module_version"`
RepoName string `json:"repo_name"`
PackageName string `json:"package_name"`
ProductName string `json:"product_name"`
}

type Fix struct {
Id int `json:"id"`
TicketId string `json:"ticket_id"`
Source string `json:"source"`
Description string `json:"description"`
}

type Product struct {
Id int `json:"id"`
Variant string `json:"variant"`
Name string `json:"name"`
MajorVersion int `json:"major_version"`
MinorVersion int `json:"minor_version"`
Arch string `json:"arch"`
}

func init() {
updater.RegisterFetcher("rocky", &RockyFetcher{})
}

func (f *RockyFetcher) FetchUpdate() (resp updater.FetcherResponse, err error) {
log.WithField("package", "Rocky").Info("Start fetching vulnerabilities")
//get data from remote
remoteResponse, err := f.fetchRemote()
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Error fetching rocky remote")
}
//process data into standard format
resp.Vulnerabilities = parseRockyJSON(remoteResponse)

log.WithFields(log.Fields{"Vulnerabilities": len(resp.Vulnerabilities)}).Info("fetching rocky done")
return resp, nil
}

// fetchRemote retrieves and stores the api json response.
func (f *RockyFetcher) fetchRemote() (*apiResponse, error) {
req, err := http.NewRequest("GET", endpoint, nil)
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Error creating rocky linux request")
return nil, err
}
client := http.Client{}
r, err := client.Do(req)
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Error retrieving rocky linux response")
return nil, err
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Error reading rocky linux response")
return nil, err
}
var jsonResponse apiResponse
err = json.Unmarshal(body, &jsonResponse)
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Error unmarshalling rocky linux response")
}
log.WithFields(log.Fields{"number of advisories": len(jsonResponse.Advisories)}).Debug("Rocky reponse")

return &jsonResponse, nil
}

// pruneDuplicates returns a slice with all duplicate entries removed from the input slice.
func pruneDuplicates(slice []common.FeatureVersion) []common.FeatureVersion {
prunedList := []common.FeatureVersion{}
set := map[string]common.FeatureVersion{}
for _, entry := range slice {
if _, ok := set[entry.Feature.Name+":"+entry.Version.String()]; !ok {
set[entry.Feature.Name+":"+entry.Version.String()] = entry
}
}

for _, val := range set {
prunedList = append(prunedList, val)
}

return prunedList
}

func makeFV(fixPackage ApiPackage, majorVersion int) common.FeatureVersion {
//Split the nevra for the version
start := strings.Index(fixPackage.Nevra, ":")
last := strings.LastIndex(fixPackage.Nevra, ".")
last = strings.LastIndex(fixPackage.Nevra[:last], ".")
verString := fixPackage.Nevra[start-1 : last]
version, err := common.NewVersion(verString)
if err != nil {
log.WithFields(log.Fields{"err": err}).Error("Error making rocky linux feature version")
}

fixed := common.FeatureVersion{
Feature: common.Feature{
Name: fixPackage.PackageName,
Namespace: "rocky:" + strconv.Itoa(majorVersion),
},
Version: version,
}
return fixed
}

// parseRockyJSON takes the data and formats it into the format []common.Vulnerability.
func parseRockyJSON(data *apiResponse) []common.Vulnerability {
var vulns []common.Vulnerability
vulMap := map[string]common.Vulnerability{}

//Iterate over advisory and make the corresponding vulnerability.
for _, advisory := range data.Advisories {
Namespaces := getNamespaces(advisory.AffectedProducts)
vuln := common.Vulnerability{
Name: advisory.Name,
Description: advisory.Description,
Severity: translateSeverity(advisory.Severity),
}
fixedIns := []common.FeatureVersion{}
for _, fix := range advisory.Packages {
fv := makeFV(fix, advisory.AffectedProducts[0].MajorVersion)
fixedIns = append(fixedIns, fv)
}
fixedIns = pruneDuplicates(fixedIns)
vuln.FixedIn = fixedIns

//Add entry for each unique namespace
for _, ns := range Namespaces {
//Check if the advisory already exists in the namespace
vuln.Namespace = ns
if _, ok := vulMap[ns+":"+advisory.Name]; !ok {
vulMap[ns+":"+advisory.Name] = vuln
} else {
log.WithFields(log.Fields{"Name": advisory.Name}).Debug("Duplicate rocky advisory entry")
continue
}
}

}

//Make slice from map
for _, val := range vulMap {
vulns = append(vulns, val)
}

return vulns
}

func getNamespaces(affectedProducts []Product) []string {
nsMap := map[string]string{}
for _, product := range affectedProducts {
majorVersion := strconv.Itoa(product.MajorVersion)
if _, ok := nsMap["rocky:"+majorVersion]; !ok {
nsMap["rocky:"+majorVersion] = "rocky:" + majorVersion
}
}
results := []string{}
for _, val := range nsMap {
results = append(results, val)
}
return results
}

func translateSeverity(incSev string) common.Priority {
var severity common.Priority
switch strings.ToLower(incSev) {
case "important":
severity = common.High
case "moderate":
severity = common.Medium
case "low":
severity = common.Low
case "none":
severity = common.Low
default:
log.WithFields(log.Fields{"sev": incSev}).Debug("unhandled severity")
}
return severity
}

func (f *RockyFetcher) Clean() {}

0 comments on commit dcf05cc

Please sign in to comment.