-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
281 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
package fedora | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"log" | ||
"path/filepath" | ||
"strings" | ||
|
||
bolt "go.etcd.io/bbolt" | ||
"golang.org/x/xerrors" | ||
|
||
"github.com/aquasecurity/trivy-db/pkg/db" | ||
"github.com/aquasecurity/trivy-db/pkg/types" | ||
"github.com/aquasecurity/trivy-db/pkg/utils" | ||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" | ||
) | ||
|
||
const ( | ||
fedoraDir = "fedora" | ||
) | ||
|
||
var ( | ||
platformFormat = map[string]string{ | ||
"fedora": "fedora %s", | ||
"epel": "epel %s", | ||
} | ||
targetMode = []string{"fedora", "epel"} | ||
targetFedoraRelease = []string{"32", "33", "34", "35"} | ||
targetEPELRelease = []string{"7", "8", "9"} | ||
targetRepository = []string{"Everything", "Modular"} | ||
targetArches = []string{"x86_64"} | ||
) | ||
|
||
type VulnSrc struct { | ||
dbc db.Operation | ||
} | ||
|
||
func NewVulnSrc() VulnSrc { | ||
return VulnSrc{ | ||
dbc: db.Config{}, | ||
} | ||
} | ||
|
||
func (vs VulnSrc) Name() string { | ||
return vulnerability.Fedora | ||
} | ||
|
||
func (vs VulnSrc) Update(dir string) error { | ||
rootDir := filepath.Join(dir, "vuln-list", fedoraDir) | ||
errata := map[string]map[string][]FSA{} | ||
for _, mode := range targetMode { | ||
errata[mode] = map[string][]FSA{} | ||
} | ||
err := utils.FileWalk(rootDir, func(r io.Reader, path string) error { | ||
var erratum FSA | ||
if err := json.NewDecoder(r).Decode(&erratum); err != nil { | ||
return xerrors.Errorf("failed to decode Fedora erratum: %w", err) | ||
} | ||
|
||
dirs := strings.Split(strings.TrimPrefix(path, rootDir), string(filepath.Separator))[1:] | ||
mode := dirs[0] | ||
if !utils.StringInSlice(mode, targetMode) { | ||
log.Printf("unsupported Fedora mode: %s\n", mode) | ||
return nil | ||
} | ||
majorVer := dirs[1] | ||
if mode == "fedora" { | ||
if !utils.StringInSlice(majorVer, targetFedoraRelease) { | ||
log.Printf("unsupported Fedora version: %s\n", majorVer) | ||
return nil | ||
} | ||
|
||
repo := dirs[2] | ||
if !utils.StringInSlice(repo, targetRepository) { | ||
log.Printf("unsupported Fedora Repository: %s\n", repo) | ||
return nil | ||
} | ||
|
||
arch := dirs[3] | ||
if !utils.StringInSlice(arch, targetArches) { | ||
switch arch { | ||
case "aarch64": | ||
default: | ||
log.Printf("unsupported Fedora arch: %s\n", arch) | ||
} | ||
return nil | ||
} | ||
} else { | ||
if !utils.StringInSlice(majorVer, targetEPELRelease) { | ||
log.Printf("unsupported EPEL version: %s\n", majorVer) | ||
return nil | ||
} | ||
|
||
if majorVer == "7" { | ||
arch := dirs[2] | ||
if !utils.StringInSlice(arch, targetArches) { | ||
switch arch { | ||
case "aarch64": | ||
default: | ||
log.Printf("unsupported EPEL arch: %s\n", arch) | ||
} | ||
return nil | ||
} | ||
} else { | ||
repo := dirs[2] | ||
if !utils.StringInSlice(repo, targetRepository) { | ||
log.Printf("unsupported EPEL Repository: %s\n", repo) | ||
return nil | ||
} | ||
|
||
arch := dirs[3] | ||
if !utils.StringInSlice(arch, targetArches) { | ||
switch arch { | ||
case "aarch64": | ||
default: | ||
log.Printf("unsupported EPEL arch: %s\n", arch) | ||
} | ||
return nil | ||
} | ||
} | ||
} | ||
errata[mode][majorVer] = append(errata[mode][majorVer], erratum) | ||
return nil | ||
}) | ||
if err != nil { | ||
return xerrors.Errorf("error in Fedora walk: %w", err) | ||
} | ||
|
||
if err := vs.save(errata); err != nil { | ||
return xerrors.Errorf("error in Fedora save: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (vs VulnSrc) save(errataMode map[string]map[string][]FSA) error { | ||
err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error { | ||
for mode, errataVer := range errataMode { | ||
for majorVer, errata := range errataVer { | ||
platformName := fmt.Sprintf(platformFormat[mode], majorVer) | ||
if err := vs.commit(tx, platformName, errata); err != nil { | ||
return xerrors.Errorf("error in save Fedora %s: %w", majorVer, err) | ||
} | ||
} | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
return xerrors.Errorf("error in db batch update: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
func (vs VulnSrc) commit(tx *bolt.Tx, platformName string, errata []FSA) error { | ||
for _, erratum := range errata { | ||
for _, cveID := range erratum.CveIDs { | ||
for _, pkg := range erratum.Packages { | ||
advisory := types.Advisory{ | ||
FixedVersion: constructVersion(pkg.Epoch, pkg.Version, pkg.Release), | ||
} | ||
if err := vs.dbc.PutAdvisoryDetail(tx, cveID, platformName, pkg.Name, advisory); err != nil { | ||
return xerrors.Errorf("failed to save Fedora/EPEL advisory: %w", err) | ||
} | ||
|
||
} | ||
var references []string | ||
for _, ref := range erratum.References { | ||
references = append(references, ref.Href) | ||
} | ||
|
||
vuln := types.VulnerabilityDetail{ | ||
Severity: generalizeSeverity(erratum.Severity), | ||
References: references, | ||
Title: erratum.Title, | ||
Description: erratum.Description, | ||
} | ||
if err := vs.dbc.PutVulnerabilityDetail(tx, cveID, vulnerability.Fedora, vuln); err != nil { | ||
return xerrors.Errorf("failed to save Fedora/EPEL vulnerability: %w", err) | ||
} | ||
|
||
if err := vs.dbc.PutVulnerabilityID(tx, cveID); err != nil { | ||
return xerrors.Errorf("failed to save the vulnerability ID: %w", err) | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (vs VulnSrc) Get(mode, release, pkgName string) ([]types.Advisory, error) { | ||
format, ok := platformFormat[mode] | ||
if !ok { | ||
return nil, xerrors.Errorf("failed to get Fedora advisories: mode(%s) is not support", mode) | ||
} | ||
bucket := fmt.Sprintf(format, release) | ||
advisories, err := vs.dbc.GetAdvisories(bucket, pkgName) | ||
if err != nil { | ||
return nil, xerrors.Errorf("failed to get Fedora advisories: %w", err) | ||
} | ||
return advisories, nil | ||
} | ||
|
||
func constructVersion(epoch, version, release string) string { | ||
verStr := "" | ||
if epoch != "0" && epoch != "" { | ||
verStr += fmt.Sprintf("%s:", epoch) | ||
} | ||
verStr += version | ||
|
||
if release != "" { | ||
verStr += fmt.Sprintf("-%s", release) | ||
|
||
} | ||
return verStr | ||
} | ||
|
||
func generalizeSeverity(severity string) types.Severity { | ||
switch strings.ToLower(severity) { | ||
case "low": | ||
return types.SeverityLow | ||
case "moderate": | ||
return types.SeverityMedium | ||
case "important": | ||
return types.SeverityHigh | ||
case "critical": | ||
return types.SeverityCritical | ||
} | ||
return types.SeverityUnknown | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package fedora | ||
|
||
// FSA has detailed data of Fedora Security Advisory | ||
type FSA struct { | ||
ID string `xml:"id" json:"id,omitempty"` | ||
Title string `xml:"title" json:"title,omitempty"` | ||
Type string `xml:"type,attr" json:"type,omitempty"` | ||
Issued Date `xml:"issued" json:"issued,omitempty"` | ||
Updated Date `xml:"updated" json:"updated,omitempty"` | ||
Severity string `xml:"severity" json:"severity,omitempty"` | ||
Description string `xml:"description" json:"description,omitempty"` | ||
Packages []Package `xml:"pkglist>collection>package" json:"packages,omitempty"` | ||
Module Module `json:"module,omitempty"` | ||
References []Reference `xml:"references>reference" json:"references,omitempty"` | ||
CveIDs []string `json:"cveids,omitempty"` | ||
} | ||
|
||
// Updated has updated at | ||
type Date struct { | ||
Date string `xml:"date,attr" json:"date,omitempty"` | ||
} | ||
|
||
// Reference has reference information | ||
type Reference struct { | ||
Href string `xml:"href,attr" json:"href,omitempty"` | ||
ID string `xml:"id,attr" json:"id,omitempty"` | ||
Title string `xml:"title,attr" json:"title,omitempty"` | ||
Type string `xml:"type,attr" json:"type,omitempty"` | ||
} | ||
|
||
// Package has affected package information | ||
type Package struct { | ||
Name string `xml:"name,attr" json:"name,omitempty"` | ||
Epoch string `xml:"epoch,attr" json:"epoch,omitempty"` | ||
Version string `xml:"version,attr" json:"version,omitempty"` | ||
Release string `xml:"release,attr" json:"release,omitempty"` | ||
Arch string `xml:"arch,attr" json:"arch,omitempty"` | ||
Filename string `xml:"filename" json:"filename,omitempty"` | ||
} | ||
|
||
// Module has modular package information | ||
type Module struct { | ||
Stream string `json:"stream,omitempty"` | ||
Name string `json:"name,omitempty"` | ||
Version int64 `json:"version,omitempty"` | ||
Arch string `json:"arch,omitempty"` | ||
Context string `json:"context,omitempty"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters