Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(epel): add EPEL(Extra Packages for Enterprise Linux) #196

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions pkg/vulnsrc/epel/epel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package epel

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"
ustrings "github.com/aquasecurity/trivy-db/pkg/utils/strings"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
)

const (
epelDir = "fedora/epel"
platformFormat = "epel %s"
)

var (
targetRepository = []string{"Everything", "Modular"}
targetArches = []string{"x86_64"}

source = types.DataSource{
ID: vulnerability.EPEL,
Name: "EPEL UpdateInfo",
URL: "https://dl.fedoraproject.org/pub/epel",
}
)

type VulnSrc struct {
dbc db.Operation
}

func NewVulnSrc() VulnSrc {
return VulnSrc{
dbc: db.Config{},
}
}

func (vs VulnSrc) Name() types.SourceID {
return source.ID
}

func (vs VulnSrc) Update(dir string) error {
rootDir := filepath.Join(dir, "vuln-list", epelDir)
errata := map[string][]UpdateInfo{}
if err := utils.FileWalk(rootDir, func(r io.Reader, path string) error {
var erratum UpdateInfo
if err := json.NewDecoder(r).Decode(&erratum); err != nil {
return xerrors.Errorf("failed to decode EPEL erratum: %w", err)
}

dirs := strings.Split(strings.TrimPrefix(path, rootDir), string(filepath.Separator))[1:]
majorVer := dirs[0]
var arch string
if majorVer == "7" {
arch = dirs[1]
} else {
if repo := dirs[1]; !ustrings.InSlice(repo, targetRepository) {
log.Printf("unsupported EPEL Repository: %s\n", repo)
return nil
}
arch = dirs[2]
}
if !ustrings.InSlice(arch, targetArches) {
switch arch {
case "aarch64":
default:
log.Printf("unsupported EPEL arch: %s\n", arch)
}
return nil
}

errata[majorVer] = append(errata[majorVer], erratum)
return nil
}); err != nil {
return xerrors.Errorf("error in EPEL walk: %w", err)
}

if err := vs.save(errata); err != nil {
return xerrors.Errorf("error in EPEL save: %w", err)
}

return nil
}

func (vs VulnSrc) save(errataVer map[string][]UpdateInfo) error {
if 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 EPEL %s: %w", majorVer, err)
}
}
return nil
}); err != nil {
return xerrors.Errorf("error in db batch update: %w", err)
}
return nil
}

func (vs VulnSrc) commit(tx *bolt.Tx, platformName string, errata []UpdateInfo) 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, pkgName, []string{platformName}, advisory); err != nil {
return xerrors.Errorf("failed to save 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.EPEL, vuln); err != nil {
return xerrors.Errorf("failed to save 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(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 EPEL 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
}
84 changes: 84 additions & 0 deletions pkg/vulnsrc/epel/epel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package epel

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-2022-0217", "epel 8", "prosody"},
value: types.Advisory{
FixedVersion: "0.11.12-1.el8",
},
},
{
key: []string{"vulnerability-detail", "CVE-2022-0217", string(vulnerability.EPEL)},
value: types.VulnerabilityDetail{
Severity: types.SeverityMedium,
References: []string{
"https://bugzilla.redhat.com/show_bug.cgi?id=2040350",
"https://bugzilla.redhat.com/show_bug.cgi?id=2040639",
"https://bugzilla.redhat.com/show_bug.cgi?id=2040641",
},
Title: "prosody-0.11.12-1.el8",
Description: "# Prosody 0.11.12\n\nUpstream is pleased to announce a new minor release from their stable branch.\n\nThis is a security release that addresses a denial-of-service vulnerability in Prosody’s mod_websocket. For more information, refer to the [20220113 advisory](https://prosody.im/security/advisory_20220113/).\n\n## Security\n * util.xml: Do not allow doctypes, comments or processing instructions\n",
},
},
{
key: []string{"vulnerability-id", "CVE-2022-0217"},
value: map[string]interface{}{},
},
},
},
{
name: "sad path",
dir: filepath.Join("testdata", "sad"),
wantErr: "failed to decode EPEL 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,48 @@
{
"id": "FEDORA-EPEL-2022-2f3bfb7a61",
"title": "prosody-0.11.12-1.el8",
"type": "security",
"issued": {
"date": "2022-01-23 00:42:19"
},
"updated": {
"date": "2022-01-14 11:23:16"
},
"severity": "Moderate",
"description": "# Prosody 0.11.12\n\nUpstream is pleased to announce a new minor release from their stable branch.\n\nThis is a security release that addresses a denial-of-service vulnerability in Prosody’s mod_websocket. For more information, refer to the [20220113 advisory](https://prosody.im/security/advisory_20220113/).\n\n## Security\n * util.xml: Do not allow doctypes, comments or processing instructions\n",
"packages": [
{
"name": "prosody",
"epoch": "0",
"version": "0.11.12",
"release": "1.el8",
"arch": "x86_64",
"src": "https://download.fedoraproject.org/pub/fedora/linux/updates/8/x86_64/p/prosody-0.11.12-1.el8.x86_64.rpm",
"filename": "prosody-0.11.12-1.el8.x86_64.rpm"
}
],
"module": {},
"references": [
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2040350",
"id": "2040350",
"title": "prosody-0.11.12 is available",
"type": "bugzilla"
},
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2040639",
"id": "2040639",
"title": "CVE-2022-0217 prosody: unauthenticated remote Denial of Service Attack",
"type": "bugzilla"
},
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2040641",
"id": "2040641",
"title": "CVE-2022-0217 prosody: unauthenticated remote Denial of Service Attack [epel-all]",
"type": "bugzilla"
}
],
"cveids": [
"CVE-2022-0217"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"id": "FEDORA-EPEL-2022-2f3bfb7a61"
"title": "prosody-0.11.12-1.el8",
"type": "security",
"issued": {
"date": "2022-01-23 00:42:19"
},
"updated": {
"date": "2022-01-14 11:23:16"
},
"severity": "Moderate",
"description": "# Prosody 0.11.12\n\nUpstream is pleased to announce a new minor release from their stable branch.\n\nThis is a security release that addresses a denial-of-service vulnerability in Prosody’s mod_websocket. For more information, refer to the [20220113 advisory](https://prosody.im/security/advisory_20220113/).\n\n## Security\n * util.xml: Do not allow doctypes, comments or processing instructions\n",
"packages": [
{
"name": "prosody",
"epoch": "0",
"version": "0.11.12",
"release": "1.el8",
"arch": "x86_64",
"src": "https://download.fedoraproject.org/pub/fedora/linux/updates/8/x86_64/p/prosody-0.11.12-1.el8.x86_64.rpm",
"filename": "prosody-0.11.12-1.el8.x86_64.rpm"
},
{
"name": "prosody-debugsource",
"epoch": "0",
"version": "0.11.12",
"release": "1.el8",
"arch": "x86_64",
"src": "https://download.fedoraproject.org/pub/fedora/linux/updates/8/x86_64/p/prosody-debugsource-0.11.12-1.el8.x86_64.rpm",
"filename": "prosody-debugsource-0.11.12-1.el8.x86_64.rpm"
},
{
"name": "prosody-debuginfo",
"epoch": "0",
"version": "0.11.12",
"release": "1.el8",
"arch": "x86_64",
"src": "https://download.fedoraproject.org/pub/fedora/linux/updates/8/x86_64/p/prosody-debuginfo-0.11.12-1.el8.x86_64.rpm",
"filename": "prosody-debuginfo-0.11.12-1.el8.x86_64.rpm"
}
],
"module": {},
"references": [
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2040350",
"id": "2040350",
"title": "prosody-0.11.12 is available",
"type": "bugzilla"
},
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2040639",
"id": "2040639",
"title": "CVE-2022-0217 prosody: unauthenticated remote Denial of Service Attack",
"type": "bugzilla"
},
{
"href": "https://bugzilla.redhat.com/show_bug.cgi?id=2040641",
"id": "2040641",
"title": "CVE-2022-0217 prosody: unauthenticated remote Denial of Service Attack [epel-all]",
"type": "bugzilla"
}
],
"cveids": [
"CVE-2022-0217"
]
}
Loading