Skip to content

Commit

Permalink
feat(fedora): support fedora
Browse files Browse the repository at this point in the history
  • Loading branch information
MaineK00n committed Jan 23, 2022
1 parent 3e43e9a commit 63cc24f
Show file tree
Hide file tree
Showing 8 changed files with 504 additions and 1 deletion.
177 changes: 177 additions & 0 deletions pkg/vulnsrc/fedora/fedora.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
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/fedora"
)

var (
platformFormat = "fedora %s"
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][]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:]
majorVer := dirs[0]
repo := dirs[1]
if !utils.StringInSlice(repo, targetRepository) {
log.Printf("unsupported Fedora Repository: %s\n", repo)
return nil
}

arch := dirs[2]
if !utils.StringInSlice(arch, targetArches) {
switch arch {
case "aarch64":
default:
log.Printf("unsupported Fedora arch: %s\n", arch)
}
return nil
}

errata[majorVer] = append(errata[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(errataVer map[string][]FSA) error {
err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error {
for majorVer, errata := range errataVer {
platformName := fmt.Sprintf(platformFormat, 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),
}

pkgName := pkg.Name
if erratum.Module.Name != "" && erratum.Module.Stream != "" {
pkgName = fmt.Sprintf("%s:%s::%s", erratum.Module.Name, erratum.Module.Stream, pkg.Name)
}

if err := vs.dbc.PutAdvisoryDetail(tx, cveID, platformName, pkgName, advisory); err != nil {
return xerrors.Errorf("failed to save Fedora 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 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(release, pkgName string) ([]types.Advisory, error) {
bucket := fmt.Sprintf(platformFormat, 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
}
109 changes: 109 additions & 0 deletions pkg/vulnsrc/fedora/fedora_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package fedora

import (
"path/filepath"
"testing"

"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/dbtest"
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestVulnSrc_Update(t *testing.T) {
type want struct {
key []string
value interface{}
}
tests := []struct {
name string
dir string
wantValues []want
wantErr string
}{
{
name: "everything package",
dir: filepath.Join("testdata", "everything"),
wantValues: []want{
{
key: []string{"advisory-detail", "CVE-2021-41159", "fedora 35", "freerdp-libs-debuginfo"},
value: types.Advisory{
FixedVersion: "2:2.4.1-1.fc35",
},
},
{
key: []string{"vulnerability-detail", "CVE-2021-41159", vulnerability.Fedora},
value: types.VulnerabilityDetail{
Severity: types.SeverityHigh,
References: []string{
"https://bugzilla.redhat.com/show_bug.cgi?id=2015189",
},
Title: "freerdp-2.4.1-1.fc35 guacamole-server-1.3.0-9.fc35 remmina-1.4.21-1.fc35",
Description: "- Update to 2.4.1 containing security fixes for CVE-2021-41159 and CVE-2021-41160.\n- Remmina 1.4.21 with bugfixes.\n\n",
},
},
{
key: []string{"vulnerability-id", "CVE-2021-41159"},
value: map[string]interface{}{},
},
},
},
{
name: "modular package",
dir: filepath.Join("testdata", "module"),
wantValues: []want{
{
key: []string{"advisory-detail", "CVE-2021-35623", "fedora 35", "mysql:8.0::community-mysql"},
value: types.Advisory{
FixedVersion: "8.0.27-1.module_f35+13269+c9322734",
},
},
{
key: []string{"vulnerability-detail", "CVE-2021-35623", vulnerability.Fedora},
value: types.VulnerabilityDetail{
Severity: types.SeverityMedium,
References: []string{
"https://bugzilla.redhat.com/show_bug.cgi?id=2016142",
},
Title: "mysql-8.0-3520211031142409.f27b74a8",
Description: "**MySQL 8.0.27**\n\nRelease notes:\n\n https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-27.html",
},
},
{
key: []string{"vulnerability-id", "CVE-2021-35623"},
value: map[string]interface{}{},
},
},
},
{
name: "sad path",
dir: filepath.Join("testdata", "sad"),
wantErr: "failed to decode Fedora erratum",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()

err := db.Init(tempDir)
require.NoError(t, err)
defer db.Close()

vs := NewVulnSrc()
err = vs.Update(tt.dir)
if tt.wantErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr)
return
}

require.NoError(t, err)
require.NoError(t, db.Close()) // Need to close before dbtest.JSONEq is called
for _, want := range tt.wantValues {
dbtest.JSONEq(t, db.Path(tempDir), want.key, want.value)
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"id": "FEDORA-2021-2c25f03d0b",
"title": "freerdp-2.4.1-1.fc35 guacamole-server-1.3.0-9.fc35 remmina-1.4.21-1.fc35",
"type": "security",
"issued": {
"date": "2021-11-17 01:12:41"
},
"updated": {
"date": "2021-11-10 20:45:11"
},
"severity": "Important",
"description": "- Update to 2.4.1 containing security fixes for CVE-2021-41159 and CVE-2021-41160.\n- Remmina 1.4.21 with bugfixes.\n\n",
"packages": [
{
"name": "freerdp-libs-debuginfo",
"epoch": "2",
"version": "2.4.1",
"release": "1.fc35",
"arch": "x86_64",
"src": "https://download.fedoraproject.org/pub/fedora/linux/updates/35/x86_64/f/freerdp-libs-debuginfo-2.4.1-1.fc35.x86_64.rpm",
"filename": "freerdp-libs-debuginfo-2.4.1-1.fc35.x86_64.rpm"
}
],
"module": {},
"references": [
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2015189",
"id": "2015189",
"title": "remmina-1.4.21 is available",
"type": "bugzilla"
}
],
"cveids": [
"CVE-2021-41159"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"id": "FEDORA-MODULAR-2021-217f84c072",
"title": "mysql-8.0-3520211031142409.f27b74a8",
"type": "security",
"issued": {
"date": "2021-11-10 00:48:52"
},
"updated": {
"date": "2021-10-31 17:53:03"
},
"severity": "Moderate",
"description": "**MySQL 8.0.27**\n\nRelease notes:\n\n https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-27.html",
"packages": [
{
"name": "community-mysql",
"epoch": "0",
"version": "8.0.27",
"release": "1.module_f35+13269+c9322734",
"arch": "x86_64",
"filename": "community-mysql-8.0.27-1.module_f35+13269+c9322734.x86_64.rpm"
}
],
"module": {
"stream": "8.0",
"name": "mysql",
"version": 3520211031142409,
"arch": "x86_64",
"context": "f27b74a8"
},
"references": [
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2016142",
"id": "2016142",
"title": "CVE-2021-2478 CVE-2021-2479 CVE-2021-2481 CVE-2021-35546 CVE-2021-35575 CVE-2021-35577 CVE-2021-35591 CVE-2021-35596 CVE-2021-35597 CVE-2021-35602 CVE-2021-35604 CVE-2021-35607 CVE-2021-35608 ... mysql:8.0/community-mysql: various flaws [fedora-all]",
"type": "bugzilla"
}
],
"cveids": [
"CVE-2021-35623"
]
}
Loading

0 comments on commit 63cc24f

Please sign in to comment.